*/}}
YimingWu 1 年之前
父节点
当前提交
507b30880a
共有 100 个文件被更改,包括 21277 次插入0 次删除
  1. 14 0
      .htaccess
  2. 0 0
      .la_lock
  3. 30 0
      .well-known/apub_private_key.php
  4. 9 0
      .well-known/apub_public_key.pem
  5. 1 0
      .well-known/webfinger/index.php
  6. 30 0
      .well_known/apub_private_key.php
  7. 9 0
      .well_known/apub_public_key.pem
  8. 1 0
      .well_known/webfinger/index.php
  9. 93 0
      OFL.txt
  10. 245 0
      PHPMailer/DSNConfigurator.php
  11. 40 0
      PHPMailer/Exception.php
  12. 139 0
      PHPMailer/OAuth.php
  13. 44 0
      PHPMailer/OAuthTokenProvider.php
  14. 5248 0
      PHPMailer/PHPMailer.php
  15. 467 0
      PHPMailer/POP3.php
  16. 1499 0
      PHPMailer/SMTP.php
  17. 0 0
      Parsedown.php
  18. 0 0
      ParsedownExtra.php
  19. 1 0
      all_subscribers.php
  20. 12 0
      archive/202003.md
  21. 162 0
      archive/202110.md
  22. 4 0
      archive/202111.md
  23. 8 0
      archive/202201.md
  24. 207 0
      archive/202202.md
  25. 197 0
      archive/202203.md
  26. 62 0
      archive/202205.md
  27. 28 0
      archive/202206.md
  28. 3024 0
      archive/202207.md
  29. 100 0
      archive/202208.md
  30. 42 0
      archive/202301.md
  31. 6 0
      archive/202302.md
  32. 91 0
      archive/202310.md
  33. 12 0
      archive/202312.md
  34. 12 0
      archive/202404.md
  35. 5 0
      auth/config.php
  36. 400 0
      auth/index.php
  37. 132 0
      auth/setup.php
  38. 18 0
      blog.txt
  39. 45 0
      chatgpt_test.php
  40. 102 0
      chatgpt_test_2.php
  41. 2 0
      custom_translations.md
  42. 14 0
      demo/.htaccess
  43. 0 0
      demo/.la_lock
  44. 2032 0
      demo/Parsedown.php
  45. 689 0
      demo/ParsedownExtra.php
  46. 二进制
      demo/images/20220118083330.jpg
  47. 二进制
      demo/images/20220118091056.jpg
  48. 2 0
      demo/images/list.md
  49. 二进制
      demo/images/thumb/20220118083330.jpg
  50. 二进制
      demo/images/thumb/20220118091056.jpg
  51. 3687 0
      demo/index.php
  52. 19 0
      demo/la_config.md
  53. 0 0
      demo/la_redirect.md
  54. 0 0
      demo/la_tokens.md
  55. 41 0
      demo/posts/202201.md
  56. 334 0
      demo/styles/main.css
  57. 152 0
      demo/translations.md
  58. 5 0
      error.php
  59. 二进制
      fonts/NotoSerifSC-Black.otf
  60. 二进制
      fonts/NotoSerifSC-Bold.otf
  61. 二进制
      fonts/NotoSerifSC-ExtraLight.otf
  62. 二进制
      fonts/NotoSerifSC-Light.otf
  63. 二进制
      fonts/NotoSerifSC-Medium.otf
  64. 二进制
      fonts/NotoSerifSC-Regular.otf
  65. 二进制
      fonts/NotoSerifSC-SemiBold.otf
  66. 656 0
      gogs_lamdwiki.css
  67. 1105 0
      gogs_lamdwiki.scss
  68. 二进制
      images/20210929145345.jpg
  69. 二进制
      images/202109291453450.jpg
  70. 二进制
      images/20210930111723.jpg
  71. 二进制
      images/20211008070157.jpg
  72. 二进制
      images/20211012145706.jpg
  73. 二进制
      images/20211012150250.jpg
  74. 二进制
      images/20211021103942.jpg
  75. 二进制
      images/20211024105235.jpg
  76. 二进制
      images/20211028081604.jpg
  77. 二进制
      images/20211109091920.jpg
  78. 二进制
      images/20211109092233.jpg
  79. 二进制
      images/20211109092304.jpg
  80. 二进制
      images/20211109092751.jpg
  81. 二进制
      images/20211109155406.jpg
  82. 二进制
      images/20211109155529.jpg
  83. 二进制
      images/20211109155604.jpg
  84. 二进制
      images/20211109155655.jpg
  85. 二进制
      images/20211109155722.jpg
  86. 二进制
      images/20211109155940.jpg
  87. 二进制
      images/20211109160020.jpg
  88. 二进制
      images/20211109160058.jpg
  89. 二进制
      images/20211109160245.jpg
  90. 二进制
      images/20211109160323.jpg
  91. 二进制
      images/20211109160348.jpg
  92. 二进制
      images/20211109163015.jpg
  93. 二进制
      images/20211224061904.jpg
  94. 二进制
      images/20220322141509.jpg
  95. 二进制
      images/20220322141612.jpg
  96. 二进制
      images/20220322141653.jpg
  97. 二进制
      images/20220322141700.jpg
  98. 二进制
      images/20220322141832.jpg
  99. 二进制
      images/20220322141841.jpg
  100. 二进制
      images/20220624090942.mp4

+ 14 - 0
.htaccess

@@ -0,0 +1,14 @@
+RewriteEngine on
+
+RewriteRule ^gallery$ /index.php?gallery=default [R=302,L,NC]
+
+RewriteRule ^gallery$ /index.php?post=gallery=default [R=302,L,NC]
+
+RewriteCond %{HTTPS} !=on
+RewriteCond %{HTTP_HOST} !=localhost
+RewriteCond %{REQUEST_URI}  !^.*(jpg|png|gif)$
+RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301,NC]
+
+<Files ~ "\.md$">
+deny from all
+</Files>

+ 0 - 0
.la_lock


+ 30 - 0
.well-known/apub_private_key.php

@@ -0,0 +1,30 @@
+<?php exit; ?>
+
+-----BEGIN PRIVATE KEY-----
+MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDM1hx6Qx9JoQOg
+T+6+z4yi/g/jT8scWgn9mSjDmuXrE9qXsuFKbB3JJX4mN8N1/WPmw6Wh9WxLegbo
+0JYYJquFUMI6beb8s2i2E5cM7mdp7pg4QcGRoiAatfOm72HURjTfqfy/DHXkUtCQ
+tlqQcB/hasqwsoOonZNhlPSQ/lxXPTH+UA4po7ICU5EHJCrNEiSQphn9idPzaKsR
+RRqxZgXyFlHzsVA++8vTPWzkm5NHPLF8+yAaXlyMmxG5tMndXpjMhSbGDC5psHWA
+6auvkI/negxYuzCMSpjS6HyawSOSFj4KtDgmRpZ8VosNOhh4KBIF7q+ipVBu/4QR
+AiGOqUYLAgMBAAECggEBAIA67ZrwWC7cuItsXrZe8di7mbCgrDM0fpqeffFuD88J
+H8fluLnBUygv63f7YsaDeT3mbtMj88aQPrESm1PFKY0M0HqJgJfjrIKHYwE6Yczk
+C7D0ITyNNPHjxANWSnT7+m+mV841+7uSyHQ/ZBSVzK9uDjNtnZQ6CSCGglAenCoT
+ynwnkAVVJQ8G7orwVz2VJbvgxesLvaFysa/FgN9bq4ojcfpYQwGVQQ4tYZ2xLkTs
+FvM8iewHKtVidf6lLfXM3TqgwQ48Xz9N6cizU9CR4Wlhk4F0zu7ZhRkmMEKNyYdY
+qcMvLaOZ8xRqbBF0jUs8pe40hlb3mp3fU0BDaXiSxVkCgYEA+NKKJaQxmj5nh3T6
+ZHDkknZajXDYDTjXh7Dmu8r0NqPNKR/PpJuL0W3G9SwyzaNxTN/RmCBIYa26wloL
+9BfAWDCC0JvIEqTr5WCowHSzU1gv+6X2pbgsfQul3CPgFFS91Er6Aje/2ek1g2Cc
+I2DH7P8CwSgCb0Hzj10/JY2V8C0CgYEA0r7ASP7QmsMxKIPP+HqDcm+D2hlPcGzN
++0hpsrn2xJfgxFsXEYQ5bp20ngamu8Febz6WwGMoPQvhyvIk0OLleSOMzkmUtgsV
+Qv34sj+wgOvYIJdCwicZCS+TSTvRtyIGvvkVfMrOWkuy8DNxTpcJLfnPGBU6PA/T
+fCbmglpwuhcCgYBUYYnp4/nSVhFj9X6oq0RIiwyAZOFP8pPc91f4HYaeNQRxStFv
+ADT1S+Yp5NhBtojIyipZDbGeNn1cRY9CMv/OqHzkY8AA2p8JBbOUuL7Lp9911r2D
+S6cCrP2NXzkxi99obBjGwcM1EgpMyGmK8do6++wiNwBEXLhNSREOGirb7QKBgQDP
+npcnEssGvSgP5xQbku8u7UfztR26/6ZKWgOvzV+cPJh2OdtXKXG/VWUcjibzMETN
+ydaQwN9ijPMskjPNCsvR9UEB9iGo2vBeaID7cp171V2+deaSChyzHQi4jFNGXSJS
+QGh34gE7mfo2hCerqkaj4cixzC/r/hd254gOgfUujwKBgQC58o3fkxnhwOt3GKSY
+eobZH9Up3ImHnU4gdcDDUJh4elMvUazRb6+Qi5aNmqt6YnFDtJOilXf/nZjUvMbm
+Xg7SSVapewj73pJrNfS7Cz6PTEuRZpwQ77+Tl+nWudRNxpfcGd9ipUu65Sx3X2yS
+rARdWe/3YLhgJ1dvonXgc1o1lQ==
+-----END PRIVATE KEY-----

+ 9 - 0
.well-known/apub_public_key.pem

@@ -0,0 +1,9 @@
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzNYcekMfSaEDoE/uvs+M
+ov4P40/LHFoJ/Zkow5rl6xPal7LhSmwdySV+JjfDdf1j5sOlofVsS3oG6NCWGCar
+hVDCOm3m/LNothOXDO5nae6YOEHBkaIgGrXzpu9h1EY036n8vwx15FLQkLZakHAf
+4WrKsLKDqJ2TYZT0kP5cVz0x/lAOKaOyAlORByQqzRIkkKYZ/YnT82irEUUasWYF
+8hZR87FQPvvL0z1s5JuTRzyxfPsgGl5cjJsRubTJ3V6YzIUmxgwuabB1gOmrr5CP
+53oMWLswjEqY0uh8msEjkhY+CrQ4JkaWfFaLDToYeCgSBe6voqVQbv+EEQIhjqlG
+CwIDAQAB
+-----END PUBLIC KEY-----

+ 1 - 0
.well-known/webfinger/index.php

@@ -0,0 +1 @@
+<?php header('Content-Type: application/json'); echo '{"subject":"acct:chengdulittlea@","links":[{"rel":"self","type":"application/activity+json","href":"localhost?apub_actor=1"}]}'; ?>

+ 30 - 0
.well_known/apub_private_key.php

@@ -0,0 +1,30 @@
+<?php exit; ?>
+
+-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDSkqXskbYPpieH
+bmvCru83pc+KUdR79GXDVX2LLYQ8dxOA1FMJb/L2PX6gJ2euIgpWU3BnajfNUK/F
+Oqe8zNacvIgb9027mhKGiZiQw1lpNm6IqZIDOMvjLABcfkwgftnrE72JcxQkk/pB
+iZrbaXeY+eT5cwN5zeaOeKFshXCj/0Hje3M1zWX65TYStZjiaVxiS8+i+vBOn6c/
+kNSNz8gTwnJmLkLUTGMWiXqXG5rus4nY8ecMVSfAz+lvK3alQuE+05k97jTvqmyf
+omvHHz5lSreV4MIy2wd10eY1fvNAN6TEHeZrHvIrYHhBWPjUN7LfLBtSS8f/hfcy
+oLIQ1UIvAgMBAAECggEAQSg9jWk9E7SPMLn7QD+PKAH3qU5O2Jg0UPAIK+PGsksK
+TufkVdw9KvK2Vuo6i8heuH2TH3zfjXl1GitfMe2UTuWLnFO2yuEO8HBtNAYuiX7d
+Tw5hbvvJIx8/cV9nQPrfhX0TPg/M4NClNFhXAaOt7TkOIB31qmALAnTTRlg4FZZb
+wpiw6s4ko2hwSeZVZayY5oAbsk2QueaYzB8/4hj5cWf2q2J4pADVx/Cj5YYjfgC4
+8boANyafxBx7XxUQyQlGLI9bKLukAhDIkIqNshYPUATDeRe/Cpfonq+MDgw7k7m2
+XcwMxZn1pojnPENV3F4eYgB8NYalUUAQFdGiENjvsQKBgQDymw+uqlq2/wP29gZk
+VgpSL1PIzruIHQTzrXe8M+Z025TbmJ8pPqKKTIY4OHGDsl67miT2ZT3UpapTPAy/
+2xigvaWj1nxXHMGinawyIW+izJ5iyAsgZl2Dk+Kh4125TDGCiG47KGOEkgCOSBSS
+5s5+36swf57Elel0ygMSSIys5wKBgQDeMtdN4u5Kh1KMweKuiG7DD+zARNl7Nkvb
+PRAYHp5ode3WYphNbMs9YRChSWr/DyKQLjnfVK8w774j8kC8skemfsL8vMjslbyP
+Q7sXhElGAV/zUvnZKbStzIwyo82CajfFDLuQ7n8Let5LcNIme8IdyoXTR3yQlj6i
+RdpiaVoPeQKBgBMoKWHac9VVZ8O9g52yqZf5wrztjiNeX0C6puTGmGEL4cJBV/gS
+yRTwm/yegGAs2ownzNlj/zDmb/+okz0nGNfAge7hBep/C89CchF3UHBjhfI456AE
+VUgJinDMGl+CY7px+XpUYx9ZxA94I5/B9/ZOxR7MRQYy7D9boEh2UB1rAoGAQAxx
+qX7PZ2ZIclB1EhA7tTiC6w0tj2mgRgr896bjzIKp0xFKi47fqsqu637pO6pWMS9R
++UFE2ptQuLXc2UeehaymcgnM9P1YhAdnB6lXIbnFubLiWCnAbhS3GOXEmGIbv04j
+RuyobFu3dhGIUhV8Z+/30WYazuawGUB/abyqfCkCgYEAuksGn/n4KyRlcU0x/gvk
+Q5vnk2DvuwDOC9necZHufWHST6ALpuSZIFzUnME4gwmThIdRgZ1QWff6Gppux5Qb
+bJv49RWQIG9NAA4rKGqEWkDzl/ZVwcifxDppcmTDLZgZxs9GZC+mfofEIOCeUwqa
+dk+eBorEIfL7MtfDxLCvJo0=
+-----END PRIVATE KEY-----

+ 9 - 0
.well_known/apub_public_key.pem

@@ -0,0 +1,9 @@
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0pKl7JG2D6Ynh25rwq7v
+N6XPilHUe/Rlw1V9iy2EPHcTgNRTCW/y9j1+oCdnriIKVlNwZ2o3zVCvxTqnvMzW
+nLyIG/dNu5oShomYkMNZaTZuiKmSAzjL4ywAXH5MIH7Z6xO9iXMUJJP6QYma22l3
+mPnk+XMDec3mjnihbIVwo/9B43tzNc1l+uU2ErWY4mlcYkvPovrwTp+nP5DUjc/I
+E8JyZi5C1ExjFol6lxua7rOJ2PHnDFUnwM/pbyt2pULhPtOZPe4076psn6Jrxx8+
+ZUq3leDCMtsHddHmNX7zQDekxB3max7yK2B4QVj41Dey3ywbUkvH/4X3MqCyENVC
+LwIDAQAB
+-----END PUBLIC KEY-----

+ 1 - 0
.well_known/webfinger/index.php

@@ -0,0 +1 @@
+<?php header('Content-Type: application/json'); echo '{"subject":"chengdulittlea@localhost","links":[{"rel":"self","type":"application\/activity+json","href":"https:\/\/localhost?apub_actor=1"}]}'; ?>

+ 93 - 0
OFL.txt

@@ -0,0 +1,93 @@
+Copyright 2012 Google Inc. All Rights Reserved.
+
+This Font Software is licensed under the SIL Open Font License, Version 1.1.
+This license is copied below, and is also available with a FAQ at:
+http://scripts.sil.org/OFL
+
+
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font creation
+efforts of academic and linguistic communities, and to provide a free and
+open framework in which fonts may be shared and improved in partnership
+with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded, 
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply
+to any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software components as
+distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to, deleting,
+or substituting -- in part or in whole -- any of the components of the
+Original Version, by changing formats or by porting the Font Software to a
+new environment.
+
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed, modify,
+redistribute, and sell modified and unmodified copies of the Font
+Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components,
+in Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the corresponding
+Copyright Holder. This restriction only applies to the primary font name as
+presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created
+using the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.

+ 245 - 0
PHPMailer/DSNConfigurator.php

@@ -0,0 +1,245 @@
+<?php
+
+/**
+ * PHPMailer - PHP email creation and transport class.
+ * PHP Version 5.5.
+ *
+ * @see https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project
+ *
+ * @author    Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk>
+ * @author    Jim Jagielski (jimjag) <jimjag@gmail.com>
+ * @author    Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>
+ * @author    Brent R. Matzelle (original founder)
+ * @copyright 2012 - 2023 Marcus Bointon
+ * @copyright 2010 - 2012 Jim Jagielski
+ * @copyright 2004 - 2009 Andy Prevost
+ * @license   https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html GNU Lesser General Public License
+ * @note      This program is distributed in the hope that it will be useful - WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+namespace PHPMailer\PHPMailer;
+
+/**
+ * Configure PHPMailer with DSN string.
+ *
+ * @see https://en.wikipedia.org/wiki/Data_source_name
+ *
+ * @author Oleg Voronkovich <oleg-voronkovich@yandex.ru>
+ */
+class DSNConfigurator
+{
+    /**
+     * Create new PHPMailer instance configured by DSN.
+     *
+     * @param string $dsn        DSN
+     * @param bool   $exceptions Should we throw external exceptions?
+     *
+     * @return PHPMailer
+     */
+    public static function mailer($dsn, $exceptions = null)
+    {
+        static $configurator = null;
+
+        if (null === $configurator) {
+            $configurator = new DSNConfigurator();
+        }
+
+        return $configurator->configure(new PHPMailer($exceptions), $dsn);
+    }
+
+    /**
+     * Configure PHPMailer instance with DSN string.
+     *
+     * @param PHPMailer $mailer PHPMailer instance
+     * @param string    $dsn    DSN
+     *
+     * @return PHPMailer
+     */
+    public function configure(PHPMailer $mailer, $dsn)
+    {
+        $config = $this->parseDSN($dsn);
+
+        $this->applyConfig($mailer, $config);
+
+        return $mailer;
+    }
+
+    /**
+     * Parse DSN string.
+     *
+     * @param string $dsn DSN
+     *
+     * @throws Exception If DSN is malformed
+     *
+     * @return array Configuration
+     */
+    private function parseDSN($dsn)
+    {
+        $config = $this->parseUrl($dsn);
+
+        if (false === $config || !isset($config['scheme']) || !isset($config['host'])) {
+            throw new Exception('Malformed DSN');
+        }
+
+        if (isset($config['query'])) {
+            parse_str($config['query'], $config['query']);
+        }
+
+        return $config;
+    }
+
+    /**
+     * Apply configuration to mailer.
+     *
+     * @param PHPMailer $mailer PHPMailer instance
+     * @param array     $config Configuration
+     *
+     * @throws Exception If scheme is invalid
+     */
+    private function applyConfig(PHPMailer $mailer, $config)
+    {
+        switch ($config['scheme']) {
+            case 'mail':
+                $mailer->isMail();
+                break;
+            case 'sendmail':
+                $mailer->isSendmail();
+                break;
+            case 'qmail':
+                $mailer->isQmail();
+                break;
+            case 'smtp':
+            case 'smtps':
+                $mailer->isSMTP();
+                $this->configureSMTP($mailer, $config);
+                break;
+            default:
+                throw new Exception(
+                    sprintf(
+                        'Invalid scheme: "%s". Allowed values: "mail", "sendmail", "qmail", "smtp", "smtps".',
+                        $config['scheme']
+                    )
+                );
+        }
+
+        if (isset($config['query'])) {
+            $this->configureOptions($mailer, $config['query']);
+        }
+    }
+
+    /**
+     * Configure SMTP.
+     *
+     * @param PHPMailer $mailer PHPMailer instance
+     * @param array     $config Configuration
+     */
+    private function configureSMTP($mailer, $config)
+    {
+        $isSMTPS = 'smtps' === $config['scheme'];
+
+        if ($isSMTPS) {
+            $mailer->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
+        }
+
+        $mailer->Host = $config['host'];
+
+        if (isset($config['port'])) {
+            $mailer->Port = $config['port'];
+        } elseif ($isSMTPS) {
+            $mailer->Port = SMTP::DEFAULT_SECURE_PORT;
+        }
+
+        $mailer->SMTPAuth = isset($config['user']) || isset($config['pass']);
+
+        if (isset($config['user'])) {
+            $mailer->Username = $config['user'];
+        }
+
+        if (isset($config['pass'])) {
+            $mailer->Password = $config['pass'];
+        }
+    }
+
+    /**
+     * Configure options.
+     *
+     * @param PHPMailer $mailer  PHPMailer instance
+     * @param array     $options Options
+     *
+     * @throws Exception If option is unknown
+     */
+    private function configureOptions(PHPMailer $mailer, $options)
+    {
+        $allowedOptions = get_object_vars($mailer);
+
+        unset($allowedOptions['Mailer']);
+        unset($allowedOptions['SMTPAuth']);
+        unset($allowedOptions['Username']);
+        unset($allowedOptions['Password']);
+        unset($allowedOptions['Hostname']);
+        unset($allowedOptions['Port']);
+        unset($allowedOptions['ErrorInfo']);
+
+        $allowedOptions = \array_keys($allowedOptions);
+
+        foreach ($options as $key => $value) {
+            if (!in_array($key, $allowedOptions)) {
+                throw new Exception(
+                    sprintf(
+                        'Unknown option: "%s". Allowed values: "%s"',
+                        $key,
+                        implode('", "', $allowedOptions)
+                    )
+                );
+            }
+
+            switch ($key) {
+                case 'AllowEmpty':
+                case 'SMTPAutoTLS':
+                case 'SMTPKeepAlive':
+                case 'SingleTo':
+                case 'UseSendmailOptions':
+                case 'do_verp':
+                case 'DKIM_copyHeaderFields':
+                    $mailer->$key = (bool) $value;
+                    break;
+                case 'Priority':
+                case 'SMTPDebug':
+                case 'WordWrap':
+                    $mailer->$key = (int) $value;
+                    break;
+                default:
+                    $mailer->$key = $value;
+                    break;
+            }
+        }
+    }
+
+    /**
+     * Parse a URL.
+     * Wrapper for the built-in parse_url function to work around a bug in PHP 5.5.
+     *
+     * @param string $url URL
+     *
+     * @return array|false
+     */
+    protected function parseUrl($url)
+    {
+        if (\PHP_VERSION_ID >= 50600 || false === strpos($url, '?')) {
+            return parse_url($url);
+        }
+
+        $chunks = explode('?', $url);
+        if (is_array($chunks)) {
+            $result = parse_url($chunks[0]);
+            if (is_array($result)) {
+                $result['query'] = $chunks[1];
+            }
+            return $result;
+        }
+
+        return false;
+    }
+}

+ 40 - 0
PHPMailer/Exception.php

@@ -0,0 +1,40 @@
+<?php
+
+/**
+ * PHPMailer Exception class.
+ * PHP Version 5.5.
+ *
+ * @see       https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project
+ *
+ * @author    Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk>
+ * @author    Jim Jagielski (jimjag) <jimjag@gmail.com>
+ * @author    Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>
+ * @author    Brent R. Matzelle (original founder)
+ * @copyright 2012 - 2020 Marcus Bointon
+ * @copyright 2010 - 2012 Jim Jagielski
+ * @copyright 2004 - 2009 Andy Prevost
+ * @license   https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html GNU Lesser General Public License
+ * @note      This program is distributed in the hope that it will be useful - WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+namespace PHPMailer\PHPMailer;
+
+/**
+ * PHPMailer exception handler.
+ *
+ * @author Marcus Bointon <phpmailer@synchromedia.co.uk>
+ */
+class Exception extends \Exception
+{
+    /**
+     * Prettify error message output.
+     *
+     * @return string
+     */
+    public function errorMessage()
+    {
+        return '<strong>' . htmlspecialchars($this->getMessage(), ENT_COMPAT | ENT_HTML401) . "</strong><br />\n";
+    }
+}

+ 139 - 0
PHPMailer/OAuth.php

@@ -0,0 +1,139 @@
+<?php
+
+/**
+ * PHPMailer - PHP email creation and transport class.
+ * PHP Version 5.5.
+ *
+ * @see       https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project
+ *
+ * @author    Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk>
+ * @author    Jim Jagielski (jimjag) <jimjag@gmail.com>
+ * @author    Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>
+ * @author    Brent R. Matzelle (original founder)
+ * @copyright 2012 - 2020 Marcus Bointon
+ * @copyright 2010 - 2012 Jim Jagielski
+ * @copyright 2004 - 2009 Andy Prevost
+ * @license   https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html GNU Lesser General Public License
+ * @note      This program is distributed in the hope that it will be useful - WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+namespace PHPMailer\PHPMailer;
+
+use League\OAuth2\Client\Grant\RefreshToken;
+use League\OAuth2\Client\Provider\AbstractProvider;
+use League\OAuth2\Client\Token\AccessToken;
+
+/**
+ * OAuth - OAuth2 authentication wrapper class.
+ * Uses the oauth2-client package from the League of Extraordinary Packages.
+ *
+ * @see     https://oauth2-client.thephpleague.com
+ *
+ * @author  Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk>
+ */
+class OAuth implements OAuthTokenProvider
+{
+    /**
+     * An instance of the League OAuth Client Provider.
+     *
+     * @var AbstractProvider
+     */
+    protected $provider;
+
+    /**
+     * The current OAuth access token.
+     *
+     * @var AccessToken
+     */
+    protected $oauthToken;
+
+    /**
+     * The user's email address, usually used as the login ID
+     * and also the from address when sending email.
+     *
+     * @var string
+     */
+    protected $oauthUserEmail = '';
+
+    /**
+     * The client secret, generated in the app definition of the service you're connecting to.
+     *
+     * @var string
+     */
+    protected $oauthClientSecret = '';
+
+    /**
+     * The client ID, generated in the app definition of the service you're connecting to.
+     *
+     * @var string
+     */
+    protected $oauthClientId = '';
+
+    /**
+     * The refresh token, used to obtain new AccessTokens.
+     *
+     * @var string
+     */
+    protected $oauthRefreshToken = '';
+
+    /**
+     * OAuth constructor.
+     *
+     * @param array $options Associative array containing
+     *                       `provider`, `userName`, `clientSecret`, `clientId` and `refreshToken` elements
+     */
+    public function __construct($options)
+    {
+        $this->provider = $options['provider'];
+        $this->oauthUserEmail = $options['userName'];
+        $this->oauthClientSecret = $options['clientSecret'];
+        $this->oauthClientId = $options['clientId'];
+        $this->oauthRefreshToken = $options['refreshToken'];
+    }
+
+    /**
+     * Get a new RefreshToken.
+     *
+     * @return RefreshToken
+     */
+    protected function getGrant()
+    {
+        return new RefreshToken();
+    }
+
+    /**
+     * Get a new AccessToken.
+     *
+     * @return AccessToken
+     */
+    protected function getToken()
+    {
+        return $this->provider->getAccessToken(
+            $this->getGrant(),
+            ['refresh_token' => $this->oauthRefreshToken]
+        );
+    }
+
+    /**
+     * Generate a base64-encoded OAuth token.
+     *
+     * @return string
+     */
+    public function getOauth64()
+    {
+        //Get a new token if it's not available or has expired
+        if (null === $this->oauthToken || $this->oauthToken->hasExpired()) {
+            $this->oauthToken = $this->getToken();
+        }
+
+        return base64_encode(
+            'user=' .
+            $this->oauthUserEmail .
+            "\001auth=Bearer " .
+            $this->oauthToken .
+            "\001\001"
+        );
+    }
+}

+ 44 - 0
PHPMailer/OAuthTokenProvider.php

@@ -0,0 +1,44 @@
+<?php
+
+/**
+ * PHPMailer - PHP email creation and transport class.
+ * PHP Version 5.5.
+ *
+ * @see https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project
+ *
+ * @author    Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk>
+ * @author    Jim Jagielski (jimjag) <jimjag@gmail.com>
+ * @author    Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>
+ * @author    Brent R. Matzelle (original founder)
+ * @copyright 2012 - 2020 Marcus Bointon
+ * @copyright 2010 - 2012 Jim Jagielski
+ * @copyright 2004 - 2009 Andy Prevost
+ * @license   https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html GNU Lesser General Public License
+ * @note      This program is distributed in the hope that it will be useful - WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+namespace PHPMailer\PHPMailer;
+
+/**
+ * OAuthTokenProvider - OAuth2 token provider interface.
+ * Provides base64 encoded OAuth2 auth strings for SMTP authentication.
+ *
+ * @see     OAuth
+ * @see     SMTP::authenticate()
+ *
+ * @author  Peter Scopes (pdscopes)
+ * @author  Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk>
+ */
+interface OAuthTokenProvider
+{
+    /**
+     * Generate a base64-encoded OAuth token ensuring that the access token has not expired.
+     * The string to be base 64 encoded should be in the form:
+     * "user=<user_email_address>\001auth=Bearer <access_token>\001\001"
+     *
+     * @return string
+     */
+    public function getOauth64();
+}

+ 5248 - 0
PHPMailer/PHPMailer.php

@@ -0,0 +1,5248 @@
+<?php
+
+/**
+ * PHPMailer - PHP email creation and transport class.
+ * PHP Version 5.5.
+ *
+ * @see https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project
+ *
+ * @author    Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk>
+ * @author    Jim Jagielski (jimjag) <jimjag@gmail.com>
+ * @author    Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>
+ * @author    Brent R. Matzelle (original founder)
+ * @copyright 2012 - 2020 Marcus Bointon
+ * @copyright 2010 - 2012 Jim Jagielski
+ * @copyright 2004 - 2009 Andy Prevost
+ * @license   https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html GNU Lesser General Public License
+ * @note      This program is distributed in the hope that it will be useful - WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+namespace PHPMailer\PHPMailer;
+
+/**
+ * PHPMailer - PHP email creation and transport class.
+ *
+ * @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk>
+ * @author Jim Jagielski (jimjag) <jimjag@gmail.com>
+ * @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>
+ * @author Brent R. Matzelle (original founder)
+ */
+class PHPMailer
+{
+    const CHARSET_ASCII = 'us-ascii';
+    const CHARSET_ISO88591 = 'iso-8859-1';
+    const CHARSET_UTF8 = 'utf-8';
+
+    const CONTENT_TYPE_PLAINTEXT = 'text/plain';
+    const CONTENT_TYPE_TEXT_CALENDAR = 'text/calendar';
+    const CONTENT_TYPE_TEXT_HTML = 'text/html';
+    const CONTENT_TYPE_MULTIPART_ALTERNATIVE = 'multipart/alternative';
+    const CONTENT_TYPE_MULTIPART_MIXED = 'multipart/mixed';
+    const CONTENT_TYPE_MULTIPART_RELATED = 'multipart/related';
+
+    const ENCODING_7BIT = '7bit';
+    const ENCODING_8BIT = '8bit';
+    const ENCODING_BASE64 = 'base64';
+    const ENCODING_BINARY = 'binary';
+    const ENCODING_QUOTED_PRINTABLE = 'quoted-printable';
+
+    const ENCRYPTION_STARTTLS = 'tls';
+    const ENCRYPTION_SMTPS = 'ssl';
+
+    const ICAL_METHOD_REQUEST = 'REQUEST';
+    const ICAL_METHOD_PUBLISH = 'PUBLISH';
+    const ICAL_METHOD_REPLY = 'REPLY';
+    const ICAL_METHOD_ADD = 'ADD';
+    const ICAL_METHOD_CANCEL = 'CANCEL';
+    const ICAL_METHOD_REFRESH = 'REFRESH';
+    const ICAL_METHOD_COUNTER = 'COUNTER';
+    const ICAL_METHOD_DECLINECOUNTER = 'DECLINECOUNTER';
+
+    /**
+     * Email priority.
+     * Options: null (default), 1 = High, 3 = Normal, 5 = low.
+     * When null, the header is not set at all.
+     *
+     * @var int|null
+     */
+    public $Priority;
+
+    /**
+     * The character set of the message.
+     *
+     * @var string
+     */
+    public $CharSet = self::CHARSET_ISO88591;
+
+    /**
+     * The MIME Content-type of the message.
+     *
+     * @var string
+     */
+    public $ContentType = self::CONTENT_TYPE_PLAINTEXT;
+
+    /**
+     * The message encoding.
+     * Options: "8bit", "7bit", "binary", "base64", and "quoted-printable".
+     *
+     * @var string
+     */
+    public $Encoding = self::ENCODING_8BIT;
+
+    /**
+     * Holds the most recent mailer error message.
+     *
+     * @var string
+     */
+    public $ErrorInfo = '';
+
+    /**
+     * The From email address for the message.
+     *
+     * @var string
+     */
+    public $From = '';
+
+    /**
+     * The From name of the message.
+     *
+     * @var string
+     */
+    public $FromName = '';
+
+    /**
+     * The envelope sender of the message.
+     * This will usually be turned into a Return-Path header by the receiver,
+     * and is the address that bounces will be sent to.
+     * If not empty, will be passed via `-f` to sendmail or as the 'MAIL FROM' value over SMTP.
+     *
+     * @var string
+     */
+    public $Sender = '';
+
+    /**
+     * The Subject of the message.
+     *
+     * @var string
+     */
+    public $Subject = '';
+
+    /**
+     * An HTML or plain text message body.
+     * If HTML then call isHTML(true).
+     *
+     * @var string
+     */
+    public $Body = '';
+
+    /**
+     * The plain-text message body.
+     * This body can be read by mail clients that do not have HTML email
+     * capability such as mutt & Eudora.
+     * Clients that can read HTML will view the normal Body.
+     *
+     * @var string
+     */
+    public $AltBody = '';
+
+    /**
+     * An iCal message part body.
+     * Only supported in simple alt or alt_inline message types
+     * To generate iCal event structures, use classes like EasyPeasyICS or iCalcreator.
+     *
+     * @see https://kigkonsult.se/iCalcreator/
+     *
+     * @var string
+     */
+    public $Ical = '';
+
+    /**
+     * Value-array of "method" in Contenttype header "text/calendar"
+     *
+     * @var string[]
+     */
+    protected static $IcalMethods = [
+        self::ICAL_METHOD_REQUEST,
+        self::ICAL_METHOD_PUBLISH,
+        self::ICAL_METHOD_REPLY,
+        self::ICAL_METHOD_ADD,
+        self::ICAL_METHOD_CANCEL,
+        self::ICAL_METHOD_REFRESH,
+        self::ICAL_METHOD_COUNTER,
+        self::ICAL_METHOD_DECLINECOUNTER,
+    ];
+
+    /**
+     * The complete compiled MIME message body.
+     *
+     * @var string
+     */
+    protected $MIMEBody = '';
+
+    /**
+     * The complete compiled MIME message headers.
+     *
+     * @var string
+     */
+    protected $MIMEHeader = '';
+
+    /**
+     * Extra headers that createHeader() doesn't fold in.
+     *
+     * @var string
+     */
+    protected $mailHeader = '';
+
+    /**
+     * Word-wrap the message body to this number of chars.
+     * Set to 0 to not wrap. A useful value here is 78, for RFC2822 section 2.1.1 compliance.
+     *
+     * @see static::STD_LINE_LENGTH
+     *
+     * @var int
+     */
+    public $WordWrap = 0;
+
+    /**
+     * Which method to use to send mail.
+     * Options: "mail", "sendmail", or "smtp".
+     *
+     * @var string
+     */
+    public $Mailer = 'mail';
+
+    /**
+     * The path to the sendmail program.
+     *
+     * @var string
+     */
+    public $Sendmail = '/usr/sbin/sendmail';
+
+    /**
+     * Whether mail() uses a fully sendmail-compatible MTA.
+     * One which supports sendmail's "-oi -f" options.
+     *
+     * @var bool
+     */
+    public $UseSendmailOptions = true;
+
+    /**
+     * The email address that a reading confirmation should be sent to, also known as read receipt.
+     *
+     * @var string
+     */
+    public $ConfirmReadingTo = '';
+
+    /**
+     * The hostname to use in the Message-ID header and as default HELO string.
+     * If empty, PHPMailer attempts to find one with, in order,
+     * $_SERVER['SERVER_NAME'], gethostname(), php_uname('n'), or the value
+     * 'localhost.localdomain'.
+     *
+     * @see PHPMailer::$Helo
+     *
+     * @var string
+     */
+    public $Hostname = '';
+
+    /**
+     * An ID to be used in the Message-ID header.
+     * If empty, a unique id will be generated.
+     * You can set your own, but it must be in the format "<id@domain>",
+     * as defined in RFC5322 section 3.6.4 or it will be ignored.
+     *
+     * @see https://tools.ietf.org/html/rfc5322#section-3.6.4
+     *
+     * @var string
+     */
+    public $MessageID = '';
+
+    /**
+     * The message Date to be used in the Date header.
+     * If empty, the current date will be added.
+     *
+     * @var string
+     */
+    public $MessageDate = '';
+
+    /**
+     * SMTP hosts.
+     * Either a single hostname or multiple semicolon-delimited hostnames.
+     * You can also specify a different port
+     * for each host by using this format: [hostname:port]
+     * (e.g. "smtp1.example.com:25;smtp2.example.com").
+     * You can also specify encryption type, for example:
+     * (e.g. "tls://smtp1.example.com:587;ssl://smtp2.example.com:465").
+     * Hosts will be tried in order.
+     *
+     * @var string
+     */
+    public $Host = 'localhost';
+
+    /**
+     * The default SMTP server port.
+     *
+     * @var int
+     */
+    public $Port = 25;
+
+    /**
+     * The SMTP HELO/EHLO name used for the SMTP connection.
+     * Default is $Hostname. If $Hostname is empty, PHPMailer attempts to find
+     * one with the same method described above for $Hostname.
+     *
+     * @see PHPMailer::$Hostname
+     *
+     * @var string
+     */
+    public $Helo = '';
+
+    /**
+     * What kind of encryption to use on the SMTP connection.
+     * Options: '', static::ENCRYPTION_STARTTLS, or static::ENCRYPTION_SMTPS.
+     *
+     * @var string
+     */
+    public $SMTPSecure = '';
+
+    /**
+     * Whether to enable TLS encryption automatically if a server supports it,
+     * even if `SMTPSecure` is not set to 'tls'.
+     * Be aware that in PHP >= 5.6 this requires that the server's certificates are valid.
+     *
+     * @var bool
+     */
+    public $SMTPAutoTLS = true;
+
+    /**
+     * Whether to use SMTP authentication.
+     * Uses the Username and Password properties.
+     *
+     * @see PHPMailer::$Username
+     * @see PHPMailer::$Password
+     *
+     * @var bool
+     */
+    public $SMTPAuth = false;
+
+    /**
+     * Options array passed to stream_context_create when connecting via SMTP.
+     *
+     * @var array
+     */
+    public $SMTPOptions = [];
+
+    /**
+     * SMTP username.
+     *
+     * @var string
+     */
+    public $Username = '';
+
+    /**
+     * SMTP password.
+     *
+     * @var string
+     */
+    public $Password = '';
+
+    /**
+     * SMTP authentication type. Options are CRAM-MD5, LOGIN, PLAIN, XOAUTH2.
+     * If not specified, the first one from that list that the server supports will be selected.
+     *
+     * @var string
+     */
+    public $AuthType = '';
+
+    /**
+     * SMTP SMTPXClient command attibutes
+     *
+     * @var array
+     */
+    protected $SMTPXClient = [];
+
+    /**
+     * An implementation of the PHPMailer OAuthTokenProvider interface.
+     *
+     * @var OAuthTokenProvider
+     */
+    protected $oauth;
+
+    /**
+     * The SMTP server timeout in seconds.
+     * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2.
+     *
+     * @var int
+     */
+    public $Timeout = 300;
+
+    /**
+     * Comma separated list of DSN notifications
+     * 'NEVER' under no circumstances a DSN must be returned to the sender.
+     *         If you use NEVER all other notifications will be ignored.
+     * 'SUCCESS' will notify you when your mail has arrived at its destination.
+     * 'FAILURE' will arrive if an error occurred during delivery.
+     * 'DELAY'   will notify you if there is an unusual delay in delivery, but the actual
+     *           delivery's outcome (success or failure) is not yet decided.
+     *
+     * @see https://tools.ietf.org/html/rfc3461 See section 4.1 for more information about NOTIFY
+     */
+    public $dsn = '';
+
+    /**
+     * SMTP class debug output mode.
+     * Debug output level.
+     * Options:
+     * @see SMTP::DEBUG_OFF: No output
+     * @see SMTP::DEBUG_CLIENT: Client messages
+     * @see SMTP::DEBUG_SERVER: Client and server messages
+     * @see SMTP::DEBUG_CONNECTION: As SERVER plus connection status
+     * @see SMTP::DEBUG_LOWLEVEL: Noisy, low-level data output, rarely needed
+     *
+     * @see SMTP::$do_debug
+     *
+     * @var int
+     */
+    public $SMTPDebug = 0;
+
+    /**
+     * How to handle debug output.
+     * Options:
+     * * `echo` Output plain-text as-is, appropriate for CLI
+     * * `html` Output escaped, line breaks converted to `<br>`, appropriate for browser output
+     * * `error_log` Output to error log as configured in php.ini
+     * By default PHPMailer will use `echo` if run from a `cli` or `cli-server` SAPI, `html` otherwise.
+     * Alternatively, you can provide a callable expecting two params: a message string and the debug level:
+     *
+     * ```php
+     * $mail->Debugoutput = function($str, $level) {echo "debug level $level; message: $str";};
+     * ```
+     *
+     * Alternatively, you can pass in an instance of a PSR-3 compatible logger, though only `debug`
+     * level output is used:
+     *
+     * ```php
+     * $mail->Debugoutput = new myPsr3Logger;
+     * ```
+     *
+     * @see SMTP::$Debugoutput
+     *
+     * @var string|callable|\Psr\Log\LoggerInterface
+     */
+    public $Debugoutput = 'echo';
+
+    /**
+     * Whether to keep the SMTP connection open after each message.
+     * If this is set to true then the connection will remain open after a send,
+     * and closing the connection will require an explicit call to smtpClose().
+     * It's a good idea to use this if you are sending multiple messages as it reduces overhead.
+     * See the mailing list example for how to use it.
+     *
+     * @var bool
+     */
+    public $SMTPKeepAlive = false;
+
+    /**
+     * Whether to split multiple to addresses into multiple messages
+     * or send them all in one message.
+     * Only supported in `mail` and `sendmail` transports, not in SMTP.
+     *
+     * @var bool
+     *
+     * @deprecated 6.0.0 PHPMailer isn't a mailing list manager!
+     */
+    public $SingleTo = false;
+
+    /**
+     * Storage for addresses when SingleTo is enabled.
+     *
+     * @var array
+     */
+    protected $SingleToArray = [];
+
+    /**
+     * Whether to generate VERP addresses on send.
+     * Only applicable when sending via SMTP.
+     *
+     * @see https://en.wikipedia.org/wiki/Variable_envelope_return_path
+     * @see https://www.postfix.org/VERP_README.html Postfix VERP info
+     *
+     * @var bool
+     */
+    public $do_verp = false;
+
+    /**
+     * Whether to allow sending messages with an empty body.
+     *
+     * @var bool
+     */
+    public $AllowEmpty = false;
+
+    /**
+     * DKIM selector.
+     *
+     * @var string
+     */
+    public $DKIM_selector = '';
+
+    /**
+     * DKIM Identity.
+     * Usually the email address used as the source of the email.
+     *
+     * @var string
+     */
+    public $DKIM_identity = '';
+
+    /**
+     * DKIM passphrase.
+     * Used if your key is encrypted.
+     *
+     * @var string
+     */
+    public $DKIM_passphrase = '';
+
+    /**
+     * DKIM signing domain name.
+     *
+     * @example 'example.com'
+     *
+     * @var string
+     */
+    public $DKIM_domain = '';
+
+    /**
+     * DKIM Copy header field values for diagnostic use.
+     *
+     * @var bool
+     */
+    public $DKIM_copyHeaderFields = true;
+
+    /**
+     * DKIM Extra signing headers.
+     *
+     * @example ['List-Unsubscribe', 'List-Help']
+     *
+     * @var array
+     */
+    public $DKIM_extraHeaders = [];
+
+    /**
+     * DKIM private key file path.
+     *
+     * @var string
+     */
+    public $DKIM_private = '';
+
+    /**
+     * DKIM private key string.
+     *
+     * If set, takes precedence over `$DKIM_private`.
+     *
+     * @var string
+     */
+    public $DKIM_private_string = '';
+
+    /**
+     * Callback Action function name.
+     *
+     * The function that handles the result of the send email action.
+     * It is called out by send() for each email sent.
+     *
+     * Value can be any php callable: https://www.php.net/is_callable
+     *
+     * Parameters:
+     *   bool $result           result of the send action
+     *   array   $to            email addresses of the recipients
+     *   array   $cc            cc email addresses
+     *   array   $bcc           bcc email addresses
+     *   string  $subject       the subject
+     *   string  $body          the email body
+     *   string  $from          email address of sender
+     *   string  $extra         extra information of possible use
+     *                          "smtp_transaction_id' => last smtp transaction id
+     *
+     * @var string
+     */
+    public $action_function = '';
+
+    /**
+     * What to put in the X-Mailer header.
+     * Options: An empty string for PHPMailer default, whitespace/null for none, or a string to use.
+     *
+     * @var string|null
+     */
+    public $XMailer = '';
+
+    /**
+     * Which validator to use by default when validating email addresses.
+     * May be a callable to inject your own validator, but there are several built-in validators.
+     * The default validator uses PHP's FILTER_VALIDATE_EMAIL filter_var option.
+     *
+     * @see PHPMailer::validateAddress()
+     *
+     * @var string|callable
+     */
+    public static $validator = 'php';
+
+    /**
+     * An instance of the SMTP sender class.
+     *
+     * @var SMTP
+     */
+    protected $smtp;
+
+    /**
+     * The array of 'to' names and addresses.
+     *
+     * @var array
+     */
+    protected $to = [];
+
+    /**
+     * The array of 'cc' names and addresses.
+     *
+     * @var array
+     */
+    protected $cc = [];
+
+    /**
+     * The array of 'bcc' names and addresses.
+     *
+     * @var array
+     */
+    protected $bcc = [];
+
+    /**
+     * The array of reply-to names and addresses.
+     *
+     * @var array
+     */
+    protected $ReplyTo = [];
+
+    /**
+     * An array of all kinds of addresses.
+     * Includes all of $to, $cc, $bcc.
+     *
+     * @see PHPMailer::$to
+     * @see PHPMailer::$cc
+     * @see PHPMailer::$bcc
+     *
+     * @var array
+     */
+    protected $all_recipients = [];
+
+    /**
+     * An array of names and addresses queued for validation.
+     * In send(), valid and non duplicate entries are moved to $all_recipients
+     * and one of $to, $cc, or $bcc.
+     * This array is used only for addresses with IDN.
+     *
+     * @see PHPMailer::$to
+     * @see PHPMailer::$cc
+     * @see PHPMailer::$bcc
+     * @see PHPMailer::$all_recipients
+     *
+     * @var array
+     */
+    protected $RecipientsQueue = [];
+
+    /**
+     * An array of reply-to names and addresses queued for validation.
+     * In send(), valid and non duplicate entries are moved to $ReplyTo.
+     * This array is used only for addresses with IDN.
+     *
+     * @see PHPMailer::$ReplyTo
+     *
+     * @var array
+     */
+    protected $ReplyToQueue = [];
+
+    /**
+     * The array of attachments.
+     *
+     * @var array
+     */
+    protected $attachment = [];
+
+    /**
+     * The array of custom headers.
+     *
+     * @var array
+     */
+    protected $CustomHeader = [];
+
+    /**
+     * The most recent Message-ID (including angular brackets).
+     *
+     * @var string
+     */
+    protected $lastMessageID = '';
+
+    /**
+     * The message's MIME type.
+     *
+     * @var string
+     */
+    protected $message_type = '';
+
+    /**
+     * The array of MIME boundary strings.
+     *
+     * @var array
+     */
+    protected $boundary = [];
+
+    /**
+     * The array of available text strings for the current language.
+     *
+     * @var array
+     */
+    protected $language = [];
+
+    /**
+     * The number of errors encountered.
+     *
+     * @var int
+     */
+    protected $error_count = 0;
+
+    /**
+     * The S/MIME certificate file path.
+     *
+     * @var string
+     */
+    protected $sign_cert_file = '';
+
+    /**
+     * The S/MIME key file path.
+     *
+     * @var string
+     */
+    protected $sign_key_file = '';
+
+    /**
+     * The optional S/MIME extra certificates ("CA Chain") file path.
+     *
+     * @var string
+     */
+    protected $sign_extracerts_file = '';
+
+    /**
+     * The S/MIME password for the key.
+     * Used only if the key is encrypted.
+     *
+     * @var string
+     */
+    protected $sign_key_pass = '';
+
+    /**
+     * Whether to throw exceptions for errors.
+     *
+     * @var bool
+     */
+    protected $exceptions = false;
+
+    /**
+     * Unique ID used for message ID and boundaries.
+     *
+     * @var string
+     */
+    protected $uniqueid = '';
+
+    /**
+     * The PHPMailer Version number.
+     *
+     * @var string
+     */
+    const VERSION = '6.9.1';
+
+    /**
+     * Error severity: message only, continue processing.
+     *
+     * @var int
+     */
+    const STOP_MESSAGE = 0;
+
+    /**
+     * Error severity: message, likely ok to continue processing.
+     *
+     * @var int
+     */
+    const STOP_CONTINUE = 1;
+
+    /**
+     * Error severity: message, plus full stop, critical error reached.
+     *
+     * @var int
+     */
+    const STOP_CRITICAL = 2;
+
+    /**
+     * The SMTP standard CRLF line break.
+     * If you want to change line break format, change static::$LE, not this.
+     */
+    const CRLF = "\r\n";
+
+    /**
+     * "Folding White Space" a white space string used for line folding.
+     */
+    const FWS = ' ';
+
+    /**
+     * SMTP RFC standard line ending; Carriage Return, Line Feed.
+     *
+     * @var string
+     */
+    protected static $LE = self::CRLF;
+
+    /**
+     * The maximum line length supported by mail().
+     *
+     * Background: mail() will sometimes corrupt messages
+     * with headers longer than 65 chars, see #818.
+     *
+     * @var int
+     */
+    const MAIL_MAX_LINE_LENGTH = 63;
+
+    /**
+     * The maximum line length allowed by RFC 2822 section 2.1.1.
+     *
+     * @var int
+     */
+    const MAX_LINE_LENGTH = 998;
+
+    /**
+     * The lower maximum line length allowed by RFC 2822 section 2.1.1.
+     * This length does NOT include the line break
+     * 76 means that lines will be 77 or 78 chars depending on whether
+     * the line break format is LF or CRLF; both are valid.
+     *
+     * @var int
+     */
+    const STD_LINE_LENGTH = 76;
+
+    /**
+     * Constructor.
+     *
+     * @param bool $exceptions Should we throw external exceptions?
+     */
+    public function __construct($exceptions = null)
+    {
+        if (null !== $exceptions) {
+            $this->exceptions = (bool) $exceptions;
+        }
+        //Pick an appropriate debug output format automatically
+        $this->Debugoutput = (strpos(PHP_SAPI, 'cli') !== false ? 'echo' : 'html');
+    }
+
+    /**
+     * Destructor.
+     */
+    public function __destruct()
+    {
+        //Close any open SMTP connection nicely
+        $this->smtpClose();
+    }
+
+    /**
+     * Call mail() in a safe_mode-aware fashion.
+     * Also, unless sendmail_path points to sendmail (or something that
+     * claims to be sendmail), don't pass params (not a perfect fix,
+     * but it will do).
+     *
+     * @param string      $to      To
+     * @param string      $subject Subject
+     * @param string      $body    Message Body
+     * @param string      $header  Additional Header(s)
+     * @param string|null $params  Params
+     *
+     * @return bool
+     */
+    private function mailPassthru($to, $subject, $body, $header, $params)
+    {
+        //Check overloading of mail function to avoid double-encoding
+        if ((int)ini_get('mbstring.func_overload') & 1) {
+            $subject = $this->secureHeader($subject);
+        } else {
+            $subject = $this->encodeHeader($this->secureHeader($subject));
+        }
+        //Calling mail() with null params breaks
+        $this->edebug('Sending with mail()');
+        $this->edebug('Sendmail path: ' . ini_get('sendmail_path'));
+        $this->edebug("Envelope sender: {$this->Sender}");
+        $this->edebug("To: {$to}");
+        $this->edebug("Subject: {$subject}");
+        $this->edebug("Headers: {$header}");
+        if (!$this->UseSendmailOptions || null === $params) {
+            $result = @mail($to, $subject, $body, $header);
+        } else {
+            $this->edebug("Additional params: {$params}");
+            $result = @mail($to, $subject, $body, $header, $params);
+        }
+        $this->edebug('Result: ' . ($result ? 'true' : 'false'));
+        return $result;
+    }
+
+    /**
+     * Output debugging info via a user-defined method.
+     * Only generates output if debug output is enabled.
+     *
+     * @see PHPMailer::$Debugoutput
+     * @see PHPMailer::$SMTPDebug
+     *
+     * @param string $str
+     */
+    protected function edebug($str)
+    {
+        if ($this->SMTPDebug <= 0) {
+            return;
+        }
+        //Is this a PSR-3 logger?
+        if ($this->Debugoutput instanceof \Psr\Log\LoggerInterface) {
+            $this->Debugoutput->debug(rtrim($str, "\r\n"));
+
+            return;
+        }
+        //Avoid clash with built-in function names
+        if (is_callable($this->Debugoutput) && !in_array($this->Debugoutput, ['error_log', 'html', 'echo'])) {
+            call_user_func($this->Debugoutput, $str, $this->SMTPDebug);
+
+            return;
+        }
+        switch ($this->Debugoutput) {
+            case 'error_log':
+                //Don't output, just log
+                /** @noinspection ForgottenDebugOutputInspection */
+                error_log($str);
+                break;
+            case 'html':
+                //Cleans up output a bit for a better looking, HTML-safe output
+                echo htmlentities(
+                    preg_replace('/[\r\n]+/', '', $str),
+                    ENT_QUOTES,
+                    'UTF-8'
+                ), "<br>\n";
+                break;
+            case 'echo':
+            default:
+                //Normalize line breaks
+                $str = preg_replace('/\r\n|\r/m', "\n", $str);
+                echo gmdate('Y-m-d H:i:s'),
+                "\t",
+                    //Trim trailing space
+                trim(
+                    //Indent for readability, except for trailing break
+                    str_replace(
+                        "\n",
+                        "\n                   \t                  ",
+                        trim($str)
+                    )
+                ),
+                "\n";
+        }
+    }
+
+    /**
+     * Sets message type to HTML or plain.
+     *
+     * @param bool $isHtml True for HTML mode
+     */
+    public function isHTML($isHtml = true)
+    {
+        if ($isHtml) {
+            $this->ContentType = static::CONTENT_TYPE_TEXT_HTML;
+        } else {
+            $this->ContentType = static::CONTENT_TYPE_PLAINTEXT;
+        }
+    }
+
+    /**
+     * Send messages using SMTP.
+     */
+    public function isSMTP()
+    {
+        $this->Mailer = 'smtp';
+    }
+
+    /**
+     * Send messages using PHP's mail() function.
+     */
+    public function isMail()
+    {
+        $this->Mailer = 'mail';
+    }
+
+    /**
+     * Send messages using $Sendmail.
+     */
+    public function isSendmail()
+    {
+        $ini_sendmail_path = ini_get('sendmail_path');
+
+        if (false === stripos($ini_sendmail_path, 'sendmail')) {
+            $this->Sendmail = '/usr/sbin/sendmail';
+        } else {
+            $this->Sendmail = $ini_sendmail_path;
+        }
+        $this->Mailer = 'sendmail';
+    }
+
+    /**
+     * Send messages using qmail.
+     */
+    public function isQmail()
+    {
+        $ini_sendmail_path = ini_get('sendmail_path');
+
+        if (false === stripos($ini_sendmail_path, 'qmail')) {
+            $this->Sendmail = '/var/qmail/bin/qmail-inject';
+        } else {
+            $this->Sendmail = $ini_sendmail_path;
+        }
+        $this->Mailer = 'qmail';
+    }
+
+    /**
+     * Add a "To" address.
+     *
+     * @param string $address The email address to send to
+     * @param string $name
+     *
+     * @throws Exception
+     *
+     * @return bool true on success, false if address already used or invalid in some way
+     */
+    public function addAddress($address, $name = '')
+    {
+        return $this->addOrEnqueueAnAddress('to', $address, $name);
+    }
+
+    /**
+     * Add a "CC" address.
+     *
+     * @param string $address The email address to send to
+     * @param string $name
+     *
+     * @throws Exception
+     *
+     * @return bool true on success, false if address already used or invalid in some way
+     */
+    public function addCC($address, $name = '')
+    {
+        return $this->addOrEnqueueAnAddress('cc', $address, $name);
+    }
+
+    /**
+     * Add a "BCC" address.
+     *
+     * @param string $address The email address to send to
+     * @param string $name
+     *
+     * @throws Exception
+     *
+     * @return bool true on success, false if address already used or invalid in some way
+     */
+    public function addBCC($address, $name = '')
+    {
+        return $this->addOrEnqueueAnAddress('bcc', $address, $name);
+    }
+
+    /**
+     * Add a "Reply-To" address.
+     *
+     * @param string $address The email address to reply to
+     * @param string $name
+     *
+     * @throws Exception
+     *
+     * @return bool true on success, false if address already used or invalid in some way
+     */
+    public function addReplyTo($address, $name = '')
+    {
+        return $this->addOrEnqueueAnAddress('Reply-To', $address, $name);
+    }
+
+    /**
+     * Add an address to one of the recipient arrays or to the ReplyTo array. Because PHPMailer
+     * can't validate addresses with an IDN without knowing the PHPMailer::$CharSet (that can still
+     * be modified after calling this function), addition of such addresses is delayed until send().
+     * Addresses that have been added already return false, but do not throw exceptions.
+     *
+     * @param string $kind    One of 'to', 'cc', 'bcc', or 'ReplyTo'
+     * @param string $address The email address
+     * @param string $name    An optional username associated with the address
+     *
+     * @throws Exception
+     *
+     * @return bool true on success, false if address already used or invalid in some way
+     */
+    protected function addOrEnqueueAnAddress($kind, $address, $name)
+    {
+        $pos = false;
+        if ($address !== null) {
+            $address = trim($address);
+            $pos = strrpos($address, '@');
+        }
+        if (false === $pos) {
+            //At-sign is missing.
+            $error_message = sprintf(
+                '%s (%s): %s',
+                $this->lang('invalid_address'),
+                $kind,
+                $address
+            );
+            $this->setError($error_message);
+            $this->edebug($error_message);
+            if ($this->exceptions) {
+                throw new Exception($error_message);
+            }
+
+            return false;
+        }
+        if ($name !== null && is_string($name)) {
+            $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
+        } else {
+            $name = '';
+        }
+        $params = [$kind, $address, $name];
+        //Enqueue addresses with IDN until we know the PHPMailer::$CharSet.
+        //Domain is assumed to be whatever is after the last @ symbol in the address
+        if (static::idnSupported() && $this->has8bitChars(substr($address, ++$pos))) {
+            if ('Reply-To' !== $kind) {
+                if (!array_key_exists($address, $this->RecipientsQueue)) {
+                    $this->RecipientsQueue[$address] = $params;
+
+                    return true;
+                }
+            } elseif (!array_key_exists($address, $this->ReplyToQueue)) {
+                $this->ReplyToQueue[$address] = $params;
+
+                return true;
+            }
+
+            return false;
+        }
+
+        //Immediately add standard addresses without IDN.
+        return call_user_func_array([$this, 'addAnAddress'], $params);
+    }
+
+    /**
+     * Set the boundaries to use for delimiting MIME parts.
+     * If you override this, ensure you set all 3 boundaries to unique values.
+     * The default boundaries include a "=_" sequence which cannot occur in quoted-printable bodies,
+     * as suggested by https://www.rfc-editor.org/rfc/rfc2045#section-6.7
+     *
+     * @return void
+     */
+    public function setBoundaries()
+    {
+        $this->uniqueid = $this->generateId();
+        $this->boundary[1] = 'b1=_' . $this->uniqueid;
+        $this->boundary[2] = 'b2=_' . $this->uniqueid;
+        $this->boundary[3] = 'b3=_' . $this->uniqueid;
+    }
+
+    /**
+     * Add an address to one of the recipient arrays or to the ReplyTo array.
+     * Addresses that have been added already return false, but do not throw exceptions.
+     *
+     * @param string $kind    One of 'to', 'cc', 'bcc', or 'ReplyTo'
+     * @param string $address The email address to send, resp. to reply to
+     * @param string $name
+     *
+     * @throws Exception
+     *
+     * @return bool true on success, false if address already used or invalid in some way
+     */
+    protected function addAnAddress($kind, $address, $name = '')
+    {
+        if (!in_array($kind, ['to', 'cc', 'bcc', 'Reply-To'])) {
+            $error_message = sprintf(
+                '%s: %s',
+                $this->lang('Invalid recipient kind'),
+                $kind
+            );
+            $this->setError($error_message);
+            $this->edebug($error_message);
+            if ($this->exceptions) {
+                throw new Exception($error_message);
+            }
+
+            return false;
+        }
+        if (!static::validateAddress($address)) {
+            $error_message = sprintf(
+                '%s (%s): %s',
+                $this->lang('invalid_address'),
+                $kind,
+                $address
+            );
+            $this->setError($error_message);
+            $this->edebug($error_message);
+            if ($this->exceptions) {
+                throw new Exception($error_message);
+            }
+
+            return false;
+        }
+        if ('Reply-To' !== $kind) {
+            if (!array_key_exists(strtolower($address), $this->all_recipients)) {
+                $this->{$kind}[] = [$address, $name];
+                $this->all_recipients[strtolower($address)] = true;
+
+                return true;
+            }
+        } elseif (!array_key_exists(strtolower($address), $this->ReplyTo)) {
+            $this->ReplyTo[strtolower($address)] = [$address, $name];
+
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Parse and validate a string containing one or more RFC822-style comma-separated email addresses
+     * of the form "display name <address>" into an array of name/address pairs.
+     * Uses the imap_rfc822_parse_adrlist function if the IMAP extension is available.
+     * Note that quotes in the name part are removed.
+     *
+     * @see https://www.andrew.cmu.edu/user/agreen1/testing/mrbs/web/Mail/RFC822.php A more careful implementation
+     *
+     * @param string $addrstr The address list string
+     * @param bool   $useimap Whether to use the IMAP extension to parse the list
+     * @param string $charset The charset to use when decoding the address list string.
+     *
+     * @return array
+     */
+    public static function parseAddresses($addrstr, $useimap = true, $charset = self::CHARSET_ISO88591)
+    {
+        $addresses = [];
+        if ($useimap && function_exists('imap_rfc822_parse_adrlist')) {
+            //Use this built-in parser if it's available
+            $list = imap_rfc822_parse_adrlist($addrstr, '');
+            // Clear any potential IMAP errors to get rid of notices being thrown at end of script.
+            imap_errors();
+            foreach ($list as $address) {
+                if (
+                    '.SYNTAX-ERROR.' !== $address->host &&
+                    static::validateAddress($address->mailbox . '@' . $address->host)
+                ) {
+                    //Decode the name part if it's present and encoded
+                    if (
+                        property_exists($address, 'personal') &&
+                        //Check for a Mbstring constant rather than using extension_loaded, which is sometimes disabled
+                        defined('MB_CASE_UPPER') &&
+                        preg_match('/^=\?.*\?=$/s', $address->personal)
+                    ) {
+                        $origCharset = mb_internal_encoding();
+                        mb_internal_encoding($charset);
+                        //Undo any RFC2047-encoded spaces-as-underscores
+                        $address->personal = str_replace('_', '=20', $address->personal);
+                        //Decode the name
+                        $address->personal = mb_decode_mimeheader($address->personal);
+                        mb_internal_encoding($origCharset);
+                    }
+
+                    $addresses[] = [
+                        'name' => (property_exists($address, 'personal') ? $address->personal : ''),
+                        'address' => $address->mailbox . '@' . $address->host,
+                    ];
+                }
+            }
+        } else {
+            //Use this simpler parser
+            $list = explode(',', $addrstr);
+            foreach ($list as $address) {
+                $address = trim($address);
+                //Is there a separate name part?
+                if (strpos($address, '<') === false) {
+                    //No separate name, just use the whole thing
+                    if (static::validateAddress($address)) {
+                        $addresses[] = [
+                            'name' => '',
+                            'address' => $address,
+                        ];
+                    }
+                } else {
+                    list($name, $email) = explode('<', $address);
+                    $email = trim(str_replace('>', '', $email));
+                    $name = trim($name);
+                    if (static::validateAddress($email)) {
+                        //Check for a Mbstring constant rather than using extension_loaded, which is sometimes disabled
+                        //If this name is encoded, decode it
+                        if (defined('MB_CASE_UPPER') && preg_match('/^=\?.*\?=$/s', $name)) {
+                            $origCharset = mb_internal_encoding();
+                            mb_internal_encoding($charset);
+                            //Undo any RFC2047-encoded spaces-as-underscores
+                            $name = str_replace('_', '=20', $name);
+                            //Decode the name
+                            $name = mb_decode_mimeheader($name);
+                            mb_internal_encoding($origCharset);
+                        }
+                        $addresses[] = [
+                            //Remove any surrounding quotes and spaces from the name
+                            'name' => trim($name, '\'" '),
+                            'address' => $email,
+                        ];
+                    }
+                }
+            }
+        }
+
+        return $addresses;
+    }
+
+    /**
+     * Set the From and FromName properties.
+     *
+     * @param string $address
+     * @param string $name
+     * @param bool   $auto    Whether to also set the Sender address, defaults to true
+     *
+     * @throws Exception
+     *
+     * @return bool
+     */
+    public function setFrom($address, $name = '', $auto = true)
+    {
+        $address = trim((string)$address);
+        $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
+        //Don't validate now addresses with IDN. Will be done in send().
+        $pos = strrpos($address, '@');
+        if (
+            (false === $pos)
+            || ((!$this->has8bitChars(substr($address, ++$pos)) || !static::idnSupported())
+            && !static::validateAddress($address))
+        ) {
+            $error_message = sprintf(
+                '%s (From): %s',
+                $this->lang('invalid_address'),
+                $address
+            );
+            $this->setError($error_message);
+            $this->edebug($error_message);
+            if ($this->exceptions) {
+                throw new Exception($error_message);
+            }
+
+            return false;
+        }
+        $this->From = $address;
+        $this->FromName = $name;
+        if ($auto && empty($this->Sender)) {
+            $this->Sender = $address;
+        }
+
+        return true;
+    }
+
+    /**
+     * Return the Message-ID header of the last email.
+     * Technically this is the value from the last time the headers were created,
+     * but it's also the message ID of the last sent message except in
+     * pathological cases.
+     *
+     * @return string
+     */
+    public function getLastMessageID()
+    {
+        return $this->lastMessageID;
+    }
+
+    /**
+     * Check that a string looks like an email address.
+     * Validation patterns supported:
+     * * `auto` Pick best pattern automatically;
+     * * `pcre8` Use the squiloople.com pattern, requires PCRE > 8.0;
+     * * `pcre` Use old PCRE implementation;
+     * * `php` Use PHP built-in FILTER_VALIDATE_EMAIL;
+     * * `html5` Use the pattern given by the HTML5 spec for 'email' type form input elements.
+     * * `noregex` Don't use a regex: super fast, really dumb.
+     * Alternatively you may pass in a callable to inject your own validator, for example:
+     *
+     * ```php
+     * PHPMailer::validateAddress('user@example.com', function($address) {
+     *     return (strpos($address, '@') !== false);
+     * });
+     * ```
+     *
+     * You can also set the PHPMailer::$validator static to a callable, allowing built-in methods to use your validator.
+     *
+     * @param string          $address       The email address to check
+     * @param string|callable $patternselect Which pattern to use
+     *
+     * @return bool
+     */
+    public static function validateAddress($address, $patternselect = null)
+    {
+        if (null === $patternselect) {
+            $patternselect = static::$validator;
+        }
+        //Don't allow strings as callables, see SECURITY.md and CVE-2021-3603
+        if (is_callable($patternselect) && !is_string($patternselect)) {
+            return call_user_func($patternselect, $address);
+        }
+        //Reject line breaks in addresses; it's valid RFC5322, but not RFC5321
+        if (strpos($address, "\n") !== false || strpos($address, "\r") !== false) {
+            return false;
+        }
+        switch ($patternselect) {
+            case 'pcre': //Kept for BC
+            case 'pcre8':
+                /*
+                 * A more complex and more permissive version of the RFC5322 regex on which FILTER_VALIDATE_EMAIL
+                 * is based.
+                 * In addition to the addresses allowed by filter_var, also permits:
+                 *  * dotless domains: `a@b`
+                 *  * comments: `1234 @ local(blah) .machine .example`
+                 *  * quoted elements: `'"test blah"@example.org'`
+                 *  * numeric TLDs: `a@b.123`
+                 *  * unbracketed IPv4 literals: `a@192.168.0.1`
+                 *  * IPv6 literals: 'first.last@[IPv6:a1::]'
+                 * Not all of these will necessarily work for sending!
+                 *
+                 * @copyright 2009-2010 Michael Rushton
+                 * Feel free to use and redistribute this code. But please keep this copyright notice.
+                 */
+                return (bool) preg_match(
+                    '/^(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){255,})(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){65,}@)' .
+                    '((?>(?>(?>((?>(?>(?>\x0D\x0A)?[\t ])+|(?>[\t ]*\x0D\x0A)?[\t ]+)?)(\((?>(?2)' .
+                    '(?>[\x01-\x08\x0B\x0C\x0E-\'*-\[\]-\x7F]|\\\[\x00-\x7F]|(?3)))*(?2)\)))+(?2))|(?2))?)' .
+                    '([!#-\'*+\/-9=?^-~-]+|"(?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\x7F]))*' .
+                    '(?2)")(?>(?1)\.(?1)(?4))*(?1)@(?!(?1)[a-z0-9-]{64,})(?1)(?>([a-z0-9](?>[a-z0-9-]*[a-z0-9])?)' .
+                    '(?>(?1)\.(?!(?1)[a-z0-9-]{64,})(?1)(?5)){0,126}|\[(?:(?>IPv6:(?>([a-f0-9]{1,4})(?>:(?6)){7}' .
+                    '|(?!(?:.*[a-f0-9][:\]]){8,})((?6)(?>:(?6)){0,6})?::(?7)?))|(?>(?>IPv6:(?>(?6)(?>:(?6)){5}:' .
+                    '|(?!(?:.*[a-f0-9]:){6,})(?8)?::(?>((?6)(?>:(?6)){0,4}):)?))?(25[0-5]|2[0-4][0-9]|1[0-9]{2}' .
+                    '|[1-9]?[0-9])(?>\.(?9)){3}))\])(?1)$/isD',
+                    $address
+                );
+            case 'html5':
+                /*
+                 * This is the pattern used in the HTML5 spec for validation of 'email' type form input elements.
+                 *
+                 * @see https://html.spec.whatwg.org/#e-mail-state-(type=email)
+                 */
+                return (bool) preg_match(
+                    '/^[a-zA-Z0-9.!#$%&\'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}' .
+                    '[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/sD',
+                    $address
+                );
+            case 'php':
+            default:
+                return filter_var($address, FILTER_VALIDATE_EMAIL) !== false;
+        }
+    }
+
+    /**
+     * Tells whether IDNs (Internationalized Domain Names) are supported or not. This requires the
+     * `intl` and `mbstring` PHP extensions.
+     *
+     * @return bool `true` if required functions for IDN support are present
+     */
+    public static function idnSupported()
+    {
+        return function_exists('idn_to_ascii') && function_exists('mb_convert_encoding');
+    }
+
+    /**
+     * Converts IDN in given email address to its ASCII form, also known as punycode, if possible.
+     * Important: Address must be passed in same encoding as currently set in PHPMailer::$CharSet.
+     * This function silently returns unmodified address if:
+     * - No conversion is necessary (i.e. domain name is not an IDN, or is already in ASCII form)
+     * - Conversion to punycode is impossible (e.g. required PHP functions are not available)
+     *   or fails for any reason (e.g. domain contains characters not allowed in an IDN).
+     *
+     * @see PHPMailer::$CharSet
+     *
+     * @param string $address The email address to convert
+     *
+     * @return string The encoded address in ASCII form
+     */
+    public function punyencodeAddress($address)
+    {
+        //Verify we have required functions, CharSet, and at-sign.
+        $pos = strrpos($address, '@');
+        if (
+            !empty($this->CharSet) &&
+            false !== $pos &&
+            static::idnSupported()
+        ) {
+            $domain = substr($address, ++$pos);
+            //Verify CharSet string is a valid one, and domain properly encoded in this CharSet.
+            if ($this->has8bitChars($domain) && @mb_check_encoding($domain, $this->CharSet)) {
+                //Convert the domain from whatever charset it's in to UTF-8
+                $domain = mb_convert_encoding($domain, self::CHARSET_UTF8, $this->CharSet);
+                //Ignore IDE complaints about this line - method signature changed in PHP 5.4
+                $errorcode = 0;
+                if (defined('INTL_IDNA_VARIANT_UTS46')) {
+                    //Use the current punycode standard (appeared in PHP 7.2)
+                    $punycode = idn_to_ascii(
+                        $domain,
+                        \IDNA_DEFAULT | \IDNA_USE_STD3_RULES | \IDNA_CHECK_BIDI |
+                            \IDNA_CHECK_CONTEXTJ | \IDNA_NONTRANSITIONAL_TO_ASCII,
+                        \INTL_IDNA_VARIANT_UTS46
+                    );
+                } elseif (defined('INTL_IDNA_VARIANT_2003')) {
+                    //Fall back to this old, deprecated/removed encoding
+                    $punycode = idn_to_ascii($domain, $errorcode, \INTL_IDNA_VARIANT_2003);
+                } else {
+                    //Fall back to a default we don't know about
+                    $punycode = idn_to_ascii($domain, $errorcode);
+                }
+                if (false !== $punycode) {
+                    return substr($address, 0, $pos) . $punycode;
+                }
+            }
+        }
+
+        return $address;
+    }
+
+    /**
+     * Create a message and send it.
+     * Uses the sending method specified by $Mailer.
+     *
+     * @throws Exception
+     *
+     * @return bool false on error - See the ErrorInfo property for details of the error
+     */
+    public function send()
+    {
+        try {
+            if (!$this->preSend()) {
+                return false;
+            }
+
+            return $this->postSend();
+        } catch (Exception $exc) {
+            $this->mailHeader = '';
+            $this->setError($exc->getMessage());
+            if ($this->exceptions) {
+                throw $exc;
+            }
+
+            return false;
+        }
+    }
+
+    /**
+     * Prepare a message for sending.
+     *
+     * @throws Exception
+     *
+     * @return bool
+     */
+    public function preSend()
+    {
+        if (
+            'smtp' === $this->Mailer
+            || ('mail' === $this->Mailer && (\PHP_VERSION_ID >= 80000 || stripos(PHP_OS, 'WIN') === 0))
+        ) {
+            //SMTP mandates RFC-compliant line endings
+            //and it's also used with mail() on Windows
+            static::setLE(self::CRLF);
+        } else {
+            //Maintain backward compatibility with legacy Linux command line mailers
+            static::setLE(PHP_EOL);
+        }
+        //Check for buggy PHP versions that add a header with an incorrect line break
+        if (
+            'mail' === $this->Mailer
+            && ((\PHP_VERSION_ID >= 70000 && \PHP_VERSION_ID < 70017)
+                || (\PHP_VERSION_ID >= 70100 && \PHP_VERSION_ID < 70103))
+            && ini_get('mail.add_x_header') === '1'
+            && stripos(PHP_OS, 'WIN') === 0
+        ) {
+            trigger_error($this->lang('buggy_php'), E_USER_WARNING);
+        }
+
+        try {
+            $this->error_count = 0; //Reset errors
+            $this->mailHeader = '';
+
+            //Dequeue recipient and Reply-To addresses with IDN
+            foreach (array_merge($this->RecipientsQueue, $this->ReplyToQueue) as $params) {
+                $params[1] = $this->punyencodeAddress($params[1]);
+                call_user_func_array([$this, 'addAnAddress'], $params);
+            }
+            if (count($this->to) + count($this->cc) + count($this->bcc) < 1) {
+                throw new Exception($this->lang('provide_address'), self::STOP_CRITICAL);
+            }
+
+            //Validate From, Sender, and ConfirmReadingTo addresses
+            foreach (['From', 'Sender', 'ConfirmReadingTo'] as $address_kind) {
+                if ($this->{$address_kind} === null) {
+                    $this->{$address_kind} = '';
+                    continue;
+                }
+                $this->{$address_kind} = trim($this->{$address_kind});
+                if (empty($this->{$address_kind})) {
+                    continue;
+                }
+                $this->{$address_kind} = $this->punyencodeAddress($this->{$address_kind});
+                if (!static::validateAddress($this->{$address_kind})) {
+                    $error_message = sprintf(
+                        '%s (%s): %s',
+                        $this->lang('invalid_address'),
+                        $address_kind,
+                        $this->{$address_kind}
+                    );
+                    $this->setError($error_message);
+                    $this->edebug($error_message);
+                    if ($this->exceptions) {
+                        throw new Exception($error_message);
+                    }
+
+                    return false;
+                }
+            }
+
+            //Set whether the message is multipart/alternative
+            if ($this->alternativeExists()) {
+                $this->ContentType = static::CONTENT_TYPE_MULTIPART_ALTERNATIVE;
+            }
+
+            $this->setMessageType();
+            //Refuse to send an empty message unless we are specifically allowing it
+            if (!$this->AllowEmpty && empty($this->Body)) {
+                throw new Exception($this->lang('empty_message'), self::STOP_CRITICAL);
+            }
+
+            //Trim subject consistently
+            $this->Subject = trim($this->Subject);
+            //Create body before headers in case body makes changes to headers (e.g. altering transfer encoding)
+            $this->MIMEHeader = '';
+            $this->MIMEBody = $this->createBody();
+            //createBody may have added some headers, so retain them
+            $tempheaders = $this->MIMEHeader;
+            $this->MIMEHeader = $this->createHeader();
+            $this->MIMEHeader .= $tempheaders;
+
+            //To capture the complete message when using mail(), create
+            //an extra header list which createHeader() doesn't fold in
+            if ('mail' === $this->Mailer) {
+                if (count($this->to) > 0) {
+                    $this->mailHeader .= $this->addrAppend('To', $this->to);
+                } else {
+                    $this->mailHeader .= $this->headerLine('To', 'undisclosed-recipients:;');
+                }
+                $this->mailHeader .= $this->headerLine(
+                    'Subject',
+                    $this->encodeHeader($this->secureHeader($this->Subject))
+                );
+            }
+
+            //Sign with DKIM if enabled
+            if (
+                !empty($this->DKIM_domain)
+                && !empty($this->DKIM_selector)
+                && (!empty($this->DKIM_private_string)
+                    || (!empty($this->DKIM_private)
+                        && static::isPermittedPath($this->DKIM_private)
+                        && file_exists($this->DKIM_private)
+                    )
+                )
+            ) {
+                $header_dkim = $this->DKIM_Add(
+                    $this->MIMEHeader . $this->mailHeader,
+                    $this->encodeHeader($this->secureHeader($this->Subject)),
+                    $this->MIMEBody
+                );
+                $this->MIMEHeader = static::stripTrailingWSP($this->MIMEHeader) . static::$LE .
+                    static::normalizeBreaks($header_dkim) . static::$LE;
+            }
+
+            return true;
+        } catch (Exception $exc) {
+            $this->setError($exc->getMessage());
+            if ($this->exceptions) {
+                throw $exc;
+            }
+
+            return false;
+        }
+    }
+
+    /**
+     * Actually send a message via the selected mechanism.
+     *
+     * @throws Exception
+     *
+     * @return bool
+     */
+    public function postSend()
+    {
+        try {
+            //Choose the mailer and send through it
+            switch ($this->Mailer) {
+                case 'sendmail':
+                case 'qmail':
+                    return $this->sendmailSend($this->MIMEHeader, $this->MIMEBody);
+                case 'smtp':
+                    return $this->smtpSend($this->MIMEHeader, $this->MIMEBody);
+                case 'mail':
+                    return $this->mailSend($this->MIMEHeader, $this->MIMEBody);
+                default:
+                    $sendMethod = $this->Mailer . 'Send';
+                    if (method_exists($this, $sendMethod)) {
+                        return $this->{$sendMethod}($this->MIMEHeader, $this->MIMEBody);
+                    }
+
+                    return $this->mailSend($this->MIMEHeader, $this->MIMEBody);
+            }
+        } catch (Exception $exc) {
+            $this->setError($exc->getMessage());
+            $this->edebug($exc->getMessage());
+            if ($this->Mailer === 'smtp' && $this->SMTPKeepAlive == true && $this->smtp->connected()) {
+                $this->smtp->reset();
+            }
+            if ($this->exceptions) {
+                throw $exc;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Send mail using the $Sendmail program.
+     *
+     * @see PHPMailer::$Sendmail
+     *
+     * @param string $header The message headers
+     * @param string $body   The message body
+     *
+     * @throws Exception
+     *
+     * @return bool
+     */
+    protected function sendmailSend($header, $body)
+    {
+        if ($this->Mailer === 'qmail') {
+            $this->edebug('Sending with qmail');
+        } else {
+            $this->edebug('Sending with sendmail');
+        }
+        $header = static::stripTrailingWSP($header) . static::$LE . static::$LE;
+        //This sets the SMTP envelope sender which gets turned into a return-path header by the receiver
+        //A space after `-f` is optional, but there is a long history of its presence
+        //causing problems, so we don't use one
+        //Exim docs: https://www.exim.org/exim-html-current/doc/html/spec_html/ch-the_exim_command_line.html
+        //Sendmail docs: https://www.sendmail.org/~ca/email/man/sendmail.html
+        //Example problem: https://www.drupal.org/node/1057954
+
+        //PHP 5.6 workaround
+        $sendmail_from_value = ini_get('sendmail_from');
+        if (empty($this->Sender) && !empty($sendmail_from_value)) {
+            //PHP config has a sender address we can use
+            $this->Sender = ini_get('sendmail_from');
+        }
+        //CVE-2016-10033, CVE-2016-10045: Don't pass -f if characters will be escaped.
+        if (!empty($this->Sender) && static::validateAddress($this->Sender) && self::isShellSafe($this->Sender)) {
+            if ($this->Mailer === 'qmail') {
+                $sendmailFmt = '%s -f%s';
+            } else {
+                $sendmailFmt = '%s -oi -f%s -t';
+            }
+        } else {
+            //allow sendmail to choose a default envelope sender. It may
+            //seem preferable to force it to use the From header as with
+            //SMTP, but that introduces new problems (see
+            //<https://github.com/PHPMailer/PHPMailer/issues/2298>), and
+            //it has historically worked this way.
+            $sendmailFmt = '%s -oi -t';
+        }
+
+        $sendmail = sprintf($sendmailFmt, escapeshellcmd($this->Sendmail), $this->Sender);
+        $this->edebug('Sendmail path: ' . $this->Sendmail);
+        $this->edebug('Sendmail command: ' . $sendmail);
+        $this->edebug('Envelope sender: ' . $this->Sender);
+        $this->edebug("Headers: {$header}");
+
+        if ($this->SingleTo) {
+            foreach ($this->SingleToArray as $toAddr) {
+                $mail = @popen($sendmail, 'w');
+                if (!$mail) {
+                    throw new Exception($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
+                }
+                $this->edebug("To: {$toAddr}");
+                fwrite($mail, 'To: ' . $toAddr . "\n");
+                fwrite($mail, $header);
+                fwrite($mail, $body);
+                $result = pclose($mail);
+                $addrinfo = static::parseAddresses($toAddr, true, $this->CharSet);
+                $this->doCallback(
+                    ($result === 0),
+                    [[$addrinfo['address'], $addrinfo['name']]],
+                    $this->cc,
+                    $this->bcc,
+                    $this->Subject,
+                    $body,
+                    $this->From,
+                    []
+                );
+                $this->edebug("Result: " . ($result === 0 ? 'true' : 'false'));
+                if (0 !== $result) {
+                    throw new Exception($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
+                }
+            }
+        } else {
+            $mail = @popen($sendmail, 'w');
+            if (!$mail) {
+                throw new Exception($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
+            }
+            fwrite($mail, $header);
+            fwrite($mail, $body);
+            $result = pclose($mail);
+            $this->doCallback(
+                ($result === 0),
+                $this->to,
+                $this->cc,
+                $this->bcc,
+                $this->Subject,
+                $body,
+                $this->From,
+                []
+            );
+            $this->edebug("Result: " . ($result === 0 ? 'true' : 'false'));
+            if (0 !== $result) {
+                throw new Exception($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Fix CVE-2016-10033 and CVE-2016-10045 by disallowing potentially unsafe shell characters.
+     * Note that escapeshellarg and escapeshellcmd are inadequate for our purposes, especially on Windows.
+     *
+     * @see https://github.com/PHPMailer/PHPMailer/issues/924 CVE-2016-10045 bug report
+     *
+     * @param string $string The string to be validated
+     *
+     * @return bool
+     */
+    protected static function isShellSafe($string)
+    {
+        //It's not possible to use shell commands safely (which includes the mail() function) without escapeshellarg,
+        //but some hosting providers disable it, creating a security problem that we don't want to have to deal with,
+        //so we don't.
+        if (!function_exists('escapeshellarg') || !function_exists('escapeshellcmd')) {
+            return false;
+        }
+
+        if (
+            escapeshellcmd($string) !== $string
+            || !in_array(escapeshellarg($string), ["'$string'", "\"$string\""])
+        ) {
+            return false;
+        }
+
+        $length = strlen($string);
+
+        for ($i = 0; $i < $length; ++$i) {
+            $c = $string[$i];
+
+            //All other characters have a special meaning in at least one common shell, including = and +.
+            //Full stop (.) has a special meaning in cmd.exe, but its impact should be negligible here.
+            //Note that this does permit non-Latin alphanumeric characters based on the current locale.
+            if (!ctype_alnum($c) && strpos('@_-.', $c) === false) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Check whether a file path is of a permitted type.
+     * Used to reject URLs and phar files from functions that access local file paths,
+     * such as addAttachment.
+     *
+     * @param string $path A relative or absolute path to a file
+     *
+     * @return bool
+     */
+    protected static function isPermittedPath($path)
+    {
+        //Matches scheme definition from https://tools.ietf.org/html/rfc3986#section-3.1
+        return !preg_match('#^[a-z][a-z\d+.-]*://#i', $path);
+    }
+
+    /**
+     * Check whether a file path is safe, accessible, and readable.
+     *
+     * @param string $path A relative or absolute path to a file
+     *
+     * @return bool
+     */
+    protected static function fileIsAccessible($path)
+    {
+        if (!static::isPermittedPath($path)) {
+            return false;
+        }
+        $readable = is_file($path);
+        //If not a UNC path (expected to start with \\), check read permission, see #2069
+        if (strpos($path, '\\\\') !== 0) {
+            $readable = $readable && is_readable($path);
+        }
+        return  $readable;
+    }
+
+    /**
+     * Send mail using the PHP mail() function.
+     *
+     * @see https://www.php.net/manual/en/book.mail.php
+     *
+     * @param string $header The message headers
+     * @param string $body   The message body
+     *
+     * @throws Exception
+     *
+     * @return bool
+     */
+    protected function mailSend($header, $body)
+    {
+        $header = static::stripTrailingWSP($header) . static::$LE . static::$LE;
+
+        $toArr = [];
+        foreach ($this->to as $toaddr) {
+            $toArr[] = $this->addrFormat($toaddr);
+        }
+        $to = trim(implode(', ', $toArr));
+
+        //If there are no To-addresses (e.g. when sending only to BCC-addresses)
+        //the following should be added to get a correct DKIM-signature.
+        //Compare with $this->preSend()
+        if ($to === '') {
+            $to = 'undisclosed-recipients:;';
+        }
+
+        $params = null;
+        //This sets the SMTP envelope sender which gets turned into a return-path header by the receiver
+        //A space after `-f` is optional, but there is a long history of its presence
+        //causing problems, so we don't use one
+        //Exim docs: https://www.exim.org/exim-html-current/doc/html/spec_html/ch-the_exim_command_line.html
+        //Sendmail docs: https://www.sendmail.org/~ca/email/man/sendmail.html
+        //Example problem: https://www.drupal.org/node/1057954
+        //CVE-2016-10033, CVE-2016-10045: Don't pass -f if characters will be escaped.
+
+        //PHP 5.6 workaround
+        $sendmail_from_value = ini_get('sendmail_from');
+        if (empty($this->Sender) && !empty($sendmail_from_value)) {
+            //PHP config has a sender address we can use
+            $this->Sender = ini_get('sendmail_from');
+        }
+        if (!empty($this->Sender) && static::validateAddress($this->Sender)) {
+            if (self::isShellSafe($this->Sender)) {
+                $params = sprintf('-f%s', $this->Sender);
+            }
+            $old_from = ini_get('sendmail_from');
+            ini_set('sendmail_from', $this->Sender);
+        }
+        $result = false;
+        if ($this->SingleTo && count($toArr) > 1) {
+            foreach ($toArr as $toAddr) {
+                $result = $this->mailPassthru($toAddr, $this->Subject, $body, $header, $params);
+                $addrinfo = static::parseAddresses($toAddr, true, $this->CharSet);
+                $this->doCallback(
+                    $result,
+                    [[$addrinfo['address'], $addrinfo['name']]],
+                    $this->cc,
+                    $this->bcc,
+                    $this->Subject,
+                    $body,
+                    $this->From,
+                    []
+                );
+            }
+        } else {
+            $result = $this->mailPassthru($to, $this->Subject, $body, $header, $params);
+            $this->doCallback($result, $this->to, $this->cc, $this->bcc, $this->Subject, $body, $this->From, []);
+        }
+        if (isset($old_from)) {
+            ini_set('sendmail_from', $old_from);
+        }
+        if (!$result) {
+            throw new Exception($this->lang('instantiate'), self::STOP_CRITICAL);
+        }
+
+        return true;
+    }
+
+    /**
+     * Get an instance to use for SMTP operations.
+     * Override this function to load your own SMTP implementation,
+     * or set one with setSMTPInstance.
+     *
+     * @return SMTP
+     */
+    public function getSMTPInstance()
+    {
+        if (!is_object($this->smtp)) {
+            $this->smtp = new SMTP();
+        }
+
+        return $this->smtp;
+    }
+
+    /**
+     * Provide an instance to use for SMTP operations.
+     *
+     * @return SMTP
+     */
+    public function setSMTPInstance(SMTP $smtp)
+    {
+        $this->smtp = $smtp;
+
+        return $this->smtp;
+    }
+
+    /**
+     * Provide SMTP XCLIENT attributes
+     *
+     * @param string $name  Attribute name
+     * @param ?string $value Attribute value
+     *
+     * @return bool
+     */
+    public function setSMTPXclientAttribute($name, $value)
+    {
+        if (!in_array($name, SMTP::$xclient_allowed_attributes)) {
+            return false;
+        }
+        if (isset($this->SMTPXClient[$name]) && $value === null) {
+            unset($this->SMTPXClient[$name]);
+        } elseif ($value !== null) {
+            $this->SMTPXClient[$name] = $value;
+        }
+
+        return true;
+    }
+
+    /**
+     * Get SMTP XCLIENT attributes
+     *
+     * @return array
+     */
+    public function getSMTPXclientAttributes()
+    {
+        return $this->SMTPXClient;
+    }
+
+    /**
+     * Send mail via SMTP.
+     * Returns false if there is a bad MAIL FROM, RCPT, or DATA input.
+     *
+     * @see PHPMailer::setSMTPInstance() to use a different class.
+     *
+     * @uses \PHPMailer\PHPMailer\SMTP
+     *
+     * @param string $header The message headers
+     * @param string $body   The message body
+     *
+     * @throws Exception
+     *
+     * @return bool
+     */
+    protected function smtpSend($header, $body)
+    {
+        $header = static::stripTrailingWSP($header) . static::$LE . static::$LE;
+        $bad_rcpt = [];
+        if (!$this->smtpConnect($this->SMTPOptions)) {
+            throw new Exception($this->lang('smtp_connect_failed'), self::STOP_CRITICAL);
+        }
+        //Sender already validated in preSend()
+        if ('' === $this->Sender) {
+            $smtp_from = $this->From;
+        } else {
+            $smtp_from = $this->Sender;
+        }
+        if (count($this->SMTPXClient)) {
+            $this->smtp->xclient($this->SMTPXClient);
+        }
+        if (!$this->smtp->mail($smtp_from)) {
+            $this->setError($this->lang('from_failed') . $smtp_from . ' : ' . implode(',', $this->smtp->getError()));
+            throw new Exception($this->ErrorInfo, self::STOP_CRITICAL);
+        }
+
+        $callbacks = [];
+        //Attempt to send to all recipients
+        foreach ([$this->to, $this->cc, $this->bcc] as $togroup) {
+            foreach ($togroup as $to) {
+                if (!$this->smtp->recipient($to[0], $this->dsn)) {
+                    $error = $this->smtp->getError();
+                    $bad_rcpt[] = ['to' => $to[0], 'error' => $error['detail']];
+                    $isSent = false;
+                } else {
+                    $isSent = true;
+                }
+
+                $callbacks[] = ['issent' => $isSent, 'to' => $to[0], 'name' => $to[1]];
+            }
+        }
+
+        //Only send the DATA command if we have viable recipients
+        if ((count($this->all_recipients) > count($bad_rcpt)) && !$this->smtp->data($header . $body)) {
+            throw new Exception($this->lang('data_not_accepted'), self::STOP_CRITICAL);
+        }
+
+        $smtp_transaction_id = $this->smtp->getLastTransactionID();
+
+        if ($this->SMTPKeepAlive) {
+            $this->smtp->reset();
+        } else {
+            $this->smtp->quit();
+            $this->smtp->close();
+        }
+
+        foreach ($callbacks as $cb) {
+            $this->doCallback(
+                $cb['issent'],
+                [[$cb['to'], $cb['name']]],
+                [],
+                [],
+                $this->Subject,
+                $body,
+                $this->From,
+                ['smtp_transaction_id' => $smtp_transaction_id]
+            );
+        }
+
+        //Create error message for any bad addresses
+        if (count($bad_rcpt) > 0) {
+            $errstr = '';
+            foreach ($bad_rcpt as $bad) {
+                $errstr .= $bad['to'] . ': ' . $bad['error'];
+            }
+            throw new Exception($this->lang('recipients_failed') . $errstr, self::STOP_CONTINUE);
+        }
+
+        return true;
+    }
+
+    /**
+     * Initiate a connection to an SMTP server.
+     * Returns false if the operation failed.
+     *
+     * @param array $options An array of options compatible with stream_context_create()
+     *
+     * @throws Exception
+     *
+     * @uses \PHPMailer\PHPMailer\SMTP
+     *
+     * @return bool
+     */
+    public function smtpConnect($options = null)
+    {
+        if (null === $this->smtp) {
+            $this->smtp = $this->getSMTPInstance();
+        }
+
+        //If no options are provided, use whatever is set in the instance
+        if (null === $options) {
+            $options = $this->SMTPOptions;
+        }
+
+        //Already connected?
+        if ($this->smtp->connected()) {
+            return true;
+        }
+
+        $this->smtp->setTimeout($this->Timeout);
+        $this->smtp->setDebugLevel($this->SMTPDebug);
+        $this->smtp->setDebugOutput($this->Debugoutput);
+        $this->smtp->setVerp($this->do_verp);
+        if ($this->Host === null) {
+            $this->Host = 'localhost';
+        }
+        $hosts = explode(';', $this->Host);
+        $lastexception = null;
+
+        foreach ($hosts as $hostentry) {
+            $hostinfo = [];
+            if (
+                !preg_match(
+                    '/^(?:(ssl|tls):\/\/)?(.+?)(?::(\d+))?$/',
+                    trim($hostentry),
+                    $hostinfo
+                )
+            ) {
+                $this->edebug($this->lang('invalid_hostentry') . ' ' . trim($hostentry));
+                //Not a valid host entry
+                continue;
+            }
+            //$hostinfo[1]: optional ssl or tls prefix
+            //$hostinfo[2]: the hostname
+            //$hostinfo[3]: optional port number
+            //The host string prefix can temporarily override the current setting for SMTPSecure
+            //If it's not specified, the default value is used
+
+            //Check the host name is a valid name or IP address before trying to use it
+            if (!static::isValidHost($hostinfo[2])) {
+                $this->edebug($this->lang('invalid_host') . ' ' . $hostinfo[2]);
+                continue;
+            }
+            $prefix = '';
+            $secure = $this->SMTPSecure;
+            $tls = (static::ENCRYPTION_STARTTLS === $this->SMTPSecure);
+            if ('ssl' === $hostinfo[1] || ('' === $hostinfo[1] && static::ENCRYPTION_SMTPS === $this->SMTPSecure)) {
+                $prefix = 'ssl://';
+                $tls = false; //Can't have SSL and TLS at the same time
+                $secure = static::ENCRYPTION_SMTPS;
+            } elseif ('tls' === $hostinfo[1]) {
+                $tls = true;
+                //TLS doesn't use a prefix
+                $secure = static::ENCRYPTION_STARTTLS;
+            }
+            //Do we need the OpenSSL extension?
+            $sslext = defined('OPENSSL_ALGO_SHA256');
+            if (static::ENCRYPTION_STARTTLS === $secure || static::ENCRYPTION_SMTPS === $secure) {
+                //Check for an OpenSSL constant rather than using extension_loaded, which is sometimes disabled
+                if (!$sslext) {
+                    throw new Exception($this->lang('extension_missing') . 'openssl', self::STOP_CRITICAL);
+                }
+            }
+            $host = $hostinfo[2];
+            $port = $this->Port;
+            if (
+                array_key_exists(3, $hostinfo) &&
+                is_numeric($hostinfo[3]) &&
+                $hostinfo[3] > 0 &&
+                $hostinfo[3] < 65536
+            ) {
+                $port = (int) $hostinfo[3];
+            }
+            if ($this->smtp->connect($prefix . $host, $port, $this->Timeout, $options)) {
+                try {
+                    if ($this->Helo) {
+                        $hello = $this->Helo;
+                    } else {
+                        $hello = $this->serverHostname();
+                    }
+                    $this->smtp->hello($hello);
+                    //Automatically enable TLS encryption if:
+                    //* it's not disabled
+                    //* we are not connecting to localhost
+                    //* we have openssl extension
+                    //* we are not already using SSL
+                    //* the server offers STARTTLS
+                    if (
+                        $this->SMTPAutoTLS &&
+                        $this->Host !== 'localhost' &&
+                        $sslext &&
+                        $secure !== 'ssl' &&
+                        $this->smtp->getServerExt('STARTTLS')
+                    ) {
+                        $tls = true;
+                    }
+                    if ($tls) {
+                        if (!$this->smtp->startTLS()) {
+                            $message = $this->getSmtpErrorMessage('connect_host');
+                            throw new Exception($message);
+                        }
+                        //We must resend EHLO after TLS negotiation
+                        $this->smtp->hello($hello);
+                    }
+                    if (
+                        $this->SMTPAuth && !$this->smtp->authenticate(
+                            $this->Username,
+                            $this->Password,
+                            $this->AuthType,
+                            $this->oauth
+                        )
+                    ) {
+                        throw new Exception($this->lang('authenticate'));
+                    }
+
+                    return true;
+                } catch (Exception $exc) {
+                    $lastexception = $exc;
+                    $this->edebug($exc->getMessage());
+                    //We must have connected, but then failed TLS or Auth, so close connection nicely
+                    $this->smtp->quit();
+                }
+            }
+        }
+        //If we get here, all connection attempts have failed, so close connection hard
+        $this->smtp->close();
+        //As we've caught all exceptions, just report whatever the last one was
+        if ($this->exceptions && null !== $lastexception) {
+            throw $lastexception;
+        }
+        if ($this->exceptions) {
+            // no exception was thrown, likely $this->smtp->connect() failed
+            $message = $this->getSmtpErrorMessage('connect_host');
+            throw new Exception($message);
+        }
+
+        return false;
+    }
+
+    /**
+     * Close the active SMTP session if one exists.
+     */
+    public function smtpClose()
+    {
+        if ((null !== $this->smtp) && $this->smtp->connected()) {
+            $this->smtp->quit();
+            $this->smtp->close();
+        }
+    }
+
+    /**
+     * Set the language for error messages.
+     * The default language is English.
+     *
+     * @param string $langcode  ISO 639-1 2-character language code (e.g. French is "fr")
+     *                          Optionally, the language code can be enhanced with a 4-character
+     *                          script annotation and/or a 2-character country annotation.
+     * @param string $lang_path Path to the language file directory, with trailing separator (slash)
+     *                          Do not set this from user input!
+     *
+     * @return bool Returns true if the requested language was loaded, false otherwise.
+     */
+    public function setLanguage($langcode = 'en', $lang_path = '')
+    {
+        //Backwards compatibility for renamed language codes
+        $renamed_langcodes = [
+            'br' => 'pt_br',
+            'cz' => 'cs',
+            'dk' => 'da',
+            'no' => 'nb',
+            'se' => 'sv',
+            'rs' => 'sr',
+            'tg' => 'tl',
+            'am' => 'hy',
+        ];
+
+        if (array_key_exists($langcode, $renamed_langcodes)) {
+            $langcode = $renamed_langcodes[$langcode];
+        }
+
+        //Define full set of translatable strings in English
+        $PHPMAILER_LANG = [
+            'authenticate' => 'SMTP Error: Could not authenticate.',
+            'buggy_php' => 'Your version of PHP is affected by a bug that may result in corrupted messages.' .
+                ' To fix it, switch to sending using SMTP, disable the mail.add_x_header option in' .
+                ' your php.ini, switch to MacOS or Linux, or upgrade your PHP to version 7.0.17+ or 7.1.3+.',
+            'connect_host' => 'SMTP Error: Could not connect to SMTP host.',
+            'data_not_accepted' => 'SMTP Error: data not accepted.',
+            'empty_message' => 'Message body empty',
+            'encoding' => 'Unknown encoding: ',
+            'execute' => 'Could not execute: ',
+            'extension_missing' => 'Extension missing: ',
+            'file_access' => 'Could not access file: ',
+            'file_open' => 'File Error: Could not open file: ',
+            'from_failed' => 'The following From address failed: ',
+            'instantiate' => 'Could not instantiate mail function.',
+            'invalid_address' => 'Invalid address: ',
+            'invalid_header' => 'Invalid header name or value',
+            'invalid_hostentry' => 'Invalid hostentry: ',
+            'invalid_host' => 'Invalid host: ',
+            'mailer_not_supported' => ' mailer is not supported.',
+            'provide_address' => 'You must provide at least one recipient email address.',
+            'recipients_failed' => 'SMTP Error: The following recipients failed: ',
+            'signing' => 'Signing Error: ',
+            'smtp_code' => 'SMTP code: ',
+            'smtp_code_ex' => 'Additional SMTP info: ',
+            'smtp_connect_failed' => 'SMTP connect() failed.',
+            'smtp_detail' => 'Detail: ',
+            'smtp_error' => 'SMTP server error: ',
+            'variable_set' => 'Cannot set or reset variable: ',
+        ];
+        if (empty($lang_path)) {
+            //Calculate an absolute path so it can work if CWD is not here
+            $lang_path = dirname(__DIR__) . DIRECTORY_SEPARATOR . 'language' . DIRECTORY_SEPARATOR;
+        }
+
+        //Validate $langcode
+        $foundlang = true;
+        $langcode  = strtolower($langcode);
+        if (
+            !preg_match('/^(?P<lang>[a-z]{2})(?P<script>_[a-z]{4})?(?P<country>_[a-z]{2})?$/', $langcode, $matches)
+            && $langcode !== 'en'
+        ) {
+            $foundlang = false;
+            $langcode = 'en';
+        }
+
+        //There is no English translation file
+        if ('en' !== $langcode) {
+            $langcodes = [];
+            if (!empty($matches['script']) && !empty($matches['country'])) {
+                $langcodes[] = $matches['lang'] . $matches['script'] . $matches['country'];
+            }
+            if (!empty($matches['country'])) {
+                $langcodes[] = $matches['lang'] . $matches['country'];
+            }
+            if (!empty($matches['script'])) {
+                $langcodes[] = $matches['lang'] . $matches['script'];
+            }
+            $langcodes[] = $matches['lang'];
+
+            //Try and find a readable language file for the requested language.
+            $foundFile = false;
+            foreach ($langcodes as $code) {
+                $lang_file = $lang_path . 'phpmailer.lang-' . $code . '.php';
+                if (static::fileIsAccessible($lang_file)) {
+                    $foundFile = true;
+                    break;
+                }
+            }
+
+            if ($foundFile === false) {
+                $foundlang = false;
+            } else {
+                $lines = file($lang_file);
+                foreach ($lines as $line) {
+                    //Translation file lines look like this:
+                    //$PHPMAILER_LANG['authenticate'] = 'SMTP-Fehler: Authentifizierung fehlgeschlagen.';
+                    //These files are parsed as text and not PHP so as to avoid the possibility of code injection
+                    //See https://blog.stevenlevithan.com/archives/match-quoted-string
+                    $matches = [];
+                    if (
+                        preg_match(
+                            '/^\$PHPMAILER_LANG\[\'([a-z\d_]+)\'\]\s*=\s*(["\'])(.+)*?\2;/',
+                            $line,
+                            $matches
+                        ) &&
+                        //Ignore unknown translation keys
+                        array_key_exists($matches[1], $PHPMAILER_LANG)
+                    ) {
+                        //Overwrite language-specific strings so we'll never have missing translation keys.
+                        $PHPMAILER_LANG[$matches[1]] = (string)$matches[3];
+                    }
+                }
+            }
+        }
+        $this->language = $PHPMAILER_LANG;
+
+        return $foundlang; //Returns false if language not found
+    }
+
+    /**
+     * Get the array of strings for the current language.
+     *
+     * @return array
+     */
+    public function getTranslations()
+    {
+        if (empty($this->language)) {
+            $this->setLanguage(); // Set the default language.
+        }
+
+        return $this->language;
+    }
+
+    /**
+     * Create recipient headers.
+     *
+     * @param string $type
+     * @param array  $addr An array of recipients,
+     *                     where each recipient is a 2-element indexed array with element 0 containing an address
+     *                     and element 1 containing a name, like:
+     *                     [['joe@example.com', 'Joe User'], ['zoe@example.com', 'Zoe User']]
+     *
+     * @return string
+     */
+    public function addrAppend($type, $addr)
+    {
+        $addresses = [];
+        foreach ($addr as $address) {
+            $addresses[] = $this->addrFormat($address);
+        }
+
+        return $type . ': ' . implode(', ', $addresses) . static::$LE;
+    }
+
+    /**
+     * Format an address for use in a message header.
+     *
+     * @param array $addr A 2-element indexed array, element 0 containing an address, element 1 containing a name like
+     *                    ['joe@example.com', 'Joe User']
+     *
+     * @return string
+     */
+    public function addrFormat($addr)
+    {
+        if (!isset($addr[1]) || ($addr[1] === '')) { //No name provided
+            return $this->secureHeader($addr[0]);
+        }
+
+        return $this->encodeHeader($this->secureHeader($addr[1]), 'phrase') .
+            ' <' . $this->secureHeader($addr[0]) . '>';
+    }
+
+    /**
+     * Word-wrap message.
+     * For use with mailers that do not automatically perform wrapping
+     * and for quoted-printable encoded messages.
+     * Original written by philippe.
+     *
+     * @param string $message The message to wrap
+     * @param int    $length  The line length to wrap to
+     * @param bool   $qp_mode Whether to run in Quoted-Printable mode
+     *
+     * @return string
+     */
+    public function wrapText($message, $length, $qp_mode = false)
+    {
+        if ($qp_mode) {
+            $soft_break = sprintf(' =%s', static::$LE);
+        } else {
+            $soft_break = static::$LE;
+        }
+        //If utf-8 encoding is used, we will need to make sure we don't
+        //split multibyte characters when we wrap
+        $is_utf8 = static::CHARSET_UTF8 === strtolower($this->CharSet);
+        $lelen = strlen(static::$LE);
+        $crlflen = strlen(static::$LE);
+
+        $message = static::normalizeBreaks($message);
+        //Remove a trailing line break
+        if (substr($message, -$lelen) === static::$LE) {
+            $message = substr($message, 0, -$lelen);
+        }
+
+        //Split message into lines
+        $lines = explode(static::$LE, $message);
+        //Message will be rebuilt in here
+        $message = '';
+        foreach ($lines as $line) {
+            $words = explode(' ', $line);
+            $buf = '';
+            $firstword = true;
+            foreach ($words as $word) {
+                if ($qp_mode && (strlen($word) > $length)) {
+                    $space_left = $length - strlen($buf) - $crlflen;
+                    if (!$firstword) {
+                        if ($space_left > 20) {
+                            $len = $space_left;
+                            if ($is_utf8) {
+                                $len = $this->utf8CharBoundary($word, $len);
+                            } elseif ('=' === substr($word, $len - 1, 1)) {
+                                --$len;
+                            } elseif ('=' === substr($word, $len - 2, 1)) {
+                                $len -= 2;
+                            }
+                            $part = substr($word, 0, $len);
+                            $word = substr($word, $len);
+                            $buf .= ' ' . $part;
+                            $message .= $buf . sprintf('=%s', static::$LE);
+                        } else {
+                            $message .= $buf . $soft_break;
+                        }
+                        $buf = '';
+                    }
+                    while ($word !== '') {
+                        if ($length <= 0) {
+                            break;
+                        }
+                        $len = $length;
+                        if ($is_utf8) {
+                            $len = $this->utf8CharBoundary($word, $len);
+                        } elseif ('=' === substr($word, $len - 1, 1)) {
+                            --$len;
+                        } elseif ('=' === substr($word, $len - 2, 1)) {
+                            $len -= 2;
+                        }
+                        $part = substr($word, 0, $len);
+                        $word = (string) substr($word, $len);
+
+                        if ($word !== '') {
+                            $message .= $part . sprintf('=%s', static::$LE);
+                        } else {
+                            $buf = $part;
+                        }
+                    }
+                } else {
+                    $buf_o = $buf;
+                    if (!$firstword) {
+                        $buf .= ' ';
+                    }
+                    $buf .= $word;
+
+                    if ('' !== $buf_o && strlen($buf) > $length) {
+                        $message .= $buf_o . $soft_break;
+                        $buf = $word;
+                    }
+                }
+                $firstword = false;
+            }
+            $message .= $buf . static::$LE;
+        }
+
+        return $message;
+    }
+
+    /**
+     * Find the last character boundary prior to $maxLength in a utf-8
+     * quoted-printable encoded string.
+     * Original written by Colin Brown.
+     *
+     * @param string $encodedText utf-8 QP text
+     * @param int    $maxLength   Find the last character boundary prior to this length
+     *
+     * @return int
+     */
+    public function utf8CharBoundary($encodedText, $maxLength)
+    {
+        $foundSplitPos = false;
+        $lookBack = 3;
+        while (!$foundSplitPos) {
+            $lastChunk = substr($encodedText, $maxLength - $lookBack, $lookBack);
+            $encodedCharPos = strpos($lastChunk, '=');
+            if (false !== $encodedCharPos) {
+                //Found start of encoded character byte within $lookBack block.
+                //Check the encoded byte value (the 2 chars after the '=')
+                $hex = substr($encodedText, $maxLength - $lookBack + $encodedCharPos + 1, 2);
+                $dec = hexdec($hex);
+                if ($dec < 128) {
+                    //Single byte character.
+                    //If the encoded char was found at pos 0, it will fit
+                    //otherwise reduce maxLength to start of the encoded char
+                    if ($encodedCharPos > 0) {
+                        $maxLength -= $lookBack - $encodedCharPos;
+                    }
+                    $foundSplitPos = true;
+                } elseif ($dec >= 192) {
+                    //First byte of a multi byte character
+                    //Reduce maxLength to split at start of character
+                    $maxLength -= $lookBack - $encodedCharPos;
+                    $foundSplitPos = true;
+                } elseif ($dec < 192) {
+                    //Middle byte of a multi byte character, look further back
+                    $lookBack += 3;
+                }
+            } else {
+                //No encoded character found
+                $foundSplitPos = true;
+            }
+        }
+
+        return $maxLength;
+    }
+
+    /**
+     * Apply word wrapping to the message body.
+     * Wraps the message body to the number of chars set in the WordWrap property.
+     * You should only do this to plain-text bodies as wrapping HTML tags may break them.
+     * This is called automatically by createBody(), so you don't need to call it yourself.
+     */
+    public function setWordWrap()
+    {
+        if ($this->WordWrap < 1) {
+            return;
+        }
+
+        switch ($this->message_type) {
+            case 'alt':
+            case 'alt_inline':
+            case 'alt_attach':
+            case 'alt_inline_attach':
+                $this->AltBody = $this->wrapText($this->AltBody, $this->WordWrap);
+                break;
+            default:
+                $this->Body = $this->wrapText($this->Body, $this->WordWrap);
+                break;
+        }
+    }
+
+    /**
+     * Assemble message headers.
+     *
+     * @return string The assembled headers
+     */
+    public function createHeader()
+    {
+        $result = '';
+
+        $result .= $this->headerLine('Date', '' === $this->MessageDate ? self::rfcDate() : $this->MessageDate);
+
+        //The To header is created automatically by mail(), so needs to be omitted here
+        if ('mail' !== $this->Mailer) {
+            if ($this->SingleTo) {
+                foreach ($this->to as $toaddr) {
+                    $this->SingleToArray[] = $this->addrFormat($toaddr);
+                }
+            } elseif (count($this->to) > 0) {
+                $result .= $this->addrAppend('To', $this->to);
+            } elseif (count($this->cc) === 0) {
+                $result .= $this->headerLine('To', 'undisclosed-recipients:;');
+            }
+        }
+        $result .= $this->addrAppend('From', [[trim($this->From), $this->FromName]]);
+
+        //sendmail and mail() extract Cc from the header before sending
+        if (count($this->cc) > 0) {
+            $result .= $this->addrAppend('Cc', $this->cc);
+        }
+
+        //sendmail and mail() extract Bcc from the header before sending
+        if (
+            (
+                'sendmail' === $this->Mailer || 'qmail' === $this->Mailer || 'mail' === $this->Mailer
+            )
+            && count($this->bcc) > 0
+        ) {
+            $result .= $this->addrAppend('Bcc', $this->bcc);
+        }
+
+        if (count($this->ReplyTo) > 0) {
+            $result .= $this->addrAppend('Reply-To', $this->ReplyTo);
+        }
+
+        //mail() sets the subject itself
+        if ('mail' !== $this->Mailer) {
+            $result .= $this->headerLine('Subject', $this->encodeHeader($this->secureHeader($this->Subject)));
+        }
+
+        //Only allow a custom message ID if it conforms to RFC 5322 section 3.6.4
+        //https://tools.ietf.org/html/rfc5322#section-3.6.4
+        if (
+            '' !== $this->MessageID &&
+            preg_match(
+                '/^<((([a-z\d!#$%&\'*+\/=?^_`{|}~-]+(\.[a-z\d!#$%&\'*+\/=?^_`{|}~-]+)*)' .
+                '|("(([\x01-\x08\x0B\x0C\x0E-\x1F\x7F]|[\x21\x23-\x5B\x5D-\x7E])' .
+                '|(\\[\x01-\x09\x0B\x0C\x0E-\x7F]))*"))@(([a-z\d!#$%&\'*+\/=?^_`{|}~-]+' .
+                '(\.[a-z\d!#$%&\'*+\/=?^_`{|}~-]+)*)|(\[(([\x01-\x08\x0B\x0C\x0E-\x1F\x7F]' .
+                '|[\x21-\x5A\x5E-\x7E])|(\\[\x01-\x09\x0B\x0C\x0E-\x7F]))*\])))>$/Di',
+                $this->MessageID
+            )
+        ) {
+            $this->lastMessageID = $this->MessageID;
+        } else {
+            $this->lastMessageID = sprintf('<%s@%s>', $this->uniqueid, $this->serverHostname());
+        }
+        $result .= $this->headerLine('Message-ID', $this->lastMessageID);
+        if (null !== $this->Priority) {
+            $result .= $this->headerLine('X-Priority', $this->Priority);
+        }
+        if ('' === $this->XMailer) {
+            //Empty string for default X-Mailer header
+            $result .= $this->headerLine(
+                'X-Mailer',
+                'PHPMailer ' . self::VERSION . ' (https://github.com/PHPMailer/PHPMailer)'
+            );
+        } elseif (is_string($this->XMailer) && trim($this->XMailer) !== '') {
+            //Some string
+            $result .= $this->headerLine('X-Mailer', trim($this->XMailer));
+        } //Other values result in no X-Mailer header
+
+        if ('' !== $this->ConfirmReadingTo) {
+            $result .= $this->headerLine('Disposition-Notification-To', '<' . $this->ConfirmReadingTo . '>');
+        }
+
+        //Add custom headers
+        foreach ($this->CustomHeader as $header) {
+            $result .= $this->headerLine(
+                trim($header[0]),
+                $this->encodeHeader(trim($header[1]))
+            );
+        }
+        if (!$this->sign_key_file) {
+            $result .= $this->headerLine('MIME-Version', '1.0');
+            $result .= $this->getMailMIME();
+        }
+
+        return $result;
+    }
+
+    /**
+     * Get the message MIME type headers.
+     *
+     * @return string
+     */
+    public function getMailMIME()
+    {
+        $result = '';
+        $ismultipart = true;
+        switch ($this->message_type) {
+            case 'inline':
+                $result .= $this->headerLine('Content-Type', static::CONTENT_TYPE_MULTIPART_RELATED . ';');
+                $result .= $this->textLine(' boundary="' . $this->boundary[1] . '"');
+                break;
+            case 'attach':
+            case 'inline_attach':
+            case 'alt_attach':
+            case 'alt_inline_attach':
+                $result .= $this->headerLine('Content-Type', static::CONTENT_TYPE_MULTIPART_MIXED . ';');
+                $result .= $this->textLine(' boundary="' . $this->boundary[1] . '"');
+                break;
+            case 'alt':
+            case 'alt_inline':
+                $result .= $this->headerLine('Content-Type', static::CONTENT_TYPE_MULTIPART_ALTERNATIVE . ';');
+                $result .= $this->textLine(' boundary="' . $this->boundary[1] . '"');
+                break;
+            default:
+                //Catches case 'plain': and case '':
+                $result .= $this->textLine('Content-Type: ' . $this->ContentType . '; charset=' . $this->CharSet);
+                $ismultipart = false;
+                break;
+        }
+        //RFC1341 part 5 says 7bit is assumed if not specified
+        if (static::ENCODING_7BIT !== $this->Encoding) {
+            //RFC 2045 section 6.4 says multipart MIME parts may only use 7bit, 8bit or binary CTE
+            if ($ismultipart) {
+                if (static::ENCODING_8BIT === $this->Encoding) {
+                    $result .= $this->headerLine('Content-Transfer-Encoding', static::ENCODING_8BIT);
+                }
+                //The only remaining alternatives are quoted-printable and base64, which are both 7bit compatible
+            } else {
+                $result .= $this->headerLine('Content-Transfer-Encoding', $this->Encoding);
+            }
+        }
+
+        return $result;
+    }
+
+    /**
+     * Returns the whole MIME message.
+     * Includes complete headers and body.
+     * Only valid post preSend().
+     *
+     * @see PHPMailer::preSend()
+     *
+     * @return string
+     */
+    public function getSentMIMEMessage()
+    {
+        return static::stripTrailingWSP($this->MIMEHeader . $this->mailHeader) .
+            static::$LE . static::$LE . $this->MIMEBody;
+    }
+
+    /**
+     * Create a unique ID to use for boundaries.
+     *
+     * @return string
+     */
+    protected function generateId()
+    {
+        $len = 32; //32 bytes = 256 bits
+        $bytes = '';
+        if (function_exists('random_bytes')) {
+            try {
+                $bytes = random_bytes($len);
+            } catch (\Exception $e) {
+                //Do nothing
+            }
+        } elseif (function_exists('openssl_random_pseudo_bytes')) {
+            /** @noinspection CryptographicallySecureRandomnessInspection */
+            $bytes = openssl_random_pseudo_bytes($len);
+        }
+        if ($bytes === '') {
+            //We failed to produce a proper random string, so make do.
+            //Use a hash to force the length to the same as the other methods
+            $bytes = hash('sha256', uniqid((string) mt_rand(), true), true);
+        }
+
+        //We don't care about messing up base64 format here, just want a random string
+        return str_replace(['=', '+', '/'], '', base64_encode(hash('sha256', $bytes, true)));
+    }
+
+    /**
+     * Assemble the message body.
+     * Returns an empty string on failure.
+     *
+     * @throws Exception
+     *
+     * @return string The assembled message body
+     */
+    public function createBody()
+    {
+        $body = '';
+        //Create unique IDs and preset boundaries
+        $this->setBoundaries();
+
+        if ($this->sign_key_file) {
+            $body .= $this->getMailMIME() . static::$LE;
+        }
+
+        $this->setWordWrap();
+
+        $bodyEncoding = $this->Encoding;
+        $bodyCharSet = $this->CharSet;
+        //Can we do a 7-bit downgrade?
+        if (static::ENCODING_8BIT === $bodyEncoding && !$this->has8bitChars($this->Body)) {
+            $bodyEncoding = static::ENCODING_7BIT;
+            //All ISO 8859, Windows codepage and UTF-8 charsets are ascii compatible up to 7-bit
+            $bodyCharSet = static::CHARSET_ASCII;
+        }
+        //If lines are too long, and we're not already using an encoding that will shorten them,
+        //change to quoted-printable transfer encoding for the body part only
+        if (static::ENCODING_BASE64 !== $this->Encoding && static::hasLineLongerThanMax($this->Body)) {
+            $bodyEncoding = static::ENCODING_QUOTED_PRINTABLE;
+        }
+
+        $altBodyEncoding = $this->Encoding;
+        $altBodyCharSet = $this->CharSet;
+        //Can we do a 7-bit downgrade?
+        if (static::ENCODING_8BIT === $altBodyEncoding && !$this->has8bitChars($this->AltBody)) {
+            $altBodyEncoding = static::ENCODING_7BIT;
+            //All ISO 8859, Windows codepage and UTF-8 charsets are ascii compatible up to 7-bit
+            $altBodyCharSet = static::CHARSET_ASCII;
+        }
+        //If lines are too long, and we're not already using an encoding that will shorten them,
+        //change to quoted-printable transfer encoding for the alt body part only
+        if (static::ENCODING_BASE64 !== $altBodyEncoding && static::hasLineLongerThanMax($this->AltBody)) {
+            $altBodyEncoding = static::ENCODING_QUOTED_PRINTABLE;
+        }
+        //Use this as a preamble in all multipart message types
+        $mimepre = '';
+        switch ($this->message_type) {
+            case 'inline':
+                $body .= $mimepre;
+                $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding);
+                $body .= $this->encodeString($this->Body, $bodyEncoding);
+                $body .= static::$LE;
+                $body .= $this->attachAll('inline', $this->boundary[1]);
+                break;
+            case 'attach':
+                $body .= $mimepre;
+                $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding);
+                $body .= $this->encodeString($this->Body, $bodyEncoding);
+                $body .= static::$LE;
+                $body .= $this->attachAll('attachment', $this->boundary[1]);
+                break;
+            case 'inline_attach':
+                $body .= $mimepre;
+                $body .= $this->textLine('--' . $this->boundary[1]);
+                $body .= $this->headerLine('Content-Type', static::CONTENT_TYPE_MULTIPART_RELATED . ';');
+                $body .= $this->textLine(' boundary="' . $this->boundary[2] . '";');
+                $body .= $this->textLine(' type="' . static::CONTENT_TYPE_TEXT_HTML . '"');
+                $body .= static::$LE;
+                $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, '', $bodyEncoding);
+                $body .= $this->encodeString($this->Body, $bodyEncoding);
+                $body .= static::$LE;
+                $body .= $this->attachAll('inline', $this->boundary[2]);
+                $body .= static::$LE;
+                $body .= $this->attachAll('attachment', $this->boundary[1]);
+                break;
+            case 'alt':
+                $body .= $mimepre;
+                $body .= $this->getBoundary(
+                    $this->boundary[1],
+                    $altBodyCharSet,
+                    static::CONTENT_TYPE_PLAINTEXT,
+                    $altBodyEncoding
+                );
+                $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
+                $body .= static::$LE;
+                $body .= $this->getBoundary(
+                    $this->boundary[1],
+                    $bodyCharSet,
+                    static::CONTENT_TYPE_TEXT_HTML,
+                    $bodyEncoding
+                );
+                $body .= $this->encodeString($this->Body, $bodyEncoding);
+                $body .= static::$LE;
+                if (!empty($this->Ical)) {
+                    $method = static::ICAL_METHOD_REQUEST;
+                    foreach (static::$IcalMethods as $imethod) {
+                        if (stripos($this->Ical, 'METHOD:' . $imethod) !== false) {
+                            $method = $imethod;
+                            break;
+                        }
+                    }
+                    $body .= $this->getBoundary(
+                        $this->boundary[1],
+                        '',
+                        static::CONTENT_TYPE_TEXT_CALENDAR . '; method=' . $method,
+                        ''
+                    );
+                    $body .= $this->encodeString($this->Ical, $this->Encoding);
+                    $body .= static::$LE;
+                }
+                $body .= $this->endBoundary($this->boundary[1]);
+                break;
+            case 'alt_inline':
+                $body .= $mimepre;
+                $body .= $this->getBoundary(
+                    $this->boundary[1],
+                    $altBodyCharSet,
+                    static::CONTENT_TYPE_PLAINTEXT,
+                    $altBodyEncoding
+                );
+                $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
+                $body .= static::$LE;
+                $body .= $this->textLine('--' . $this->boundary[1]);
+                $body .= $this->headerLine('Content-Type', static::CONTENT_TYPE_MULTIPART_RELATED . ';');
+                $body .= $this->textLine(' boundary="' . $this->boundary[2] . '";');
+                $body .= $this->textLine(' type="' . static::CONTENT_TYPE_TEXT_HTML . '"');
+                $body .= static::$LE;
+                $body .= $this->getBoundary(
+                    $this->boundary[2],
+                    $bodyCharSet,
+                    static::CONTENT_TYPE_TEXT_HTML,
+                    $bodyEncoding
+                );
+                $body .= $this->encodeString($this->Body, $bodyEncoding);
+                $body .= static::$LE;
+                $body .= $this->attachAll('inline', $this->boundary[2]);
+                $body .= static::$LE;
+                $body .= $this->endBoundary($this->boundary[1]);
+                break;
+            case 'alt_attach':
+                $body .= $mimepre;
+                $body .= $this->textLine('--' . $this->boundary[1]);
+                $body .= $this->headerLine('Content-Type', static::CONTENT_TYPE_MULTIPART_ALTERNATIVE . ';');
+                $body .= $this->textLine(' boundary="' . $this->boundary[2] . '"');
+                $body .= static::$LE;
+                $body .= $this->getBoundary(
+                    $this->boundary[2],
+                    $altBodyCharSet,
+                    static::CONTENT_TYPE_PLAINTEXT,
+                    $altBodyEncoding
+                );
+                $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
+                $body .= static::$LE;
+                $body .= $this->getBoundary(
+                    $this->boundary[2],
+                    $bodyCharSet,
+                    static::CONTENT_TYPE_TEXT_HTML,
+                    $bodyEncoding
+                );
+                $body .= $this->encodeString($this->Body, $bodyEncoding);
+                $body .= static::$LE;
+                if (!empty($this->Ical)) {
+                    $method = static::ICAL_METHOD_REQUEST;
+                    foreach (static::$IcalMethods as $imethod) {
+                        if (stripos($this->Ical, 'METHOD:' . $imethod) !== false) {
+                            $method = $imethod;
+                            break;
+                        }
+                    }
+                    $body .= $this->getBoundary(
+                        $this->boundary[2],
+                        '',
+                        static::CONTENT_TYPE_TEXT_CALENDAR . '; method=' . $method,
+                        ''
+                    );
+                    $body .= $this->encodeString($this->Ical, $this->Encoding);
+                }
+                $body .= $this->endBoundary($this->boundary[2]);
+                $body .= static::$LE;
+                $body .= $this->attachAll('attachment', $this->boundary[1]);
+                break;
+            case 'alt_inline_attach':
+                $body .= $mimepre;
+                $body .= $this->textLine('--' . $this->boundary[1]);
+                $body .= $this->headerLine('Content-Type', static::CONTENT_TYPE_MULTIPART_ALTERNATIVE . ';');
+                $body .= $this->textLine(' boundary="' . $this->boundary[2] . '"');
+                $body .= static::$LE;
+                $body .= $this->getBoundary(
+                    $this->boundary[2],
+                    $altBodyCharSet,
+                    static::CONTENT_TYPE_PLAINTEXT,
+                    $altBodyEncoding
+                );
+                $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
+                $body .= static::$LE;
+                $body .= $this->textLine('--' . $this->boundary[2]);
+                $body .= $this->headerLine('Content-Type', static::CONTENT_TYPE_MULTIPART_RELATED . ';');
+                $body .= $this->textLine(' boundary="' . $this->boundary[3] . '";');
+                $body .= $this->textLine(' type="' . static::CONTENT_TYPE_TEXT_HTML . '"');
+                $body .= static::$LE;
+                $body .= $this->getBoundary(
+                    $this->boundary[3],
+                    $bodyCharSet,
+                    static::CONTENT_TYPE_TEXT_HTML,
+                    $bodyEncoding
+                );
+                $body .= $this->encodeString($this->Body, $bodyEncoding);
+                $body .= static::$LE;
+                $body .= $this->attachAll('inline', $this->boundary[3]);
+                $body .= static::$LE;
+                $body .= $this->endBoundary($this->boundary[2]);
+                $body .= static::$LE;
+                $body .= $this->attachAll('attachment', $this->boundary[1]);
+                break;
+            default:
+                //Catch case 'plain' and case '', applies to simple `text/plain` and `text/html` body content types
+                //Reset the `Encoding` property in case we changed it for line length reasons
+                $this->Encoding = $bodyEncoding;
+                $body .= $this->encodeString($this->Body, $this->Encoding);
+                break;
+        }
+
+        if ($this->isError()) {
+            $body = '';
+            if ($this->exceptions) {
+                throw new Exception($this->lang('empty_message'), self::STOP_CRITICAL);
+            }
+        } elseif ($this->sign_key_file) {
+            try {
+                if (!defined('PKCS7_TEXT')) {
+                    throw new Exception($this->lang('extension_missing') . 'openssl');
+                }
+
+                $file = tempnam(sys_get_temp_dir(), 'srcsign');
+                $signed = tempnam(sys_get_temp_dir(), 'mailsign');
+                file_put_contents($file, $body);
+
+                //Workaround for PHP bug https://bugs.php.net/bug.php?id=69197
+                if (empty($this->sign_extracerts_file)) {
+                    $sign = @openssl_pkcs7_sign(
+                        $file,
+                        $signed,
+                        'file://' . realpath($this->sign_cert_file),
+                        ['file://' . realpath($this->sign_key_file), $this->sign_key_pass],
+                        []
+                    );
+                } else {
+                    $sign = @openssl_pkcs7_sign(
+                        $file,
+                        $signed,
+                        'file://' . realpath($this->sign_cert_file),
+                        ['file://' . realpath($this->sign_key_file), $this->sign_key_pass],
+                        [],
+                        PKCS7_DETACHED,
+                        $this->sign_extracerts_file
+                    );
+                }
+
+                @unlink($file);
+                if ($sign) {
+                    $body = file_get_contents($signed);
+                    @unlink($signed);
+                    //The message returned by openssl contains both headers and body, so need to split them up
+                    $parts = explode("\n\n", $body, 2);
+                    $this->MIMEHeader .= $parts[0] . static::$LE . static::$LE;
+                    $body = $parts[1];
+                } else {
+                    @unlink($signed);
+                    throw new Exception($this->lang('signing') . openssl_error_string());
+                }
+            } catch (Exception $exc) {
+                $body = '';
+                if ($this->exceptions) {
+                    throw $exc;
+                }
+            }
+        }
+
+        return $body;
+    }
+
+    /**
+     * Get the boundaries that this message will use
+     * @return array
+     */
+    public function getBoundaries()
+    {
+        if (empty($this->boundary)) {
+            $this->setBoundaries();
+        }
+        return $this->boundary;
+    }
+
+    /**
+     * Return the start of a message boundary.
+     *
+     * @param string $boundary
+     * @param string $charSet
+     * @param string $contentType
+     * @param string $encoding
+     *
+     * @return string
+     */
+    protected function getBoundary($boundary, $charSet, $contentType, $encoding)
+    {
+        $result = '';
+        if ('' === $charSet) {
+            $charSet = $this->CharSet;
+        }
+        if ('' === $contentType) {
+            $contentType = $this->ContentType;
+        }
+        if ('' === $encoding) {
+            $encoding = $this->Encoding;
+        }
+        $result .= $this->textLine('--' . $boundary);
+        $result .= sprintf('Content-Type: %s; charset=%s', $contentType, $charSet);
+        $result .= static::$LE;
+        //RFC1341 part 5 says 7bit is assumed if not specified
+        if (static::ENCODING_7BIT !== $encoding) {
+            $result .= $this->headerLine('Content-Transfer-Encoding', $encoding);
+        }
+        $result .= static::$LE;
+
+        return $result;
+    }
+
+    /**
+     * Return the end of a message boundary.
+     *
+     * @param string $boundary
+     *
+     * @return string
+     */
+    protected function endBoundary($boundary)
+    {
+        return static::$LE . '--' . $boundary . '--' . static::$LE;
+    }
+
+    /**
+     * Set the message type.
+     * PHPMailer only supports some preset message types, not arbitrary MIME structures.
+     */
+    protected function setMessageType()
+    {
+        $type = [];
+        if ($this->alternativeExists()) {
+            $type[] = 'alt';
+        }
+        if ($this->inlineImageExists()) {
+            $type[] = 'inline';
+        }
+        if ($this->attachmentExists()) {
+            $type[] = 'attach';
+        }
+        $this->message_type = implode('_', $type);
+        if ('' === $this->message_type) {
+            //The 'plain' message_type refers to the message having a single body element, not that it is plain-text
+            $this->message_type = 'plain';
+        }
+    }
+
+    /**
+     * Format a header line.
+     *
+     * @param string     $name
+     * @param string|int $value
+     *
+     * @return string
+     */
+    public function headerLine($name, $value)
+    {
+        return $name . ': ' . $value . static::$LE;
+    }
+
+    /**
+     * Return a formatted mail line.
+     *
+     * @param string $value
+     *
+     * @return string
+     */
+    public function textLine($value)
+    {
+        return $value . static::$LE;
+    }
+
+    /**
+     * Add an attachment from a path on the filesystem.
+     * Never use a user-supplied path to a file!
+     * Returns false if the file could not be found or read.
+     * Explicitly *does not* support passing URLs; PHPMailer is not an HTTP client.
+     * If you need to do that, fetch the resource yourself and pass it in via a local file or string.
+     *
+     * @param string $path        Path to the attachment
+     * @param string $name        Overrides the attachment name
+     * @param string $encoding    File encoding (see $Encoding)
+     * @param string $type        MIME type, e.g. `image/jpeg`; determined automatically from $path if not specified
+     * @param string $disposition Disposition to use
+     *
+     * @throws Exception
+     *
+     * @return bool
+     */
+    public function addAttachment(
+        $path,
+        $name = '',
+        $encoding = self::ENCODING_BASE64,
+        $type = '',
+        $disposition = 'attachment'
+    ) {
+        try {
+            if (!static::fileIsAccessible($path)) {
+                throw new Exception($this->lang('file_access') . $path, self::STOP_CONTINUE);
+            }
+
+            //If a MIME type is not specified, try to work it out from the file name
+            if ('' === $type) {
+                $type = static::filenameToType($path);
+            }
+
+            $filename = (string) static::mb_pathinfo($path, PATHINFO_BASENAME);
+            if ('' === $name) {
+                $name = $filename;
+            }
+            if (!$this->validateEncoding($encoding)) {
+                throw new Exception($this->lang('encoding') . $encoding);
+            }
+
+            $this->attachment[] = [
+                0 => $path,
+                1 => $filename,
+                2 => $name,
+                3 => $encoding,
+                4 => $type,
+                5 => false, //isStringAttachment
+                6 => $disposition,
+                7 => $name,
+            ];
+        } catch (Exception $exc) {
+            $this->setError($exc->getMessage());
+            $this->edebug($exc->getMessage());
+            if ($this->exceptions) {
+                throw $exc;
+            }
+
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Return the array of attachments.
+     *
+     * @return array
+     */
+    public function getAttachments()
+    {
+        return $this->attachment;
+    }
+
+    /**
+     * Attach all file, string, and binary attachments to the message.
+     * Returns an empty string on failure.
+     *
+     * @param string $disposition_type
+     * @param string $boundary
+     *
+     * @throws Exception
+     *
+     * @return string
+     */
+    protected function attachAll($disposition_type, $boundary)
+    {
+        //Return text of body
+        $mime = [];
+        $cidUniq = [];
+        $incl = [];
+
+        //Add all attachments
+        foreach ($this->attachment as $attachment) {
+            //Check if it is a valid disposition_filter
+            if ($attachment[6] === $disposition_type) {
+                //Check for string attachment
+                $string = '';
+                $path = '';
+                $bString = $attachment[5];
+                if ($bString) {
+                    $string = $attachment[0];
+                } else {
+                    $path = $attachment[0];
+                }
+
+                $inclhash = hash('sha256', serialize($attachment));
+                if (in_array($inclhash, $incl, true)) {
+                    continue;
+                }
+                $incl[] = $inclhash;
+                $name = $attachment[2];
+                $encoding = $attachment[3];
+                $type = $attachment[4];
+                $disposition = $attachment[6];
+                $cid = $attachment[7];
+                if ('inline' === $disposition && array_key_exists($cid, $cidUniq)) {
+                    continue;
+                }
+                $cidUniq[$cid] = true;
+
+                $mime[] = sprintf('--%s%s', $boundary, static::$LE);
+                //Only include a filename property if we have one
+                if (!empty($name)) {
+                    $mime[] = sprintf(
+                        'Content-Type: %s; name=%s%s',
+                        $type,
+                        static::quotedString($this->encodeHeader($this->secureHeader($name))),
+                        static::$LE
+                    );
+                } else {
+                    $mime[] = sprintf(
+                        'Content-Type: %s%s',
+                        $type,
+                        static::$LE
+                    );
+                }
+                //RFC1341 part 5 says 7bit is assumed if not specified
+                if (static::ENCODING_7BIT !== $encoding) {
+                    $mime[] = sprintf('Content-Transfer-Encoding: %s%s', $encoding, static::$LE);
+                }
+
+                //Only set Content-IDs on inline attachments
+                if ((string) $cid !== '' && $disposition === 'inline') {
+                    $mime[] = 'Content-ID: <' . $this->encodeHeader($this->secureHeader($cid)) . '>' . static::$LE;
+                }
+
+                //Allow for bypassing the Content-Disposition header
+                if (!empty($disposition)) {
+                    $encoded_name = $this->encodeHeader($this->secureHeader($name));
+                    if (!empty($encoded_name)) {
+                        $mime[] = sprintf(
+                            'Content-Disposition: %s; filename=%s%s',
+                            $disposition,
+                            static::quotedString($encoded_name),
+                            static::$LE . static::$LE
+                        );
+                    } else {
+                        $mime[] = sprintf(
+                            'Content-Disposition: %s%s',
+                            $disposition,
+                            static::$LE . static::$LE
+                        );
+                    }
+                } else {
+                    $mime[] = static::$LE;
+                }
+
+                //Encode as string attachment
+                if ($bString) {
+                    $mime[] = $this->encodeString($string, $encoding);
+                } else {
+                    $mime[] = $this->encodeFile($path, $encoding);
+                }
+                if ($this->isError()) {
+                    return '';
+                }
+                $mime[] = static::$LE;
+            }
+        }
+
+        $mime[] = sprintf('--%s--%s', $boundary, static::$LE);
+
+        return implode('', $mime);
+    }
+
+    /**
+     * Encode a file attachment in requested format.
+     * Returns an empty string on failure.
+     *
+     * @param string $path     The full path to the file
+     * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable'
+     *
+     * @return string
+     */
+    protected function encodeFile($path, $encoding = self::ENCODING_BASE64)
+    {
+        try {
+            if (!static::fileIsAccessible($path)) {
+                throw new Exception($this->lang('file_open') . $path, self::STOP_CONTINUE);
+            }
+            $file_buffer = file_get_contents($path);
+            if (false === $file_buffer) {
+                throw new Exception($this->lang('file_open') . $path, self::STOP_CONTINUE);
+            }
+            $file_buffer = $this->encodeString($file_buffer, $encoding);
+
+            return $file_buffer;
+        } catch (Exception $exc) {
+            $this->setError($exc->getMessage());
+            $this->edebug($exc->getMessage());
+            if ($this->exceptions) {
+                throw $exc;
+            }
+
+            return '';
+        }
+    }
+
+    /**
+     * Encode a string in requested format.
+     * Returns an empty string on failure.
+     *
+     * @param string $str      The text to encode
+     * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable'
+     *
+     * @throws Exception
+     *
+     * @return string
+     */
+    public function encodeString($str, $encoding = self::ENCODING_BASE64)
+    {
+        $encoded = '';
+        switch (strtolower($encoding)) {
+            case static::ENCODING_BASE64:
+                $encoded = chunk_split(
+                    base64_encode($str),
+                    static::STD_LINE_LENGTH,
+                    static::$LE
+                );
+                break;
+            case static::ENCODING_7BIT:
+            case static::ENCODING_8BIT:
+                $encoded = static::normalizeBreaks($str);
+                //Make sure it ends with a line break
+                if (substr($encoded, -(strlen(static::$LE))) !== static::$LE) {
+                    $encoded .= static::$LE;
+                }
+                break;
+            case static::ENCODING_BINARY:
+                $encoded = $str;
+                break;
+            case static::ENCODING_QUOTED_PRINTABLE:
+                $encoded = $this->encodeQP($str);
+                break;
+            default:
+                $this->setError($this->lang('encoding') . $encoding);
+                if ($this->exceptions) {
+                    throw new Exception($this->lang('encoding') . $encoding);
+                }
+                break;
+        }
+
+        return $encoded;
+    }
+
+    /**
+     * Encode a header value (not including its label) optimally.
+     * Picks shortest of Q, B, or none. Result includes folding if needed.
+     * See RFC822 definitions for phrase, comment and text positions.
+     *
+     * @param string $str      The header value to encode
+     * @param string $position What context the string will be used in
+     *
+     * @return string
+     */
+    public function encodeHeader($str, $position = 'text')
+    {
+        $matchcount = 0;
+        switch (strtolower($position)) {
+            case 'phrase':
+                if (!preg_match('/[\200-\377]/', $str)) {
+                    //Can't use addslashes as we don't know the value of magic_quotes_sybase
+                    $encoded = addcslashes($str, "\0..\37\177\\\"");
+                    if (($str === $encoded) && !preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str)) {
+                        return $encoded;
+                    }
+
+                    return "\"$encoded\"";
+                }
+                $matchcount = preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches);
+                break;
+            /* @noinspection PhpMissingBreakStatementInspection */
+            case 'comment':
+                $matchcount = preg_match_all('/[()"]/', $str, $matches);
+            //fallthrough
+            case 'text':
+            default:
+                $matchcount += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches);
+                break;
+        }
+
+        if ($this->has8bitChars($str)) {
+            $charset = $this->CharSet;
+        } else {
+            $charset = static::CHARSET_ASCII;
+        }
+
+        //Q/B encoding adds 8 chars and the charset ("` =?<charset>?[QB]?<content>?=`").
+        $overhead = 8 + strlen($charset);
+
+        if ('mail' === $this->Mailer) {
+            $maxlen = static::MAIL_MAX_LINE_LENGTH - $overhead;
+        } else {
+            $maxlen = static::MAX_LINE_LENGTH - $overhead;
+        }
+
+        //Select the encoding that produces the shortest output and/or prevents corruption.
+        if ($matchcount > strlen($str) / 3) {
+            //More than 1/3 of the content needs encoding, use B-encode.
+            $encoding = 'B';
+        } elseif ($matchcount > 0) {
+            //Less than 1/3 of the content needs encoding, use Q-encode.
+            $encoding = 'Q';
+        } elseif (strlen($str) > $maxlen) {
+            //No encoding needed, but value exceeds max line length, use Q-encode to prevent corruption.
+            $encoding = 'Q';
+        } else {
+            //No reformatting needed
+            $encoding = false;
+        }
+
+        switch ($encoding) {
+            case 'B':
+                if ($this->hasMultiBytes($str)) {
+                    //Use a custom function which correctly encodes and wraps long
+                    //multibyte strings without breaking lines within a character
+                    $encoded = $this->base64EncodeWrapMB($str, "\n");
+                } else {
+                    $encoded = base64_encode($str);
+                    $maxlen -= $maxlen % 4;
+                    $encoded = trim(chunk_split($encoded, $maxlen, "\n"));
+                }
+                $encoded = preg_replace('/^(.*)$/m', ' =?' . $charset . "?$encoding?\\1?=", $encoded);
+                break;
+            case 'Q':
+                $encoded = $this->encodeQ($str, $position);
+                $encoded = $this->wrapText($encoded, $maxlen, true);
+                $encoded = str_replace('=' . static::$LE, "\n", trim($encoded));
+                $encoded = preg_replace('/^(.*)$/m', ' =?' . $charset . "?$encoding?\\1?=", $encoded);
+                break;
+            default:
+                return $str;
+        }
+
+        return trim(static::normalizeBreaks($encoded));
+    }
+
+    /**
+     * Check if a string contains multi-byte characters.
+     *
+     * @param string $str multi-byte text to wrap encode
+     *
+     * @return bool
+     */
+    public function hasMultiBytes($str)
+    {
+        if (function_exists('mb_strlen')) {
+            return strlen($str) > mb_strlen($str, $this->CharSet);
+        }
+
+        //Assume no multibytes (we can't handle without mbstring functions anyway)
+        return false;
+    }
+
+    /**
+     * Does a string contain any 8-bit chars (in any charset)?
+     *
+     * @param string $text
+     *
+     * @return bool
+     */
+    public function has8bitChars($text)
+    {
+        return (bool) preg_match('/[\x80-\xFF]/', $text);
+    }
+
+    /**
+     * Encode and wrap long multibyte strings for mail headers
+     * without breaking lines within a character.
+     * Adapted from a function by paravoid.
+     *
+     * @see https://www.php.net/manual/en/function.mb-encode-mimeheader.php#60283
+     *
+     * @param string $str       multi-byte text to wrap encode
+     * @param string $linebreak string to use as linefeed/end-of-line
+     *
+     * @return string
+     */
+    public function base64EncodeWrapMB($str, $linebreak = null)
+    {
+        $start = '=?' . $this->CharSet . '?B?';
+        $end = '?=';
+        $encoded = '';
+        if (null === $linebreak) {
+            $linebreak = static::$LE;
+        }
+
+        $mb_length = mb_strlen($str, $this->CharSet);
+        //Each line must have length <= 75, including $start and $end
+        $length = 75 - strlen($start) - strlen($end);
+        //Average multi-byte ratio
+        $ratio = $mb_length / strlen($str);
+        //Base64 has a 4:3 ratio
+        $avgLength = floor($length * $ratio * .75);
+
+        $offset = 0;
+        for ($i = 0; $i < $mb_length; $i += $offset) {
+            $lookBack = 0;
+            do {
+                $offset = $avgLength - $lookBack;
+                $chunk = mb_substr($str, $i, $offset, $this->CharSet);
+                $chunk = base64_encode($chunk);
+                ++$lookBack;
+            } while (strlen($chunk) > $length);
+            $encoded .= $chunk . $linebreak;
+        }
+
+        //Chomp the last linefeed
+        return substr($encoded, 0, -strlen($linebreak));
+    }
+
+    /**
+     * Encode a string in quoted-printable format.
+     * According to RFC2045 section 6.7.
+     *
+     * @param string $string The text to encode
+     *
+     * @return string
+     */
+    public function encodeQP($string)
+    {
+        return static::normalizeBreaks(quoted_printable_encode($string));
+    }
+
+    /**
+     * Encode a string using Q encoding.
+     *
+     * @see https://www.rfc-editor.org/rfc/rfc2047#section-4.2
+     *
+     * @param string $str      the text to encode
+     * @param string $position Where the text is going to be used, see the RFC for what that means
+     *
+     * @return string
+     */
+    public function encodeQ($str, $position = 'text')
+    {
+        //There should not be any EOL in the string
+        $pattern = '';
+        $encoded = str_replace(["\r", "\n"], '', $str);
+        switch (strtolower($position)) {
+            case 'phrase':
+                //RFC 2047 section 5.3
+                $pattern = '^A-Za-z0-9!*+\/ -';
+                break;
+            /*
+             * RFC 2047 section 5.2.
+             * Build $pattern without including delimiters and []
+             */
+            /* @noinspection PhpMissingBreakStatementInspection */
+            case 'comment':
+                $pattern = '\(\)"';
+            /* Intentional fall through */
+            case 'text':
+            default:
+                //RFC 2047 section 5.1
+                //Replace every high ascii, control, =, ? and _ characters
+                $pattern = '\000-\011\013\014\016-\037\075\077\137\177-\377' . $pattern;
+                break;
+        }
+        $matches = [];
+        if (preg_match_all("/[{$pattern}]/", $encoded, $matches)) {
+            //If the string contains an '=', make sure it's the first thing we replace
+            //so as to avoid double-encoding
+            $eqkey = array_search('=', $matches[0], true);
+            if (false !== $eqkey) {
+                unset($matches[0][$eqkey]);
+                array_unshift($matches[0], '=');
+            }
+            foreach (array_unique($matches[0]) as $char) {
+                $encoded = str_replace($char, '=' . sprintf('%02X', ord($char)), $encoded);
+            }
+        }
+        //Replace spaces with _ (more readable than =20)
+        //RFC 2047 section 4.2(2)
+        return str_replace(' ', '_', $encoded);
+    }
+
+    /**
+     * Add a string or binary attachment (non-filesystem).
+     * This method can be used to attach ascii or binary data,
+     * such as a BLOB record from a database.
+     *
+     * @param string $string      String attachment data
+     * @param string $filename    Name of the attachment
+     * @param string $encoding    File encoding (see $Encoding)
+     * @param string $type        File extension (MIME) type
+     * @param string $disposition Disposition to use
+     *
+     * @throws Exception
+     *
+     * @return bool True on successfully adding an attachment
+     */
+    public function addStringAttachment(
+        $string,
+        $filename,
+        $encoding = self::ENCODING_BASE64,
+        $type = '',
+        $disposition = 'attachment'
+    ) {
+        try {
+            //If a MIME type is not specified, try to work it out from the file name
+            if ('' === $type) {
+                $type = static::filenameToType($filename);
+            }
+
+            if (!$this->validateEncoding($encoding)) {
+                throw new Exception($this->lang('encoding') . $encoding);
+            }
+
+            //Append to $attachment array
+            $this->attachment[] = [
+                0 => $string,
+                1 => $filename,
+                2 => static::mb_pathinfo($filename, PATHINFO_BASENAME),
+                3 => $encoding,
+                4 => $type,
+                5 => true, //isStringAttachment
+                6 => $disposition,
+                7 => 0,
+            ];
+        } catch (Exception $exc) {
+            $this->setError($exc->getMessage());
+            $this->edebug($exc->getMessage());
+            if ($this->exceptions) {
+                throw $exc;
+            }
+
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Add an embedded (inline) attachment from a file.
+     * This can include images, sounds, and just about any other document type.
+     * These differ from 'regular' attachments in that they are intended to be
+     * displayed inline with the message, not just attached for download.
+     * This is used in HTML messages that embed the images
+     * the HTML refers to using the `$cid` value in `img` tags, for example `<img src="cid:mylogo">`.
+     * Never use a user-supplied path to a file!
+     *
+     * @param string $path        Path to the attachment
+     * @param string $cid         Content ID of the attachment; Use this to reference
+     *                            the content when using an embedded image in HTML
+     * @param string $name        Overrides the attachment filename
+     * @param string $encoding    File encoding (see $Encoding) defaults to `base64`
+     * @param string $type        File MIME type (by default mapped from the `$path` filename's extension)
+     * @param string $disposition Disposition to use: `inline` (default) or `attachment`
+     *                            (unlikely you want this – {@see `addAttachment()`} instead)
+     *
+     * @return bool True on successfully adding an attachment
+     * @throws Exception
+     *
+     */
+    public function addEmbeddedImage(
+        $path,
+        $cid,
+        $name = '',
+        $encoding = self::ENCODING_BASE64,
+        $type = '',
+        $disposition = 'inline'
+    ) {
+        try {
+            if (!static::fileIsAccessible($path)) {
+                throw new Exception($this->lang('file_access') . $path, self::STOP_CONTINUE);
+            }
+
+            //If a MIME type is not specified, try to work it out from the file name
+            if ('' === $type) {
+                $type = static::filenameToType($path);
+            }
+
+            if (!$this->validateEncoding($encoding)) {
+                throw new Exception($this->lang('encoding') . $encoding);
+            }
+
+            $filename = (string) static::mb_pathinfo($path, PATHINFO_BASENAME);
+            if ('' === $name) {
+                $name = $filename;
+            }
+
+            //Append to $attachment array
+            $this->attachment[] = [
+                0 => $path,
+                1 => $filename,
+                2 => $name,
+                3 => $encoding,
+                4 => $type,
+                5 => false, //isStringAttachment
+                6 => $disposition,
+                7 => $cid,
+            ];
+        } catch (Exception $exc) {
+            $this->setError($exc->getMessage());
+            $this->edebug($exc->getMessage());
+            if ($this->exceptions) {
+                throw $exc;
+            }
+
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Add an embedded stringified attachment.
+     * This can include images, sounds, and just about any other document type.
+     * If your filename doesn't contain an extension, be sure to set the $type to an appropriate MIME type.
+     *
+     * @param string $string      The attachment binary data
+     * @param string $cid         Content ID of the attachment; Use this to reference
+     *                            the content when using an embedded image in HTML
+     * @param string $name        A filename for the attachment. If this contains an extension,
+     *                            PHPMailer will attempt to set a MIME type for the attachment.
+     *                            For example 'file.jpg' would get an 'image/jpeg' MIME type.
+     * @param string $encoding    File encoding (see $Encoding), defaults to 'base64'
+     * @param string $type        MIME type - will be used in preference to any automatically derived type
+     * @param string $disposition Disposition to use
+     *
+     * @throws Exception
+     *
+     * @return bool True on successfully adding an attachment
+     */
+    public function addStringEmbeddedImage(
+        $string,
+        $cid,
+        $name = '',
+        $encoding = self::ENCODING_BASE64,
+        $type = '',
+        $disposition = 'inline'
+    ) {
+        try {
+            //If a MIME type is not specified, try to work it out from the name
+            if ('' === $type && !empty($name)) {
+                $type = static::filenameToType($name);
+            }
+
+            if (!$this->validateEncoding($encoding)) {
+                throw new Exception($this->lang('encoding') . $encoding);
+            }
+
+            //Append to $attachment array
+            $this->attachment[] = [
+                0 => $string,
+                1 => $name,
+                2 => $name,
+                3 => $encoding,
+                4 => $type,
+                5 => true, //isStringAttachment
+                6 => $disposition,
+                7 => $cid,
+            ];
+        } catch (Exception $exc) {
+            $this->setError($exc->getMessage());
+            $this->edebug($exc->getMessage());
+            if ($this->exceptions) {
+                throw $exc;
+            }
+
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Validate encodings.
+     *
+     * @param string $encoding
+     *
+     * @return bool
+     */
+    protected function validateEncoding($encoding)
+    {
+        return in_array(
+            $encoding,
+            [
+                self::ENCODING_7BIT,
+                self::ENCODING_QUOTED_PRINTABLE,
+                self::ENCODING_BASE64,
+                self::ENCODING_8BIT,
+                self::ENCODING_BINARY,
+            ],
+            true
+        );
+    }
+
+    /**
+     * Check if an embedded attachment is present with this cid.
+     *
+     * @param string $cid
+     *
+     * @return bool
+     */
+    protected function cidExists($cid)
+    {
+        foreach ($this->attachment as $attachment) {
+            if ('inline' === $attachment[6] && $cid === $attachment[7]) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Check if an inline attachment is present.
+     *
+     * @return bool
+     */
+    public function inlineImageExists()
+    {
+        foreach ($this->attachment as $attachment) {
+            if ('inline' === $attachment[6]) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Check if an attachment (non-inline) is present.
+     *
+     * @return bool
+     */
+    public function attachmentExists()
+    {
+        foreach ($this->attachment as $attachment) {
+            if ('attachment' === $attachment[6]) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Check if this message has an alternative body set.
+     *
+     * @return bool
+     */
+    public function alternativeExists()
+    {
+        return !empty($this->AltBody);
+    }
+
+    /**
+     * Clear queued addresses of given kind.
+     *
+     * @param string $kind 'to', 'cc', or 'bcc'
+     */
+    public function clearQueuedAddresses($kind)
+    {
+        $this->RecipientsQueue = array_filter(
+            $this->RecipientsQueue,
+            static function ($params) use ($kind) {
+                return $params[0] !== $kind;
+            }
+        );
+    }
+
+    /**
+     * Clear all To recipients.
+     */
+    public function clearAddresses()
+    {
+        foreach ($this->to as $to) {
+            unset($this->all_recipients[strtolower($to[0])]);
+        }
+        $this->to = [];
+        $this->clearQueuedAddresses('to');
+    }
+
+    /**
+     * Clear all CC recipients.
+     */
+    public function clearCCs()
+    {
+        foreach ($this->cc as $cc) {
+            unset($this->all_recipients[strtolower($cc[0])]);
+        }
+        $this->cc = [];
+        $this->clearQueuedAddresses('cc');
+    }
+
+    /**
+     * Clear all BCC recipients.
+     */
+    public function clearBCCs()
+    {
+        foreach ($this->bcc as $bcc) {
+            unset($this->all_recipients[strtolower($bcc[0])]);
+        }
+        $this->bcc = [];
+        $this->clearQueuedAddresses('bcc');
+    }
+
+    /**
+     * Clear all ReplyTo recipients.
+     */
+    public function clearReplyTos()
+    {
+        $this->ReplyTo = [];
+        $this->ReplyToQueue = [];
+    }
+
+    /**
+     * Clear all recipient types.
+     */
+    public function clearAllRecipients()
+    {
+        $this->to = [];
+        $this->cc = [];
+        $this->bcc = [];
+        $this->all_recipients = [];
+        $this->RecipientsQueue = [];
+    }
+
+    /**
+     * Clear all filesystem, string, and binary attachments.
+     */
+    public function clearAttachments()
+    {
+        $this->attachment = [];
+    }
+
+    /**
+     * Clear all custom headers.
+     */
+    public function clearCustomHeaders()
+    {
+        $this->CustomHeader = [];
+    }
+
+    /**
+     * Clear a specific custom header by name or name and value.
+     * $name value can be overloaded to contain
+     * both header name and value (name:value).
+     *
+     * @param string      $name  Custom header name
+     * @param string|null $value Header value
+     *
+     * @return bool True if a header was replaced successfully
+     */
+    public function clearCustomHeader($name, $value = null)
+    {
+        if (null === $value && strpos($name, ':') !== false) {
+            //Value passed in as name:value
+            list($name, $value) = explode(':', $name, 2);
+        }
+        $name = trim($name);
+        $value = (null === $value) ? null : trim($value);
+
+        foreach ($this->CustomHeader as $k => $pair) {
+            if ($pair[0] == $name) {
+                // We remove the header if the value is not provided or it matches.
+                if (null === $value ||  $pair[1] == $value) {
+                    unset($this->CustomHeader[$k]);
+                }
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Replace a custom header.
+     * $name value can be overloaded to contain
+     * both header name and value (name:value).
+     *
+     * @param string      $name  Custom header name
+     * @param string|null $value Header value
+     *
+     * @return bool True if a header was replaced successfully
+     * @throws Exception
+     */
+    public function replaceCustomHeader($name, $value = null)
+    {
+        if (null === $value && strpos($name, ':') !== false) {
+            //Value passed in as name:value
+            list($name, $value) = explode(':', $name, 2);
+        }
+        $name = trim($name);
+        $value = (null === $value) ? '' : trim($value);
+
+        $replaced = false;
+        foreach ($this->CustomHeader as $k => $pair) {
+            if ($pair[0] == $name) {
+                if ($replaced) {
+                    unset($this->CustomHeader[$k]);
+                    continue;
+                }
+                if (strpbrk($name . $value, "\r\n") !== false) {
+                    if ($this->exceptions) {
+                        throw new Exception($this->lang('invalid_header'));
+                    }
+
+                    return false;
+                }
+                $this->CustomHeader[$k] = [$name, $value];
+                $replaced = true;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Add an error message to the error container.
+     *
+     * @param string $msg
+     */
+    protected function setError($msg)
+    {
+        ++$this->error_count;
+        if ('smtp' === $this->Mailer && null !== $this->smtp) {
+            $lasterror = $this->smtp->getError();
+            if (!empty($lasterror['error'])) {
+                $msg .= $this->lang('smtp_error') . $lasterror['error'];
+                if (!empty($lasterror['detail'])) {
+                    $msg .= ' ' . $this->lang('smtp_detail') . $lasterror['detail'];
+                }
+                if (!empty($lasterror['smtp_code'])) {
+                    $msg .= ' ' . $this->lang('smtp_code') . $lasterror['smtp_code'];
+                }
+                if (!empty($lasterror['smtp_code_ex'])) {
+                    $msg .= ' ' . $this->lang('smtp_code_ex') . $lasterror['smtp_code_ex'];
+                }
+            }
+        }
+        $this->ErrorInfo = $msg;
+    }
+
+    /**
+     * Return an RFC 822 formatted date.
+     *
+     * @return string
+     */
+    public static function rfcDate()
+    {
+        //Set the time zone to whatever the default is to avoid 500 errors
+        //Will default to UTC if it's not set properly in php.ini
+        date_default_timezone_set(@date_default_timezone_get());
+
+        return date('D, j M Y H:i:s O');
+    }
+
+    /**
+     * Get the server hostname.
+     * Returns 'localhost.localdomain' if unknown.
+     *
+     * @return string
+     */
+    protected function serverHostname()
+    {
+        $result = '';
+        if (!empty($this->Hostname)) {
+            $result = $this->Hostname;
+        } elseif (isset($_SERVER) && array_key_exists('SERVER_NAME', $_SERVER)) {
+            $result = $_SERVER['SERVER_NAME'];
+        } elseif (function_exists('gethostname') && gethostname() !== false) {
+            $result = gethostname();
+        } elseif (php_uname('n') !== false) {
+            $result = php_uname('n');
+        }
+        if (!static::isValidHost($result)) {
+            return 'localhost.localdomain';
+        }
+
+        return $result;
+    }
+
+    /**
+     * Validate whether a string contains a valid value to use as a hostname or IP address.
+     * IPv6 addresses must include [], e.g. `[::1]`, not just `::1`.
+     *
+     * @param string $host The host name or IP address to check
+     *
+     * @return bool
+     */
+    public static function isValidHost($host)
+    {
+        //Simple syntax limits
+        if (
+            empty($host)
+            || !is_string($host)
+            || strlen($host) > 256
+            || !preg_match('/^([a-zA-Z\d.-]*|\[[a-fA-F\d:]+\])$/', $host)
+        ) {
+            return false;
+        }
+        //Looks like a bracketed IPv6 address
+        if (strlen($host) > 2 && substr($host, 0, 1) === '[' && substr($host, -1, 1) === ']') {
+            return filter_var(substr($host, 1, -1), FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) !== false;
+        }
+        //If removing all the dots results in a numeric string, it must be an IPv4 address.
+        //Need to check this first because otherwise things like `999.0.0.0` are considered valid host names
+        if (is_numeric(str_replace('.', '', $host))) {
+            //Is it a valid IPv4 address?
+            return filter_var($host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== false;
+        }
+        //Is it a syntactically valid hostname (when embedded in a URL)?
+        return filter_var('https://' . $host, FILTER_VALIDATE_URL) !== false;
+    }
+
+    /**
+     * Get an error message in the current language.
+     *
+     * @param string $key
+     *
+     * @return string
+     */
+    protected function lang($key)
+    {
+        if (count($this->language) < 1) {
+            $this->setLanguage(); //Set the default language
+        }
+
+        if (array_key_exists($key, $this->language)) {
+            if ('smtp_connect_failed' === $key) {
+                //Include a link to troubleshooting docs on SMTP connection failure.
+                //This is by far the biggest cause of support questions
+                //but it's usually not PHPMailer's fault.
+                return $this->language[$key] . ' https://github.com/PHPMailer/PHPMailer/wiki/Troubleshooting';
+            }
+
+            return $this->language[$key];
+        }
+
+        //Return the key as a fallback
+        return $key;
+    }
+
+    /**
+     * Build an error message starting with a generic one and adding details if possible.
+     *
+     * @param string $base_key
+     * @return string
+     */
+    private function getSmtpErrorMessage($base_key)
+    {
+        $message = $this->lang($base_key);
+        $error = $this->smtp->getError();
+        if (!empty($error['error'])) {
+            $message .= ' ' . $error['error'];
+            if (!empty($error['detail'])) {
+                $message .= ' ' . $error['detail'];
+            }
+        }
+
+        return $message;
+    }
+
+    /**
+     * Check if an error occurred.
+     *
+     * @return bool True if an error did occur
+     */
+    public function isError()
+    {
+        return $this->error_count > 0;
+    }
+
+    /**
+     * Add a custom header.
+     * $name value can be overloaded to contain
+     * both header name and value (name:value).
+     *
+     * @param string      $name  Custom header name
+     * @param string|null $value Header value
+     *
+     * @return bool True if a header was set successfully
+     * @throws Exception
+     */
+    public function addCustomHeader($name, $value = null)
+    {
+        if (null === $value && strpos($name, ':') !== false) {
+            //Value passed in as name:value
+            list($name, $value) = explode(':', $name, 2);
+        }
+        $name = trim($name);
+        $value = (null === $value) ? '' : trim($value);
+        //Ensure name is not empty, and that neither name nor value contain line breaks
+        if (empty($name) || strpbrk($name . $value, "\r\n") !== false) {
+            if ($this->exceptions) {
+                throw new Exception($this->lang('invalid_header'));
+            }
+
+            return false;
+        }
+        $this->CustomHeader[] = [$name, $value];
+
+        return true;
+    }
+
+    /**
+     * Returns all custom headers.
+     *
+     * @return array
+     */
+    public function getCustomHeaders()
+    {
+        return $this->CustomHeader;
+    }
+
+    /**
+     * Create a message body from an HTML string.
+     * Automatically inlines images and creates a plain-text version by converting the HTML,
+     * overwriting any existing values in Body and AltBody.
+     * Do not source $message content from user input!
+     * $basedir is prepended when handling relative URLs, e.g. <img src="/images/a.png"> and must not be empty
+     * will look for an image file in $basedir/images/a.png and convert it to inline.
+     * If you don't provide a $basedir, relative paths will be left untouched (and thus probably break in email)
+     * Converts data-uri images into embedded attachments.
+     * If you don't want to apply these transformations to your HTML, just set Body and AltBody directly.
+     *
+     * @param string        $message  HTML message string
+     * @param string        $basedir  Absolute path to a base directory to prepend to relative paths to images
+     * @param bool|callable $advanced Whether to use the internal HTML to text converter
+     *                                or your own custom converter
+     * @return string The transformed message body
+     *
+     * @throws Exception
+     *
+     * @see PHPMailer::html2text()
+     */
+    public function msgHTML($message, $basedir = '', $advanced = false)
+    {
+        preg_match_all('/(?<!-)(src|background)=["\'](.*)["\']/Ui', $message, $images);
+        if (array_key_exists(2, $images)) {
+            if (strlen($basedir) > 1 && '/' !== substr($basedir, -1)) {
+                //Ensure $basedir has a trailing /
+                $basedir .= '/';
+            }
+            foreach ($images[2] as $imgindex => $url) {
+                //Convert data URIs into embedded images
+                //e.g. "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="
+                $match = [];
+                if (preg_match('#^data:(image/(?:jpe?g|gif|png));?(base64)?,(.+)#', $url, $match)) {
+                    if (count($match) === 4 && static::ENCODING_BASE64 === $match[2]) {
+                        $data = base64_decode($match[3]);
+                    } elseif ('' === $match[2]) {
+                        $data = rawurldecode($match[3]);
+                    } else {
+                        //Not recognised so leave it alone
+                        continue;
+                    }
+                    //Hash the decoded data, not the URL, so that the same data-URI image used in multiple places
+                    //will only be embedded once, even if it used a different encoding
+                    $cid = substr(hash('sha256', $data), 0, 32) . '@phpmailer.0'; //RFC2392 S 2
+
+                    if (!$this->cidExists($cid)) {
+                        $this->addStringEmbeddedImage(
+                            $data,
+                            $cid,
+                            'embed' . $imgindex,
+                            static::ENCODING_BASE64,
+                            $match[1]
+                        );
+                    }
+                    $message = str_replace(
+                        $images[0][$imgindex],
+                        $images[1][$imgindex] . '="cid:' . $cid . '"',
+                        $message
+                    );
+                    continue;
+                }
+                if (
+                    //Only process relative URLs if a basedir is provided (i.e. no absolute local paths)
+                    !empty($basedir)
+                    //Ignore URLs containing parent dir traversal (..)
+                    && (strpos($url, '..') === false)
+                    //Do not change urls that are already inline images
+                    && 0 !== strpos($url, 'cid:')
+                    //Do not change absolute URLs, including anonymous protocol
+                    && !preg_match('#^[a-z][a-z0-9+.-]*:?//#i', $url)
+                ) {
+                    $filename = static::mb_pathinfo($url, PATHINFO_BASENAME);
+                    $directory = dirname($url);
+                    if ('.' === $directory) {
+                        $directory = '';
+                    }
+                    //RFC2392 S 2
+                    $cid = substr(hash('sha256', $url), 0, 32) . '@phpmailer.0';
+                    if (strlen($basedir) > 1 && '/' !== substr($basedir, -1)) {
+                        $basedir .= '/';
+                    }
+                    if (strlen($directory) > 1 && '/' !== substr($directory, -1)) {
+                        $directory .= '/';
+                    }
+                    if (
+                        $this->addEmbeddedImage(
+                            $basedir . $directory . $filename,
+                            $cid,
+                            $filename,
+                            static::ENCODING_BASE64,
+                            static::_mime_types((string) static::mb_pathinfo($filename, PATHINFO_EXTENSION))
+                        )
+                    ) {
+                        $message = preg_replace(
+                            '/' . $images[1][$imgindex] . '=["\']' . preg_quote($url, '/') . '["\']/Ui',
+                            $images[1][$imgindex] . '="cid:' . $cid . '"',
+                            $message
+                        );
+                    }
+                }
+            }
+        }
+        $this->isHTML();
+        //Convert all message body line breaks to LE, makes quoted-printable encoding work much better
+        $this->Body = static::normalizeBreaks($message);
+        $this->AltBody = static::normalizeBreaks($this->html2text($message, $advanced));
+        if (!$this->alternativeExists()) {
+            $this->AltBody = 'This is an HTML-only message. To view it, activate HTML in your email application.'
+                . static::$LE;
+        }
+
+        return $this->Body;
+    }
+
+    /**
+     * Convert an HTML string into plain text.
+     * This is used by msgHTML().
+     * Note - older versions of this function used a bundled advanced converter
+     * which was removed for license reasons in #232.
+     * Example usage:
+     *
+     * ```php
+     * //Use default conversion
+     * $plain = $mail->html2text($html);
+     * //Use your own custom converter
+     * $plain = $mail->html2text($html, function($html) {
+     *     $converter = new MyHtml2text($html);
+     *     return $converter->get_text();
+     * });
+     * ```
+     *
+     * @param string        $html     The HTML text to convert
+     * @param bool|callable $advanced Any boolean value to use the internal converter,
+     *                                or provide your own callable for custom conversion.
+     *                                *Never* pass user-supplied data into this parameter
+     *
+     * @return string
+     */
+    public function html2text($html, $advanced = false)
+    {
+        if (is_callable($advanced)) {
+            return call_user_func($advanced, $html);
+        }
+
+        return html_entity_decode(
+            trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/si', '', $html))),
+            ENT_QUOTES,
+            $this->CharSet
+        );
+    }
+
+    /**
+     * Get the MIME type for a file extension.
+     *
+     * @param string $ext File extension
+     *
+     * @return string MIME type of file
+     */
+    public static function _mime_types($ext = '')
+    {
+        $mimes = [
+            'xl' => 'application/excel',
+            'js' => 'application/javascript',
+            'hqx' => 'application/mac-binhex40',
+            'cpt' => 'application/mac-compactpro',
+            'bin' => 'application/macbinary',
+            'doc' => 'application/msword',
+            'word' => 'application/msword',
+            'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
+            'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
+            'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template',
+            'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
+            'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
+            'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide',
+            'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
+            'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
+            'xlam' => 'application/vnd.ms-excel.addin.macroEnabled.12',
+            'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
+            'class' => 'application/octet-stream',
+            'dll' => 'application/octet-stream',
+            'dms' => 'application/octet-stream',
+            'exe' => 'application/octet-stream',
+            'lha' => 'application/octet-stream',
+            'lzh' => 'application/octet-stream',
+            'psd' => 'application/octet-stream',
+            'sea' => 'application/octet-stream',
+            'so' => 'application/octet-stream',
+            'oda' => 'application/oda',
+            'pdf' => 'application/pdf',
+            'ai' => 'application/postscript',
+            'eps' => 'application/postscript',
+            'ps' => 'application/postscript',
+            'smi' => 'application/smil',
+            'smil' => 'application/smil',
+            'mif' => 'application/vnd.mif',
+            'xls' => 'application/vnd.ms-excel',
+            'ppt' => 'application/vnd.ms-powerpoint',
+            'wbxml' => 'application/vnd.wap.wbxml',
+            'wmlc' => 'application/vnd.wap.wmlc',
+            'dcr' => 'application/x-director',
+            'dir' => 'application/x-director',
+            'dxr' => 'application/x-director',
+            'dvi' => 'application/x-dvi',
+            'gtar' => 'application/x-gtar',
+            'php3' => 'application/x-httpd-php',
+            'php4' => 'application/x-httpd-php',
+            'php' => 'application/x-httpd-php',
+            'phtml' => 'application/x-httpd-php',
+            'phps' => 'application/x-httpd-php-source',
+            'swf' => 'application/x-shockwave-flash',
+            'sit' => 'application/x-stuffit',
+            'tar' => 'application/x-tar',
+            'tgz' => 'application/x-tar',
+            'xht' => 'application/xhtml+xml',
+            'xhtml' => 'application/xhtml+xml',
+            'zip' => 'application/zip',
+            'mid' => 'audio/midi',
+            'midi' => 'audio/midi',
+            'mp2' => 'audio/mpeg',
+            'mp3' => 'audio/mpeg',
+            'm4a' => 'audio/mp4',
+            'mpga' => 'audio/mpeg',
+            'aif' => 'audio/x-aiff',
+            'aifc' => 'audio/x-aiff',
+            'aiff' => 'audio/x-aiff',
+            'ram' => 'audio/x-pn-realaudio',
+            'rm' => 'audio/x-pn-realaudio',
+            'rpm' => 'audio/x-pn-realaudio-plugin',
+            'ra' => 'audio/x-realaudio',
+            'wav' => 'audio/x-wav',
+            'mka' => 'audio/x-matroska',
+            'bmp' => 'image/bmp',
+            'gif' => 'image/gif',
+            'jpeg' => 'image/jpeg',
+            'jpe' => 'image/jpeg',
+            'jpg' => 'image/jpeg',
+            'png' => 'image/png',
+            'tiff' => 'image/tiff',
+            'tif' => 'image/tiff',
+            'webp' => 'image/webp',
+            'avif' => 'image/avif',
+            'heif' => 'image/heif',
+            'heifs' => 'image/heif-sequence',
+            'heic' => 'image/heic',
+            'heics' => 'image/heic-sequence',
+            'eml' => 'message/rfc822',
+            'css' => 'text/css',
+            'html' => 'text/html',
+            'htm' => 'text/html',
+            'shtml' => 'text/html',
+            'log' => 'text/plain',
+            'text' => 'text/plain',
+            'txt' => 'text/plain',
+            'rtx' => 'text/richtext',
+            'rtf' => 'text/rtf',
+            'vcf' => 'text/vcard',
+            'vcard' => 'text/vcard',
+            'ics' => 'text/calendar',
+            'xml' => 'text/xml',
+            'xsl' => 'text/xml',
+            'csv' => 'text/csv',
+            'wmv' => 'video/x-ms-wmv',
+            'mpeg' => 'video/mpeg',
+            'mpe' => 'video/mpeg',
+            'mpg' => 'video/mpeg',
+            'mp4' => 'video/mp4',
+            'm4v' => 'video/mp4',
+            'mov' => 'video/quicktime',
+            'qt' => 'video/quicktime',
+            'rv' => 'video/vnd.rn-realvideo',
+            'avi' => 'video/x-msvideo',
+            'movie' => 'video/x-sgi-movie',
+            'webm' => 'video/webm',
+            'mkv' => 'video/x-matroska',
+        ];
+        $ext = strtolower($ext);
+        if (array_key_exists($ext, $mimes)) {
+            return $mimes[$ext];
+        }
+
+        return 'application/octet-stream';
+    }
+
+    /**
+     * Map a file name to a MIME type.
+     * Defaults to 'application/octet-stream', i.e.. arbitrary binary data.
+     *
+     * @param string $filename A file name or full path, does not need to exist as a file
+     *
+     * @return string
+     */
+    public static function filenameToType($filename)
+    {
+        //In case the path is a URL, strip any query string before getting extension
+        $qpos = strpos($filename, '?');
+        if (false !== $qpos) {
+            $filename = substr($filename, 0, $qpos);
+        }
+        $ext = static::mb_pathinfo($filename, PATHINFO_EXTENSION);
+
+        return static::_mime_types($ext);
+    }
+
+    /**
+     * Multi-byte-safe pathinfo replacement.
+     * Drop-in replacement for pathinfo(), but multibyte- and cross-platform-safe.
+     *
+     * @see https://www.php.net/manual/en/function.pathinfo.php#107461
+     *
+     * @param string     $path    A filename or path, does not need to exist as a file
+     * @param int|string $options Either a PATHINFO_* constant,
+     *                            or a string name to return only the specified piece
+     *
+     * @return string|array
+     */
+    public static function mb_pathinfo($path, $options = null)
+    {
+        $ret = ['dirname' => '', 'basename' => '', 'extension' => '', 'filename' => ''];
+        $pathinfo = [];
+        if (preg_match('#^(.*?)[\\\\/]*(([^/\\\\]*?)(\.([^.\\\\/]+?)|))[\\\\/.]*$#m', $path, $pathinfo)) {
+            if (array_key_exists(1, $pathinfo)) {
+                $ret['dirname'] = $pathinfo[1];
+            }
+            if (array_key_exists(2, $pathinfo)) {
+                $ret['basename'] = $pathinfo[2];
+            }
+            if (array_key_exists(5, $pathinfo)) {
+                $ret['extension'] = $pathinfo[5];
+            }
+            if (array_key_exists(3, $pathinfo)) {
+                $ret['filename'] = $pathinfo[3];
+            }
+        }
+        switch ($options) {
+            case PATHINFO_DIRNAME:
+            case 'dirname':
+                return $ret['dirname'];
+            case PATHINFO_BASENAME:
+            case 'basename':
+                return $ret['basename'];
+            case PATHINFO_EXTENSION:
+            case 'extension':
+                return $ret['extension'];
+            case PATHINFO_FILENAME:
+            case 'filename':
+                return $ret['filename'];
+            default:
+                return $ret;
+        }
+    }
+
+    /**
+     * Set or reset instance properties.
+     * You should avoid this function - it's more verbose, less efficient, more error-prone and
+     * harder to debug than setting properties directly.
+     * Usage Example:
+     * `$mail->set('SMTPSecure', static::ENCRYPTION_STARTTLS);`
+     *   is the same as:
+     * `$mail->SMTPSecure = static::ENCRYPTION_STARTTLS;`.
+     *
+     * @param string $name  The property name to set
+     * @param mixed  $value The value to set the property to
+     *
+     * @return bool
+     */
+    public function set($name, $value = '')
+    {
+        if (property_exists($this, $name)) {
+            $this->{$name} = $value;
+
+            return true;
+        }
+        $this->setError($this->lang('variable_set') . $name);
+
+        return false;
+    }
+
+    /**
+     * Strip newlines to prevent header injection.
+     *
+     * @param string $str
+     *
+     * @return string
+     */
+    public function secureHeader($str)
+    {
+        return trim(str_replace(["\r", "\n"], '', $str));
+    }
+
+    /**
+     * Normalize line breaks in a string.
+     * Converts UNIX LF, Mac CR and Windows CRLF line breaks into a single line break format.
+     * Defaults to CRLF (for message bodies) and preserves consecutive breaks.
+     *
+     * @param string $text
+     * @param string $breaktype What kind of line break to use; defaults to static::$LE
+     *
+     * @return string
+     */
+    public static function normalizeBreaks($text, $breaktype = null)
+    {
+        if (null === $breaktype) {
+            $breaktype = static::$LE;
+        }
+        //Normalise to \n
+        $text = str_replace([self::CRLF, "\r"], "\n", $text);
+        //Now convert LE as needed
+        if ("\n" !== $breaktype) {
+            $text = str_replace("\n", $breaktype, $text);
+        }
+
+        return $text;
+    }
+
+    /**
+     * Remove trailing whitespace from a string.
+     *
+     * @param string $text
+     *
+     * @return string The text to remove whitespace from
+     */
+    public static function stripTrailingWSP($text)
+    {
+        return rtrim($text, " \r\n\t");
+    }
+
+    /**
+     * Strip trailing line breaks from a string.
+     *
+     * @param string $text
+     *
+     * @return string The text to remove breaks from
+     */
+    public static function stripTrailingBreaks($text)
+    {
+        return rtrim($text, "\r\n");
+    }
+
+    /**
+     * Return the current line break format string.
+     *
+     * @return string
+     */
+    public static function getLE()
+    {
+        return static::$LE;
+    }
+
+    /**
+     * Set the line break format string, e.g. "\r\n".
+     *
+     * @param string $le
+     */
+    protected static function setLE($le)
+    {
+        static::$LE = $le;
+    }
+
+    /**
+     * Set the public and private key files and password for S/MIME signing.
+     *
+     * @param string $cert_filename
+     * @param string $key_filename
+     * @param string $key_pass            Password for private key
+     * @param string $extracerts_filename Optional path to chain certificate
+     */
+    public function sign($cert_filename, $key_filename, $key_pass, $extracerts_filename = '')
+    {
+        $this->sign_cert_file = $cert_filename;
+        $this->sign_key_file = $key_filename;
+        $this->sign_key_pass = $key_pass;
+        $this->sign_extracerts_file = $extracerts_filename;
+    }
+
+    /**
+     * Quoted-Printable-encode a DKIM header.
+     *
+     * @param string $txt
+     *
+     * @return string
+     */
+    public function DKIM_QP($txt)
+    {
+        $line = '';
+        $len = strlen($txt);
+        for ($i = 0; $i < $len; ++$i) {
+            $ord = ord($txt[$i]);
+            if (((0x21 <= $ord) && ($ord <= 0x3A)) || $ord === 0x3C || ((0x3E <= $ord) && ($ord <= 0x7E))) {
+                $line .= $txt[$i];
+            } else {
+                $line .= '=' . sprintf('%02X', $ord);
+            }
+        }
+
+        return $line;
+    }
+
+    /**
+     * Generate a DKIM signature.
+     *
+     * @param string $signHeader
+     *
+     * @throws Exception
+     *
+     * @return string The DKIM signature value
+     */
+    public function DKIM_Sign($signHeader)
+    {
+        if (!defined('PKCS7_TEXT')) {
+            if ($this->exceptions) {
+                throw new Exception($this->lang('extension_missing') . 'openssl');
+            }
+
+            return '';
+        }
+        $privKeyStr = !empty($this->DKIM_private_string) ?
+            $this->DKIM_private_string :
+            file_get_contents($this->DKIM_private);
+        if ('' !== $this->DKIM_passphrase) {
+            $privKey = openssl_pkey_get_private($privKeyStr, $this->DKIM_passphrase);
+        } else {
+            $privKey = openssl_pkey_get_private($privKeyStr);
+        }
+        if (openssl_sign($signHeader, $signature, $privKey, 'sha256WithRSAEncryption')) {
+            if (\PHP_MAJOR_VERSION < 8) {
+                openssl_pkey_free($privKey);
+            }
+
+            return base64_encode($signature);
+        }
+        if (\PHP_MAJOR_VERSION < 8) {
+            openssl_pkey_free($privKey);
+        }
+
+        return '';
+    }
+
+    /**
+     * Generate a DKIM canonicalization header.
+     * Uses the 'relaxed' algorithm from RFC6376 section 3.4.2.
+     * Canonicalized headers should *always* use CRLF, regardless of mailer setting.
+     *
+     * @see https://tools.ietf.org/html/rfc6376#section-3.4.2
+     *
+     * @param string $signHeader Header
+     *
+     * @return string
+     */
+    public function DKIM_HeaderC($signHeader)
+    {
+        //Normalize breaks to CRLF (regardless of the mailer)
+        $signHeader = static::normalizeBreaks($signHeader, self::CRLF);
+        //Unfold header lines
+        //Note PCRE \s is too broad a definition of whitespace; RFC5322 defines it as `[ \t]`
+        //@see https://tools.ietf.org/html/rfc5322#section-2.2
+        //That means this may break if you do something daft like put vertical tabs in your headers.
+        $signHeader = preg_replace('/\r\n[ \t]+/', ' ', $signHeader);
+        //Break headers out into an array
+        $lines = explode(self::CRLF, $signHeader);
+        foreach ($lines as $key => $line) {
+            //If the header is missing a :, skip it as it's invalid
+            //This is likely to happen because the explode() above will also split
+            //on the trailing LE, leaving an empty line
+            if (strpos($line, ':') === false) {
+                continue;
+            }
+            list($heading, $value) = explode(':', $line, 2);
+            //Lower-case header name
+            $heading = strtolower($heading);
+            //Collapse white space within the value, also convert WSP to space
+            $value = preg_replace('/[ \t]+/', ' ', $value);
+            //RFC6376 is slightly unclear here - it says to delete space at the *end* of each value
+            //But then says to delete space before and after the colon.
+            //Net result is the same as trimming both ends of the value.
+            //By elimination, the same applies to the field name
+            $lines[$key] = trim($heading, " \t") . ':' . trim($value, " \t");
+        }
+
+        return implode(self::CRLF, $lines);
+    }
+
+    /**
+     * Generate a DKIM canonicalization body.
+     * Uses the 'simple' algorithm from RFC6376 section 3.4.3.
+     * Canonicalized bodies should *always* use CRLF, regardless of mailer setting.
+     *
+     * @see https://tools.ietf.org/html/rfc6376#section-3.4.3
+     *
+     * @param string $body Message Body
+     *
+     * @return string
+     */
+    public function DKIM_BodyC($body)
+    {
+        if (empty($body)) {
+            return self::CRLF;
+        }
+        //Normalize line endings to CRLF
+        $body = static::normalizeBreaks($body, self::CRLF);
+
+        //Reduce multiple trailing line breaks to a single one
+        return static::stripTrailingBreaks($body) . self::CRLF;
+    }
+
+    /**
+     * Create the DKIM header and body in a new message header.
+     *
+     * @param string $headers_line Header lines
+     * @param string $subject      Subject
+     * @param string $body         Body
+     *
+     * @throws Exception
+     *
+     * @return string
+     */
+    public function DKIM_Add($headers_line, $subject, $body)
+    {
+        $DKIMsignatureType = 'rsa-sha256'; //Signature & hash algorithms
+        $DKIMcanonicalization = 'relaxed/simple'; //Canonicalization methods of header & body
+        $DKIMquery = 'dns/txt'; //Query method
+        $DKIMtime = time();
+        //Always sign these headers without being asked
+        //Recommended list from https://tools.ietf.org/html/rfc6376#section-5.4.1
+        $autoSignHeaders = [
+            'from',
+            'to',
+            'cc',
+            'date',
+            'subject',
+            'reply-to',
+            'message-id',
+            'content-type',
+            'mime-version',
+            'x-mailer',
+        ];
+        if (stripos($headers_line, 'Subject') === false) {
+            $headers_line .= 'Subject: ' . $subject . static::$LE;
+        }
+        $headerLines = explode(static::$LE, $headers_line);
+        $currentHeaderLabel = '';
+        $currentHeaderValue = '';
+        $parsedHeaders = [];
+        $headerLineIndex = 0;
+        $headerLineCount = count($headerLines);
+        foreach ($headerLines as $headerLine) {
+            $matches = [];
+            if (preg_match('/^([^ \t]*?)(?::[ \t]*)(.*)$/', $headerLine, $matches)) {
+                if ($currentHeaderLabel !== '') {
+                    //We were previously in another header; This is the start of a new header, so save the previous one
+                    $parsedHeaders[] = ['label' => $currentHeaderLabel, 'value' => $currentHeaderValue];
+                }
+                $currentHeaderLabel = $matches[1];
+                $currentHeaderValue = $matches[2];
+            } elseif (preg_match('/^[ \t]+(.*)$/', $headerLine, $matches)) {
+                //This is a folded continuation of the current header, so unfold it
+                $currentHeaderValue .= ' ' . $matches[1];
+            }
+            ++$headerLineIndex;
+            if ($headerLineIndex >= $headerLineCount) {
+                //This was the last line, so finish off this header
+                $parsedHeaders[] = ['label' => $currentHeaderLabel, 'value' => $currentHeaderValue];
+            }
+        }
+        $copiedHeaders = [];
+        $headersToSignKeys = [];
+        $headersToSign = [];
+        foreach ($parsedHeaders as $header) {
+            //Is this header one that must be included in the DKIM signature?
+            if (in_array(strtolower($header['label']), $autoSignHeaders, true)) {
+                $headersToSignKeys[] = $header['label'];
+                $headersToSign[] = $header['label'] . ': ' . $header['value'];
+                if ($this->DKIM_copyHeaderFields) {
+                    $copiedHeaders[] = $header['label'] . ':' . //Note no space after this, as per RFC
+                        str_replace('|', '=7C', $this->DKIM_QP($header['value']));
+                }
+                continue;
+            }
+            //Is this an extra custom header we've been asked to sign?
+            if (in_array($header['label'], $this->DKIM_extraHeaders, true)) {
+                //Find its value in custom headers
+                foreach ($this->CustomHeader as $customHeader) {
+                    if ($customHeader[0] === $header['label']) {
+                        $headersToSignKeys[] = $header['label'];
+                        $headersToSign[] = $header['label'] . ': ' . $header['value'];
+                        if ($this->DKIM_copyHeaderFields) {
+                            $copiedHeaders[] = $header['label'] . ':' . //Note no space after this, as per RFC
+                                str_replace('|', '=7C', $this->DKIM_QP($header['value']));
+                        }
+                        //Skip straight to the next header
+                        continue 2;
+                    }
+                }
+            }
+        }
+        $copiedHeaderFields = '';
+        if ($this->DKIM_copyHeaderFields && count($copiedHeaders) > 0) {
+            //Assemble a DKIM 'z' tag
+            $copiedHeaderFields = ' z=';
+            $first = true;
+            foreach ($copiedHeaders as $copiedHeader) {
+                if (!$first) {
+                    $copiedHeaderFields .= static::$LE . ' |';
+                }
+                //Fold long values
+                if (strlen($copiedHeader) > self::STD_LINE_LENGTH - 3) {
+                    $copiedHeaderFields .= substr(
+                        chunk_split($copiedHeader, self::STD_LINE_LENGTH - 3, static::$LE . self::FWS),
+                        0,
+                        -strlen(static::$LE . self::FWS)
+                    );
+                } else {
+                    $copiedHeaderFields .= $copiedHeader;
+                }
+                $first = false;
+            }
+            $copiedHeaderFields .= ';' . static::$LE;
+        }
+        $headerKeys = ' h=' . implode(':', $headersToSignKeys) . ';' . static::$LE;
+        $headerValues = implode(static::$LE, $headersToSign);
+        $body = $this->DKIM_BodyC($body);
+        //Base64 of packed binary SHA-256 hash of body
+        $DKIMb64 = base64_encode(pack('H*', hash('sha256', $body)));
+        $ident = '';
+        if ('' !== $this->DKIM_identity) {
+            $ident = ' i=' . $this->DKIM_identity . ';' . static::$LE;
+        }
+        //The DKIM-Signature header is included in the signature *except for* the value of the `b` tag
+        //which is appended after calculating the signature
+        //https://tools.ietf.org/html/rfc6376#section-3.5
+        $dkimSignatureHeader = 'DKIM-Signature: v=1;' .
+            ' d=' . $this->DKIM_domain . ';' .
+            ' s=' . $this->DKIM_selector . ';' . static::$LE .
+            ' a=' . $DKIMsignatureType . ';' .
+            ' q=' . $DKIMquery . ';' .
+            ' t=' . $DKIMtime . ';' .
+            ' c=' . $DKIMcanonicalization . ';' . static::$LE .
+            $headerKeys .
+            $ident .
+            $copiedHeaderFields .
+            ' bh=' . $DKIMb64 . ';' . static::$LE .
+            ' b=';
+        //Canonicalize the set of headers
+        $canonicalizedHeaders = $this->DKIM_HeaderC(
+            $headerValues . static::$LE . $dkimSignatureHeader
+        );
+        $signature = $this->DKIM_Sign($canonicalizedHeaders);
+        $signature = trim(chunk_split($signature, self::STD_LINE_LENGTH - 3, static::$LE . self::FWS));
+
+        return static::normalizeBreaks($dkimSignatureHeader . $signature);
+    }
+
+    /**
+     * Detect if a string contains a line longer than the maximum line length
+     * allowed by RFC 2822 section 2.1.1.
+     *
+     * @param string $str
+     *
+     * @return bool
+     */
+    public static function hasLineLongerThanMax($str)
+    {
+        return (bool) preg_match('/^(.{' . (self::MAX_LINE_LENGTH + strlen(static::$LE)) . ',})/m', $str);
+    }
+
+    /**
+     * If a string contains any "special" characters, double-quote the name,
+     * and escape any double quotes with a backslash.
+     *
+     * @param string $str
+     *
+     * @return string
+     *
+     * @see RFC822 3.4.1
+     */
+    public static function quotedString($str)
+    {
+        if (preg_match('/[ ()<>@,;:"\/\[\]?=]/', $str)) {
+            //If the string contains any of these chars, it must be double-quoted
+            //and any double quotes must be escaped with a backslash
+            return '"' . str_replace('"', '\\"', $str) . '"';
+        }
+
+        //Return the string untouched, it doesn't need quoting
+        return $str;
+    }
+
+    /**
+     * Allows for public read access to 'to' property.
+     * Before the send() call, queued addresses (i.e. with IDN) are not yet included.
+     *
+     * @return array
+     */
+    public function getToAddresses()
+    {
+        return $this->to;
+    }
+
+    /**
+     * Allows for public read access to 'cc' property.
+     * Before the send() call, queued addresses (i.e. with IDN) are not yet included.
+     *
+     * @return array
+     */
+    public function getCcAddresses()
+    {
+        return $this->cc;
+    }
+
+    /**
+     * Allows for public read access to 'bcc' property.
+     * Before the send() call, queued addresses (i.e. with IDN) are not yet included.
+     *
+     * @return array
+     */
+    public function getBccAddresses()
+    {
+        return $this->bcc;
+    }
+
+    /**
+     * Allows for public read access to 'ReplyTo' property.
+     * Before the send() call, queued addresses (i.e. with IDN) are not yet included.
+     *
+     * @return array
+     */
+    public function getReplyToAddresses()
+    {
+        return $this->ReplyTo;
+    }
+
+    /**
+     * Allows for public read access to 'all_recipients' property.
+     * Before the send() call, queued addresses (i.e. with IDN) are not yet included.
+     *
+     * @return array
+     */
+    public function getAllRecipientAddresses()
+    {
+        return $this->all_recipients;
+    }
+
+    /**
+     * Perform a callback.
+     *
+     * @param bool   $isSent
+     * @param array  $to
+     * @param array  $cc
+     * @param array  $bcc
+     * @param string $subject
+     * @param string $body
+     * @param string $from
+     * @param array  $extra
+     */
+    protected function doCallback($isSent, $to, $cc, $bcc, $subject, $body, $from, $extra)
+    {
+        if (!empty($this->action_function) && is_callable($this->action_function)) {
+            call_user_func($this->action_function, $isSent, $to, $cc, $bcc, $subject, $body, $from, $extra);
+        }
+    }
+
+    /**
+     * Get the OAuthTokenProvider instance.
+     *
+     * @return OAuthTokenProvider
+     */
+    public function getOAuth()
+    {
+        return $this->oauth;
+    }
+
+    /**
+     * Set an OAuthTokenProvider instance.
+     */
+    public function setOAuth(OAuthTokenProvider $oauth)
+    {
+        $this->oauth = $oauth;
+    }
+}

+ 467 - 0
PHPMailer/POP3.php

@@ -0,0 +1,467 @@
+<?php
+
+/**
+ * PHPMailer POP-Before-SMTP Authentication Class.
+ * PHP Version 5.5.
+ *
+ * @see https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project
+ *
+ * @author    Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk>
+ * @author    Jim Jagielski (jimjag) <jimjag@gmail.com>
+ * @author    Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>
+ * @author    Brent R. Matzelle (original founder)
+ * @copyright 2012 - 2020 Marcus Bointon
+ * @copyright 2010 - 2012 Jim Jagielski
+ * @copyright 2004 - 2009 Andy Prevost
+ * @license   https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html GNU Lesser General Public License
+ * @note      This program is distributed in the hope that it will be useful - WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+namespace PHPMailer\PHPMailer;
+
+/**
+ * PHPMailer POP-Before-SMTP Authentication Class.
+ * Specifically for PHPMailer to use for RFC1939 POP-before-SMTP authentication.
+ * 1) This class does not support APOP authentication.
+ * 2) Opening and closing lots of POP3 connections can be quite slow. If you need
+ *   to send a batch of emails then just perform the authentication once at the start,
+ *   and then loop through your mail sending script. Providing this process doesn't
+ *   take longer than the verification period lasts on your POP3 server, you should be fine.
+ * 3) This is really ancient technology; you should only need to use it to talk to very old systems.
+ * 4) This POP3 class is deliberately lightweight and incomplete, implementing just
+ *   enough to do authentication.
+ *   If you want a more complete class there are other POP3 classes for PHP available.
+ *
+ * @author Richard Davey (original author) <rich@corephp.co.uk>
+ * @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk>
+ * @author Jim Jagielski (jimjag) <jimjag@gmail.com>
+ * @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>
+ */
+class POP3
+{
+    /**
+     * The POP3 PHPMailer Version number.
+     *
+     * @var string
+     */
+    const VERSION = '6.9.1';
+
+    /**
+     * Default POP3 port number.
+     *
+     * @var int
+     */
+    const DEFAULT_PORT = 110;
+
+    /**
+     * Default timeout in seconds.
+     *
+     * @var int
+     */
+    const DEFAULT_TIMEOUT = 30;
+
+    /**
+     * POP3 class debug output mode.
+     * Debug output level.
+     * Options:
+     * @see POP3::DEBUG_OFF: No output
+     * @see POP3::DEBUG_SERVER: Server messages, connection/server errors
+     * @see POP3::DEBUG_CLIENT: Client and Server messages, connection/server errors
+     *
+     * @var int
+     */
+    public $do_debug = self::DEBUG_OFF;
+
+    /**
+     * POP3 mail server hostname.
+     *
+     * @var string
+     */
+    public $host;
+
+    /**
+     * POP3 port number.
+     *
+     * @var int
+     */
+    public $port;
+
+    /**
+     * POP3 Timeout Value in seconds.
+     *
+     * @var int
+     */
+    public $tval;
+
+    /**
+     * POP3 username.
+     *
+     * @var string
+     */
+    public $username;
+
+    /**
+     * POP3 password.
+     *
+     * @var string
+     */
+    public $password;
+
+    /**
+     * Resource handle for the POP3 connection socket.
+     *
+     * @var resource
+     */
+    protected $pop_conn;
+
+    /**
+     * Are we connected?
+     *
+     * @var bool
+     */
+    protected $connected = false;
+
+    /**
+     * Error container.
+     *
+     * @var array
+     */
+    protected $errors = [];
+
+    /**
+     * Line break constant.
+     */
+    const LE = "\r\n";
+
+    /**
+     * Debug level for no output.
+     *
+     * @var int
+     */
+    const DEBUG_OFF = 0;
+
+    /**
+     * Debug level to show server -> client messages
+     * also shows clients connection errors or errors from server
+     *
+     * @var int
+     */
+    const DEBUG_SERVER = 1;
+
+    /**
+     * Debug level to show client -> server and server -> client messages.
+     *
+     * @var int
+     */
+    const DEBUG_CLIENT = 2;
+
+    /**
+     * Simple static wrapper for all-in-one POP before SMTP.
+     *
+     * @param string   $host        The hostname to connect to
+     * @param int|bool $port        The port number to connect to
+     * @param int|bool $timeout     The timeout value
+     * @param string   $username
+     * @param string   $password
+     * @param int      $debug_level
+     *
+     * @return bool
+     */
+    public static function popBeforeSmtp(
+        $host,
+        $port = false,
+        $timeout = false,
+        $username = '',
+        $password = '',
+        $debug_level = 0
+    ) {
+        $pop = new self();
+
+        return $pop->authorise($host, $port, $timeout, $username, $password, $debug_level);
+    }
+
+    /**
+     * Authenticate with a POP3 server.
+     * A connect, login, disconnect sequence
+     * appropriate for POP-before SMTP authorisation.
+     *
+     * @param string   $host        The hostname to connect to
+     * @param int|bool $port        The port number to connect to
+     * @param int|bool $timeout     The timeout value
+     * @param string   $username
+     * @param string   $password
+     * @param int      $debug_level
+     *
+     * @return bool
+     */
+    public function authorise($host, $port = false, $timeout = false, $username = '', $password = '', $debug_level = 0)
+    {
+        $this->host = $host;
+        //If no port value provided, use default
+        if (false === $port) {
+            $this->port = static::DEFAULT_PORT;
+        } else {
+            $this->port = (int) $port;
+        }
+        //If no timeout value provided, use default
+        if (false === $timeout) {
+            $this->tval = static::DEFAULT_TIMEOUT;
+        } else {
+            $this->tval = (int) $timeout;
+        }
+        $this->do_debug = $debug_level;
+        $this->username = $username;
+        $this->password = $password;
+        //Reset the error log
+        $this->errors = [];
+        //Connect
+        $result = $this->connect($this->host, $this->port, $this->tval);
+        if ($result) {
+            $login_result = $this->login($this->username, $this->password);
+            if ($login_result) {
+                $this->disconnect();
+
+                return true;
+            }
+        }
+        //We need to disconnect regardless of whether the login succeeded
+        $this->disconnect();
+
+        return false;
+    }
+
+    /**
+     * Connect to a POP3 server.
+     *
+     * @param string   $host
+     * @param int|bool $port
+     * @param int      $tval
+     *
+     * @return bool
+     */
+    public function connect($host, $port = false, $tval = 30)
+    {
+        //Are we already connected?
+        if ($this->connected) {
+            return true;
+        }
+
+        //On Windows this will raise a PHP Warning error if the hostname doesn't exist.
+        //Rather than suppress it with @fsockopen, capture it cleanly instead
+        set_error_handler([$this, 'catchWarning']);
+
+        if (false === $port) {
+            $port = static::DEFAULT_PORT;
+        }
+
+        //Connect to the POP3 server
+        $errno = 0;
+        $errstr = '';
+        $this->pop_conn = fsockopen(
+            $host, //POP3 Host
+            $port, //Port #
+            $errno, //Error Number
+            $errstr, //Error Message
+            $tval
+        ); //Timeout (seconds)
+        //Restore the error handler
+        restore_error_handler();
+
+        //Did we connect?
+        if (false === $this->pop_conn) {
+            //It would appear not...
+            $this->setError(
+                "Failed to connect to server $host on port $port. errno: $errno; errstr: $errstr"
+            );
+
+            return false;
+        }
+
+        //Increase the stream time-out
+        stream_set_timeout($this->pop_conn, $tval, 0);
+
+        //Get the POP3 server response
+        $pop3_response = $this->getResponse();
+        //Check for the +OK
+        if ($this->checkResponse($pop3_response)) {
+            //The connection is established and the POP3 server is talking
+            $this->connected = true;
+
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Log in to the POP3 server.
+     * Does not support APOP (RFC 2828, 4949).
+     *
+     * @param string $username
+     * @param string $password
+     *
+     * @return bool
+     */
+    public function login($username = '', $password = '')
+    {
+        if (!$this->connected) {
+            $this->setError('Not connected to POP3 server');
+            return false;
+        }
+        if (empty($username)) {
+            $username = $this->username;
+        }
+        if (empty($password)) {
+            $password = $this->password;
+        }
+
+        //Send the Username
+        $this->sendString("USER $username" . static::LE);
+        $pop3_response = $this->getResponse();
+        if ($this->checkResponse($pop3_response)) {
+            //Send the Password
+            $this->sendString("PASS $password" . static::LE);
+            $pop3_response = $this->getResponse();
+            if ($this->checkResponse($pop3_response)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Disconnect from the POP3 server.
+     */
+    public function disconnect()
+    {
+        // If could not connect at all, no need to disconnect
+        if ($this->pop_conn === false) {
+            return;
+        }
+
+        $this->sendString('QUIT' . static::LE);
+
+        // RFC 1939 shows POP3 server sending a +OK response to the QUIT command.
+        // Try to get it.  Ignore any failures here.
+        try {
+            $this->getResponse();
+        } catch (Exception $e) {
+            //Do nothing
+        }
+
+        //The QUIT command may cause the daemon to exit, which will kill our connection
+        //So ignore errors here
+        try {
+            @fclose($this->pop_conn);
+        } catch (Exception $e) {
+            //Do nothing
+        }
+
+        // Clean up attributes.
+        $this->connected = false;
+        $this->pop_conn  = false;
+    }
+
+    /**
+     * Get a response from the POP3 server.
+     *
+     * @param int $size The maximum number of bytes to retrieve
+     *
+     * @return string
+     */
+    protected function getResponse($size = 128)
+    {
+        $response = fgets($this->pop_conn, $size);
+        if ($this->do_debug >= self::DEBUG_SERVER) {
+            echo 'Server -> Client: ', $response;
+        }
+
+        return $response;
+    }
+
+    /**
+     * Send raw data to the POP3 server.
+     *
+     * @param string $string
+     *
+     * @return int
+     */
+    protected function sendString($string)
+    {
+        if ($this->pop_conn) {
+            if ($this->do_debug >= self::DEBUG_CLIENT) { //Show client messages when debug >= 2
+                echo 'Client -> Server: ', $string;
+            }
+
+            return fwrite($this->pop_conn, $string, strlen($string));
+        }
+
+        return 0;
+    }
+
+    /**
+     * Checks the POP3 server response.
+     * Looks for for +OK or -ERR.
+     *
+     * @param string $string
+     *
+     * @return bool
+     */
+    protected function checkResponse($string)
+    {
+        if (strpos($string, '+OK') !== 0) {
+            $this->setError("Server reported an error: $string");
+
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Add an error to the internal error store.
+     * Also display debug output if it's enabled.
+     *
+     * @param string $error
+     */
+    protected function setError($error)
+    {
+        $this->errors[] = $error;
+        if ($this->do_debug >= self::DEBUG_SERVER) {
+            echo '<pre>';
+            foreach ($this->errors as $e) {
+                print_r($e);
+            }
+            echo '</pre>';
+        }
+    }
+
+    /**
+     * Get an array of error messages, if any.
+     *
+     * @return array
+     */
+    public function getErrors()
+    {
+        return $this->errors;
+    }
+
+    /**
+     * POP3 connection error handler.
+     *
+     * @param int    $errno
+     * @param string $errstr
+     * @param string $errfile
+     * @param int    $errline
+     */
+    protected function catchWarning($errno, $errstr, $errfile, $errline)
+    {
+        $this->setError(
+            'Connecting to the POP3 server raised a PHP warning:' .
+            "errno: $errno errstr: $errstr; errfile: $errfile; errline: $errline"
+        );
+    }
+}

+ 1499 - 0
PHPMailer/SMTP.php

@@ -0,0 +1,1499 @@
+<?php
+
+/**
+ * PHPMailer RFC821 SMTP email transport class.
+ * PHP Version 5.5.
+ *
+ * @see       https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project
+ *
+ * @author    Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk>
+ * @author    Jim Jagielski (jimjag) <jimjag@gmail.com>
+ * @author    Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>
+ * @author    Brent R. Matzelle (original founder)
+ * @copyright 2012 - 2020 Marcus Bointon
+ * @copyright 2010 - 2012 Jim Jagielski
+ * @copyright 2004 - 2009 Andy Prevost
+ * @license   https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html GNU Lesser General Public License
+ * @note      This program is distributed in the hope that it will be useful - WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+namespace PHPMailer\PHPMailer;
+
+/**
+ * PHPMailer RFC821 SMTP email transport class.
+ * Implements RFC 821 SMTP commands and provides some utility methods for sending mail to an SMTP server.
+ *
+ * @author Chris Ryan
+ * @author Marcus Bointon <phpmailer@synchromedia.co.uk>
+ */
+class SMTP
+{
+    /**
+     * The PHPMailer SMTP version number.
+     *
+     * @var string
+     */
+    const VERSION = '6.9.1';
+
+    /**
+     * SMTP line break constant.
+     *
+     * @var string
+     */
+    const LE = "\r\n";
+
+    /**
+     * The SMTP port to use if one is not specified.
+     *
+     * @var int
+     */
+    const DEFAULT_PORT = 25;
+
+    /**
+     * The SMTPs port to use if one is not specified.
+     *
+     * @var int
+     */
+    const DEFAULT_SECURE_PORT = 465;
+
+    /**
+     * The maximum line length allowed by RFC 5321 section 4.5.3.1.6,
+     * *excluding* a trailing CRLF break.
+     *
+     * @see https://tools.ietf.org/html/rfc5321#section-4.5.3.1.6
+     *
+     * @var int
+     */
+    const MAX_LINE_LENGTH = 998;
+
+    /**
+     * The maximum line length allowed for replies in RFC 5321 section 4.5.3.1.5,
+     * *including* a trailing CRLF line break.
+     *
+     * @see https://tools.ietf.org/html/rfc5321#section-4.5.3.1.5
+     *
+     * @var int
+     */
+    const MAX_REPLY_LENGTH = 512;
+
+    /**
+     * Debug level for no output.
+     *
+     * @var int
+     */
+    const DEBUG_OFF = 0;
+
+    /**
+     * Debug level to show client -> server messages.
+     *
+     * @var int
+     */
+    const DEBUG_CLIENT = 1;
+
+    /**
+     * Debug level to show client -> server and server -> client messages.
+     *
+     * @var int
+     */
+    const DEBUG_SERVER = 2;
+
+    /**
+     * Debug level to show connection status, client -> server and server -> client messages.
+     *
+     * @var int
+     */
+    const DEBUG_CONNECTION = 3;
+
+    /**
+     * Debug level to show all messages.
+     *
+     * @var int
+     */
+    const DEBUG_LOWLEVEL = 4;
+
+    /**
+     * Debug output level.
+     * Options:
+     * * self::DEBUG_OFF (`0`) No debug output, default
+     * * self::DEBUG_CLIENT (`1`) Client commands
+     * * self::DEBUG_SERVER (`2`) Client commands and server responses
+     * * self::DEBUG_CONNECTION (`3`) As DEBUG_SERVER plus connection status
+     * * self::DEBUG_LOWLEVEL (`4`) Low-level data output, all messages.
+     *
+     * @var int
+     */
+    public $do_debug = self::DEBUG_OFF;
+
+    /**
+     * How to handle debug output.
+     * Options:
+     * * `echo` Output plain-text as-is, appropriate for CLI
+     * * `html` Output escaped, line breaks converted to `<br>`, appropriate for browser output
+     * * `error_log` Output to error log as configured in php.ini
+     * Alternatively, you can provide a callable expecting two params: a message string and the debug level:
+     *
+     * ```php
+     * $smtp->Debugoutput = function($str, $level) {echo "debug level $level; message: $str";};
+     * ```
+     *
+     * Alternatively, you can pass in an instance of a PSR-3 compatible logger, though only `debug`
+     * level output is used:
+     *
+     * ```php
+     * $mail->Debugoutput = new myPsr3Logger;
+     * ```
+     *
+     * @var string|callable|\Psr\Log\LoggerInterface
+     */
+    public $Debugoutput = 'echo';
+
+    /**
+     * Whether to use VERP.
+     *
+     * @see https://en.wikipedia.org/wiki/Variable_envelope_return_path
+     * @see https://www.postfix.org/VERP_README.html Info on VERP
+     *
+     * @var bool
+     */
+    public $do_verp = false;
+
+    /**
+     * The timeout value for connection, in seconds.
+     * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2.
+     * This needs to be quite high to function correctly with hosts using greetdelay as an anti-spam measure.
+     *
+     * @see https://www.rfc-editor.org/rfc/rfc2821#section-4.5.3.2
+     *
+     * @var int
+     */
+    public $Timeout = 300;
+
+    /**
+     * How long to wait for commands to complete, in seconds.
+     * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2.
+     *
+     * @var int
+     */
+    public $Timelimit = 300;
+
+    /**
+     * Patterns to extract an SMTP transaction id from reply to a DATA command.
+     * The first capture group in each regex will be used as the ID.
+     * MS ESMTP returns the message ID, which may not be correct for internal tracking.
+     *
+     * @var string[]
+     */
+    protected $smtp_transaction_id_patterns = [
+        'exim' => '/[\d]{3} OK id=(.*)/',
+        'sendmail' => '/[\d]{3} 2\.0\.0 (.*) Message/',
+        'postfix' => '/[\d]{3} 2\.0\.0 Ok: queued as (.*)/',
+        'Microsoft_ESMTP' => '/[0-9]{3} 2\.[\d]\.0 (.*)@(?:.*) Queued mail for delivery/',
+        'Amazon_SES' => '/[\d]{3} Ok (.*)/',
+        'SendGrid' => '/[\d]{3} Ok: queued as (.*)/',
+        'CampaignMonitor' => '/[\d]{3} 2\.0\.0 OK:([a-zA-Z\d]{48})/',
+        'Haraka' => '/[\d]{3} Message Queued \((.*)\)/',
+        'ZoneMTA' => '/[\d]{3} Message queued as (.*)/',
+        'Mailjet' => '/[\d]{3} OK queued as (.*)/',
+    ];
+
+    /**
+     * Allowed SMTP XCLIENT attributes.
+     * Must be allowed by the SMTP server. EHLO response is not checked.
+     *
+     * @see https://www.postfix.org/XCLIENT_README.html
+     *
+     * @var array
+     */
+    public static $xclient_allowed_attributes = [
+        'NAME', 'ADDR', 'PORT', 'PROTO', 'HELO', 'LOGIN', 'DESTADDR', 'DESTPORT'
+    ];
+
+    /**
+     * The last transaction ID issued in response to a DATA command,
+     * if one was detected.
+     *
+     * @var string|bool|null
+     */
+    protected $last_smtp_transaction_id;
+
+    /**
+     * The socket for the server connection.
+     *
+     * @var ?resource
+     */
+    protected $smtp_conn;
+
+    /**
+     * Error information, if any, for the last SMTP command.
+     *
+     * @var array
+     */
+    protected $error = [
+        'error' => '',
+        'detail' => '',
+        'smtp_code' => '',
+        'smtp_code_ex' => '',
+    ];
+
+    /**
+     * The reply the server sent to us for HELO.
+     * If null, no HELO string has yet been received.
+     *
+     * @var string|null
+     */
+    protected $helo_rply;
+
+    /**
+     * The set of SMTP extensions sent in reply to EHLO command.
+     * Indexes of the array are extension names.
+     * Value at index 'HELO' or 'EHLO' (according to command that was sent)
+     * represents the server name. In case of HELO it is the only element of the array.
+     * Other values can be boolean TRUE or an array containing extension options.
+     * If null, no HELO/EHLO string has yet been received.
+     *
+     * @var array|null
+     */
+    protected $server_caps;
+
+    /**
+     * The most recent reply received from the server.
+     *
+     * @var string
+     */
+    protected $last_reply = '';
+
+    /**
+     * Output debugging info via a user-selected method.
+     *
+     * @param string $str   Debug string to output
+     * @param int    $level The debug level of this message; see DEBUG_* constants
+     *
+     * @see SMTP::$Debugoutput
+     * @see SMTP::$do_debug
+     */
+    protected function edebug($str, $level = 0)
+    {
+        if ($level > $this->do_debug) {
+            return;
+        }
+        //Is this a PSR-3 logger?
+        if ($this->Debugoutput instanceof \Psr\Log\LoggerInterface) {
+            //Remove trailing line breaks potentially added by calls to SMTP::client_send()
+            $this->Debugoutput->debug(rtrim($str, "\r\n"));
+
+            return;
+        }
+        //Avoid clash with built-in function names
+        if (is_callable($this->Debugoutput) && !in_array($this->Debugoutput, ['error_log', 'html', 'echo'])) {
+            call_user_func($this->Debugoutput, $str, $level);
+
+            return;
+        }
+        switch ($this->Debugoutput) {
+            case 'error_log':
+                //Don't output, just log
+                /** @noinspection ForgottenDebugOutputInspection */
+                error_log($str);
+                break;
+            case 'html':
+                //Cleans up output a bit for a better looking, HTML-safe output
+                echo gmdate('Y-m-d H:i:s'), ' ', htmlentities(
+                    preg_replace('/[\r\n]+/', '', $str),
+                    ENT_QUOTES,
+                    'UTF-8'
+                ), "<br>\n";
+                break;
+            case 'echo':
+            default:
+                //Normalize line breaks
+                $str = preg_replace('/\r\n|\r/m', "\n", $str);
+                echo gmdate('Y-m-d H:i:s'),
+                "\t",
+                    //Trim trailing space
+                trim(
+                    //Indent for readability, except for trailing break
+                    str_replace(
+                        "\n",
+                        "\n                   \t                  ",
+                        trim($str)
+                    )
+                ),
+                "\n";
+        }
+    }
+
+    /**
+     * Connect to an SMTP server.
+     *
+     * @param string $host    SMTP server IP or host name
+     * @param int    $port    The port number to connect to
+     * @param int    $timeout How long to wait for the connection to open
+     * @param array  $options An array of options for stream_context_create()
+     *
+     * @return bool
+     */
+    public function connect($host, $port = null, $timeout = 30, $options = [])
+    {
+        //Clear errors to avoid confusion
+        $this->setError('');
+        //Make sure we are __not__ connected
+        if ($this->connected()) {
+            //Already connected, generate error
+            $this->setError('Already connected to a server');
+
+            return false;
+        }
+        if (empty($port)) {
+            $port = self::DEFAULT_PORT;
+        }
+        //Connect to the SMTP server
+        $this->edebug(
+            "Connection: opening to $host:$port, timeout=$timeout, options=" .
+            (count($options) > 0 ? var_export($options, true) : 'array()'),
+            self::DEBUG_CONNECTION
+        );
+
+        $this->smtp_conn = $this->getSMTPConnection($host, $port, $timeout, $options);
+
+        if ($this->smtp_conn === false) {
+            //Error info already set inside `getSMTPConnection()`
+            return false;
+        }
+
+        $this->edebug('Connection: opened', self::DEBUG_CONNECTION);
+
+        //Get any announcement
+        $this->last_reply = $this->get_lines();
+        $this->edebug('SERVER -> CLIENT: ' . $this->last_reply, self::DEBUG_SERVER);
+        $responseCode = (int)substr($this->last_reply, 0, 3);
+        if ($responseCode === 220) {
+            return true;
+        }
+        //Anything other than a 220 response means something went wrong
+        //RFC 5321 says the server will wait for us to send a QUIT in response to a 554 error
+        //https://tools.ietf.org/html/rfc5321#section-3.1
+        if ($responseCode === 554) {
+            $this->quit();
+        }
+        //This will handle 421 responses which may not wait for a QUIT (e.g. if the server is being shut down)
+        $this->edebug('Connection: closing due to error', self::DEBUG_CONNECTION);
+        $this->close();
+        return false;
+    }
+
+    /**
+     * Create connection to the SMTP server.
+     *
+     * @param string $host    SMTP server IP or host name
+     * @param int    $port    The port number to connect to
+     * @param int    $timeout How long to wait for the connection to open
+     * @param array  $options An array of options for stream_context_create()
+     *
+     * @return false|resource
+     */
+    protected function getSMTPConnection($host, $port = null, $timeout = 30, $options = [])
+    {
+        static $streamok;
+        //This is enabled by default since 5.0.0 but some providers disable it
+        //Check this once and cache the result
+        if (null === $streamok) {
+            $streamok = function_exists('stream_socket_client');
+        }
+
+        $errno = 0;
+        $errstr = '';
+        if ($streamok) {
+            $socket_context = stream_context_create($options);
+            set_error_handler([$this, 'errorHandler']);
+            $connection = stream_socket_client(
+                $host . ':' . $port,
+                $errno,
+                $errstr,
+                $timeout,
+                STREAM_CLIENT_CONNECT,
+                $socket_context
+            );
+        } else {
+            //Fall back to fsockopen which should work in more places, but is missing some features
+            $this->edebug(
+                'Connection: stream_socket_client not available, falling back to fsockopen',
+                self::DEBUG_CONNECTION
+            );
+            set_error_handler([$this, 'errorHandler']);
+            $connection = fsockopen(
+                $host,
+                $port,
+                $errno,
+                $errstr,
+                $timeout
+            );
+        }
+        restore_error_handler();
+
+        //Verify we connected properly
+        if (!is_resource($connection)) {
+            $this->setError(
+                'Failed to connect to server',
+                '',
+                (string) $errno,
+                $errstr
+            );
+            $this->edebug(
+                'SMTP ERROR: ' . $this->error['error']
+                . ": $errstr ($errno)",
+                self::DEBUG_CLIENT
+            );
+
+            return false;
+        }
+
+        //SMTP server can take longer to respond, give longer timeout for first read
+        //Windows does not have support for this timeout function
+        if (strpos(PHP_OS, 'WIN') !== 0) {
+            $max = (int)ini_get('max_execution_time');
+            //Don't bother if unlimited, or if set_time_limit is disabled
+            if (0 !== $max && $timeout > $max && strpos(ini_get('disable_functions'), 'set_time_limit') === false) {
+                @set_time_limit($timeout);
+            }
+            stream_set_timeout($connection, $timeout, 0);
+        }
+
+        return $connection;
+    }
+
+    /**
+     * Initiate a TLS (encrypted) session.
+     *
+     * @return bool
+     */
+    public function startTLS()
+    {
+        if (!$this->sendCommand('STARTTLS', 'STARTTLS', 220)) {
+            return false;
+        }
+
+        //Allow the best TLS version(s) we can
+        $crypto_method = STREAM_CRYPTO_METHOD_TLS_CLIENT;
+
+        //PHP 5.6.7 dropped inclusion of TLS 1.1 and 1.2 in STREAM_CRYPTO_METHOD_TLS_CLIENT
+        //so add them back in manually if we can
+        if (defined('STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT')) {
+            $crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT;
+            $crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT;
+        }
+
+        //Begin encrypted connection
+        set_error_handler([$this, 'errorHandler']);
+        $crypto_ok = stream_socket_enable_crypto(
+            $this->smtp_conn,
+            true,
+            $crypto_method
+        );
+        restore_error_handler();
+
+        return (bool) $crypto_ok;
+    }
+
+    /**
+     * Perform SMTP authentication.
+     * Must be run after hello().
+     *
+     * @see    hello()
+     *
+     * @param string $username The user name
+     * @param string $password The password
+     * @param string $authtype The auth type (CRAM-MD5, PLAIN, LOGIN, XOAUTH2)
+     * @param OAuthTokenProvider $OAuth An optional OAuthTokenProvider instance for XOAUTH2 authentication
+     *
+     * @return bool True if successfully authenticated
+     */
+    public function authenticate(
+        $username,
+        $password,
+        $authtype = null,
+        $OAuth = null
+    ) {
+        if (!$this->server_caps) {
+            $this->setError('Authentication is not allowed before HELO/EHLO');
+
+            return false;
+        }
+
+        if (array_key_exists('EHLO', $this->server_caps)) {
+            //SMTP extensions are available; try to find a proper authentication method
+            if (!array_key_exists('AUTH', $this->server_caps)) {
+                $this->setError('Authentication is not allowed at this stage');
+                //'at this stage' means that auth may be allowed after the stage changes
+                //e.g. after STARTTLS
+
+                return false;
+            }
+
+            $this->edebug('Auth method requested: ' . ($authtype ?: 'UNSPECIFIED'), self::DEBUG_LOWLEVEL);
+            $this->edebug(
+                'Auth methods available on the server: ' . implode(',', $this->server_caps['AUTH']),
+                self::DEBUG_LOWLEVEL
+            );
+
+            //If we have requested a specific auth type, check the server supports it before trying others
+            if (null !== $authtype && !in_array($authtype, $this->server_caps['AUTH'], true)) {
+                $this->edebug('Requested auth method not available: ' . $authtype, self::DEBUG_LOWLEVEL);
+                $authtype = null;
+            }
+
+            if (empty($authtype)) {
+                //If no auth mechanism is specified, attempt to use these, in this order
+                //Try CRAM-MD5 first as it's more secure than the others
+                foreach (['CRAM-MD5', 'LOGIN', 'PLAIN', 'XOAUTH2'] as $method) {
+                    if (in_array($method, $this->server_caps['AUTH'], true)) {
+                        $authtype = $method;
+                        break;
+                    }
+                }
+                if (empty($authtype)) {
+                    $this->setError('No supported authentication methods found');
+
+                    return false;
+                }
+                $this->edebug('Auth method selected: ' . $authtype, self::DEBUG_LOWLEVEL);
+            }
+
+            if (!in_array($authtype, $this->server_caps['AUTH'], true)) {
+                $this->setError("The requested authentication method \"$authtype\" is not supported by the server");
+
+                return false;
+            }
+        } elseif (empty($authtype)) {
+            $authtype = 'LOGIN';
+        }
+        switch ($authtype) {
+            case 'PLAIN':
+                //Start authentication
+                if (!$this->sendCommand('AUTH', 'AUTH PLAIN', 334)) {
+                    return false;
+                }
+                //Send encoded username and password
+                if (
+                    //Format from https://tools.ietf.org/html/rfc4616#section-2
+                    //We skip the first field (it's forgery), so the string starts with a null byte
+                    !$this->sendCommand(
+                        'User & Password',
+                        base64_encode("\0" . $username . "\0" . $password),
+                        235
+                    )
+                ) {
+                    return false;
+                }
+                break;
+            case 'LOGIN':
+                //Start authentication
+                if (!$this->sendCommand('AUTH', 'AUTH LOGIN', 334)) {
+                    return false;
+                }
+                if (!$this->sendCommand('Username', base64_encode($username), 334)) {
+                    return false;
+                }
+                if (!$this->sendCommand('Password', base64_encode($password), 235)) {
+                    return false;
+                }
+                break;
+            case 'CRAM-MD5':
+                //Start authentication
+                if (!$this->sendCommand('AUTH CRAM-MD5', 'AUTH CRAM-MD5', 334)) {
+                    return false;
+                }
+                //Get the challenge
+                $challenge = base64_decode(substr($this->last_reply, 4));
+
+                //Build the response
+                $response = $username . ' ' . $this->hmac($challenge, $password);
+
+                //send encoded credentials
+                return $this->sendCommand('Username', base64_encode($response), 235);
+            case 'XOAUTH2':
+                //The OAuth instance must be set up prior to requesting auth.
+                if (null === $OAuth) {
+                    return false;
+                }
+                $oauth = $OAuth->getOauth64();
+
+                //Start authentication
+                if (!$this->sendCommand('AUTH', 'AUTH XOAUTH2 ' . $oauth, 235)) {
+                    return false;
+                }
+                break;
+            default:
+                $this->setError("Authentication method \"$authtype\" is not supported");
+
+                return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Calculate an MD5 HMAC hash.
+     * Works like hash_hmac('md5', $data, $key)
+     * in case that function is not available.
+     *
+     * @param string $data The data to hash
+     * @param string $key  The key to hash with
+     *
+     * @return string
+     */
+    protected function hmac($data, $key)
+    {
+        if (function_exists('hash_hmac')) {
+            return hash_hmac('md5', $data, $key);
+        }
+
+        //The following borrowed from
+        //https://www.php.net/manual/en/function.mhash.php#27225
+
+        //RFC 2104 HMAC implementation for php.
+        //Creates an md5 HMAC.
+        //Eliminates the need to install mhash to compute a HMAC
+        //by Lance Rushing
+
+        $bytelen = 64; //byte length for md5
+        if (strlen($key) > $bytelen) {
+            $key = pack('H*', md5($key));
+        }
+        $key = str_pad($key, $bytelen, chr(0x00));
+        $ipad = str_pad('', $bytelen, chr(0x36));
+        $opad = str_pad('', $bytelen, chr(0x5c));
+        $k_ipad = $key ^ $ipad;
+        $k_opad = $key ^ $opad;
+
+        return md5($k_opad . pack('H*', md5($k_ipad . $data)));
+    }
+
+    /**
+     * Check connection state.
+     *
+     * @return bool True if connected
+     */
+    public function connected()
+    {
+        if (is_resource($this->smtp_conn)) {
+            $sock_status = stream_get_meta_data($this->smtp_conn);
+            if ($sock_status['eof']) {
+                //The socket is valid but we are not connected
+                $this->edebug(
+                    'SMTP NOTICE: EOF caught while checking if connected',
+                    self::DEBUG_CLIENT
+                );
+                $this->close();
+
+                return false;
+            }
+
+            return true; //everything looks good
+        }
+
+        return false;
+    }
+
+    /**
+     * Close the socket and clean up the state of the class.
+     * Don't use this function without first trying to use QUIT.
+     *
+     * @see quit()
+     */
+    public function close()
+    {
+        $this->server_caps = null;
+        $this->helo_rply = null;
+        if (is_resource($this->smtp_conn)) {
+            //Close the connection and cleanup
+            fclose($this->smtp_conn);
+            $this->smtp_conn = null; //Makes for cleaner serialization
+            $this->edebug('Connection: closed', self::DEBUG_CONNECTION);
+        }
+    }
+
+    /**
+     * Send an SMTP DATA command.
+     * Issues a data command and sends the msg_data to the server,
+     * finalizing the mail transaction. $msg_data is the message
+     * that is to be sent with the headers. Each header needs to be
+     * on a single line followed by a <CRLF> with the message headers
+     * and the message body being separated by an additional <CRLF>.
+     * Implements RFC 821: DATA <CRLF>.
+     *
+     * @param string $msg_data Message data to send
+     *
+     * @return bool
+     */
+    public function data($msg_data)
+    {
+        //This will use the standard timelimit
+        if (!$this->sendCommand('DATA', 'DATA', 354)) {
+            return false;
+        }
+
+        /* The server is ready to accept data!
+         * According to rfc821 we should not send more than 1000 characters on a single line (including the LE)
+         * so we will break the data up into lines by \r and/or \n then if needed we will break each of those into
+         * smaller lines to fit within the limit.
+         * We will also look for lines that start with a '.' and prepend an additional '.'.
+         * NOTE: this does not count towards line-length limit.
+         */
+
+        //Normalize line breaks before exploding
+        $lines = explode("\n", str_replace(["\r\n", "\r"], "\n", $msg_data));
+
+        /* To distinguish between a complete RFC822 message and a plain message body, we check if the first field
+         * of the first line (':' separated) does not contain a space then it _should_ be a header, and we will
+         * process all lines before a blank line as headers.
+         */
+
+        $field = substr($lines[0], 0, strpos($lines[0], ':'));
+        $in_headers = false;
+        if (!empty($field) && strpos($field, ' ') === false) {
+            $in_headers = true;
+        }
+
+        foreach ($lines as $line) {
+            $lines_out = [];
+            if ($in_headers && $line === '') {
+                $in_headers = false;
+            }
+            //Break this line up into several smaller lines if it's too long
+            //Micro-optimisation: isset($str[$len]) is faster than (strlen($str) > $len),
+            while (isset($line[self::MAX_LINE_LENGTH])) {
+                //Working backwards, try to find a space within the last MAX_LINE_LENGTH chars of the line to break on
+                //so as to avoid breaking in the middle of a word
+                $pos = strrpos(substr($line, 0, self::MAX_LINE_LENGTH), ' ');
+                //Deliberately matches both false and 0
+                if (!$pos) {
+                    //No nice break found, add a hard break
+                    $pos = self::MAX_LINE_LENGTH - 1;
+                    $lines_out[] = substr($line, 0, $pos);
+                    $line = substr($line, $pos);
+                } else {
+                    //Break at the found point
+                    $lines_out[] = substr($line, 0, $pos);
+                    //Move along by the amount we dealt with
+                    $line = substr($line, $pos + 1);
+                }
+                //If processing headers add a LWSP-char to the front of new line RFC822 section 3.1.1
+                if ($in_headers) {
+                    $line = "\t" . $line;
+                }
+            }
+            $lines_out[] = $line;
+
+            //Send the lines to the server
+            foreach ($lines_out as $line_out) {
+                //Dot-stuffing as per RFC5321 section 4.5.2
+                //https://tools.ietf.org/html/rfc5321#section-4.5.2
+                if (!empty($line_out) && $line_out[0] === '.') {
+                    $line_out = '.' . $line_out;
+                }
+                $this->client_send($line_out . static::LE, 'DATA');
+            }
+        }
+
+        //Message data has been sent, complete the command
+        //Increase timelimit for end of DATA command
+        $savetimelimit = $this->Timelimit;
+        $this->Timelimit *= 2;
+        $result = $this->sendCommand('DATA END', '.', 250);
+        $this->recordLastTransactionID();
+        //Restore timelimit
+        $this->Timelimit = $savetimelimit;
+
+        return $result;
+    }
+
+    /**
+     * Send an SMTP HELO or EHLO command.
+     * Used to identify the sending server to the receiving server.
+     * This makes sure that client and server are in a known state.
+     * Implements RFC 821: HELO <SP> <domain> <CRLF>
+     * and RFC 2821 EHLO.
+     *
+     * @param string $host The host name or IP to connect to
+     *
+     * @return bool
+     */
+    public function hello($host = '')
+    {
+        //Try extended hello first (RFC 2821)
+        if ($this->sendHello('EHLO', $host)) {
+            return true;
+        }
+
+        //Some servers shut down the SMTP service here (RFC 5321)
+        if (substr($this->helo_rply, 0, 3) == '421') {
+            return false;
+        }
+
+        return $this->sendHello('HELO', $host);
+    }
+
+    /**
+     * Send an SMTP HELO or EHLO command.
+     * Low-level implementation used by hello().
+     *
+     * @param string $hello The HELO string
+     * @param string $host  The hostname to say we are
+     *
+     * @return bool
+     *
+     * @see hello()
+     */
+    protected function sendHello($hello, $host)
+    {
+        $noerror = $this->sendCommand($hello, $hello . ' ' . $host, 250);
+        $this->helo_rply = $this->last_reply;
+        if ($noerror) {
+            $this->parseHelloFields($hello);
+        } else {
+            $this->server_caps = null;
+        }
+
+        return $noerror;
+    }
+
+    /**
+     * Parse a reply to HELO/EHLO command to discover server extensions.
+     * In case of HELO, the only parameter that can be discovered is a server name.
+     *
+     * @param string $type `HELO` or `EHLO`
+     */
+    protected function parseHelloFields($type)
+    {
+        $this->server_caps = [];
+        $lines = explode("\n", $this->helo_rply);
+
+        foreach ($lines as $n => $s) {
+            //First 4 chars contain response code followed by - or space
+            $s = trim(substr($s, 4));
+            if (empty($s)) {
+                continue;
+            }
+            $fields = explode(' ', $s);
+            if (!empty($fields)) {
+                if (!$n) {
+                    $name = $type;
+                    $fields = $fields[0];
+                } else {
+                    $name = array_shift($fields);
+                    switch ($name) {
+                        case 'SIZE':
+                            $fields = ($fields ? $fields[0] : 0);
+                            break;
+                        case 'AUTH':
+                            if (!is_array($fields)) {
+                                $fields = [];
+                            }
+                            break;
+                        default:
+                            $fields = true;
+                    }
+                }
+                $this->server_caps[$name] = $fields;
+            }
+        }
+    }
+
+    /**
+     * Send an SMTP MAIL command.
+     * Starts a mail transaction from the email address specified in
+     * $from. Returns true if successful or false otherwise. If True
+     * the mail transaction is started and then one or more recipient
+     * commands may be called followed by a data command.
+     * Implements RFC 821: MAIL <SP> FROM:<reverse-path> <CRLF>.
+     *
+     * @param string $from Source address of this message
+     *
+     * @return bool
+     */
+    public function mail($from)
+    {
+        $useVerp = ($this->do_verp ? ' XVERP' : '');
+
+        return $this->sendCommand(
+            'MAIL FROM',
+            'MAIL FROM:<' . $from . '>' . $useVerp,
+            250
+        );
+    }
+
+    /**
+     * Send an SMTP QUIT command.
+     * Closes the socket if there is no error or the $close_on_error argument is true.
+     * Implements from RFC 821: QUIT <CRLF>.
+     *
+     * @param bool $close_on_error Should the connection close if an error occurs?
+     *
+     * @return bool
+     */
+    public function quit($close_on_error = true)
+    {
+        $noerror = $this->sendCommand('QUIT', 'QUIT', 221);
+        $err = $this->error; //Save any error
+        if ($noerror || $close_on_error) {
+            $this->close();
+            $this->error = $err; //Restore any error from the quit command
+        }
+
+        return $noerror;
+    }
+
+    /**
+     * Send an SMTP RCPT command.
+     * Sets the TO argument to $toaddr.
+     * Returns true if the recipient was accepted false if it was rejected.
+     * Implements from RFC 821: RCPT <SP> TO:<forward-path> <CRLF>.
+     *
+     * @param string $address The address the message is being sent to
+     * @param string $dsn     Comma separated list of DSN notifications. NEVER, SUCCESS, FAILURE
+     *                        or DELAY. If you specify NEVER all other notifications are ignored.
+     *
+     * @return bool
+     */
+    public function recipient($address, $dsn = '')
+    {
+        if (empty($dsn)) {
+            $rcpt = 'RCPT TO:<' . $address . '>';
+        } else {
+            $dsn = strtoupper($dsn);
+            $notify = [];
+
+            if (strpos($dsn, 'NEVER') !== false) {
+                $notify[] = 'NEVER';
+            } else {
+                foreach (['SUCCESS', 'FAILURE', 'DELAY'] as $value) {
+                    if (strpos($dsn, $value) !== false) {
+                        $notify[] = $value;
+                    }
+                }
+            }
+
+            $rcpt = 'RCPT TO:<' . $address . '> NOTIFY=' . implode(',', $notify);
+        }
+
+        return $this->sendCommand(
+            'RCPT TO',
+            $rcpt,
+            [250, 251]
+        );
+    }
+
+    /**
+     * Send SMTP XCLIENT command to server and check its return code.
+     *
+     * @return bool True on success
+     */
+    public function xclient(array $vars)
+    {
+        $xclient_options = "";
+        foreach ($vars as $key => $value) {
+            if (in_array($key, SMTP::$xclient_allowed_attributes)) {
+                $xclient_options .= " {$key}={$value}";
+            }
+        }
+        if (!$xclient_options) {
+            return true;
+        }
+        return $this->sendCommand('XCLIENT', 'XCLIENT' . $xclient_options, 250);
+    }
+
+    /**
+     * Send an SMTP RSET command.
+     * Abort any transaction that is currently in progress.
+     * Implements RFC 821: RSET <CRLF>.
+     *
+     * @return bool True on success
+     */
+    public function reset()
+    {
+        return $this->sendCommand('RSET', 'RSET', 250);
+    }
+
+    /**
+     * Send a command to an SMTP server and check its return code.
+     *
+     * @param string    $command       The command name - not sent to the server
+     * @param string    $commandstring The actual command to send
+     * @param int|array $expect        One or more expected integer success codes
+     *
+     * @return bool True on success
+     */
+    protected function sendCommand($command, $commandstring, $expect)
+    {
+        if (!$this->connected()) {
+            $this->setError("Called $command without being connected");
+
+            return false;
+        }
+        //Reject line breaks in all commands
+        if ((strpos($commandstring, "\n") !== false) || (strpos($commandstring, "\r") !== false)) {
+            $this->setError("Command '$command' contained line breaks");
+
+            return false;
+        }
+        $this->client_send($commandstring . static::LE, $command);
+
+        $this->last_reply = $this->get_lines();
+        //Fetch SMTP code and possible error code explanation
+        $matches = [];
+        if (preg_match('/^([\d]{3})[ -](?:([\d]\\.[\d]\\.[\d]{1,2}) )?/', $this->last_reply, $matches)) {
+            $code = (int) $matches[1];
+            $code_ex = (count($matches) > 2 ? $matches[2] : null);
+            //Cut off error code from each response line
+            $detail = preg_replace(
+                "/{$code}[ -]" .
+                ($code_ex ? str_replace('.', '\\.', $code_ex) . ' ' : '') . '/m',
+                '',
+                $this->last_reply
+            );
+        } else {
+            //Fall back to simple parsing if regex fails
+            $code = (int) substr($this->last_reply, 0, 3);
+            $code_ex = null;
+            $detail = substr($this->last_reply, 4);
+        }
+
+        $this->edebug('SERVER -> CLIENT: ' . $this->last_reply, self::DEBUG_SERVER);
+
+        if (!in_array($code, (array) $expect, true)) {
+            $this->setError(
+                "$command command failed",
+                $detail,
+                $code,
+                $code_ex
+            );
+            $this->edebug(
+                'SMTP ERROR: ' . $this->error['error'] . ': ' . $this->last_reply,
+                self::DEBUG_CLIENT
+            );
+
+            return false;
+        }
+
+        //Don't clear the error store when using keepalive
+        if ($command !== 'RSET') {
+            $this->setError('');
+        }
+
+        return true;
+    }
+
+    /**
+     * Send an SMTP SAML command.
+     * Starts a mail transaction from the email address specified in $from.
+     * Returns true if successful or false otherwise. If True
+     * the mail transaction is started and then one or more recipient
+     * commands may be called followed by a data command. This command
+     * will send the message to the users terminal if they are logged
+     * in and send them an email.
+     * Implements RFC 821: SAML <SP> FROM:<reverse-path> <CRLF>.
+     *
+     * @param string $from The address the message is from
+     *
+     * @return bool
+     */
+    public function sendAndMail($from)
+    {
+        return $this->sendCommand('SAML', "SAML FROM:$from", 250);
+    }
+
+    /**
+     * Send an SMTP VRFY command.
+     *
+     * @param string $name The name to verify
+     *
+     * @return bool
+     */
+    public function verify($name)
+    {
+        return $this->sendCommand('VRFY', "VRFY $name", [250, 251]);
+    }
+
+    /**
+     * Send an SMTP NOOP command.
+     * Used to keep keep-alives alive, doesn't actually do anything.
+     *
+     * @return bool
+     */
+    public function noop()
+    {
+        return $this->sendCommand('NOOP', 'NOOP', 250);
+    }
+
+    /**
+     * Send an SMTP TURN command.
+     * This is an optional command for SMTP that this class does not support.
+     * This method is here to make the RFC821 Definition complete for this class
+     * and _may_ be implemented in future.
+     * Implements from RFC 821: TURN <CRLF>.
+     *
+     * @return bool
+     */
+    public function turn()
+    {
+        $this->setError('The SMTP TURN command is not implemented');
+        $this->edebug('SMTP NOTICE: ' . $this->error['error'], self::DEBUG_CLIENT);
+
+        return false;
+    }
+
+    /**
+     * Send raw data to the server.
+     *
+     * @param string $data    The data to send
+     * @param string $command Optionally, the command this is part of, used only for controlling debug output
+     *
+     * @return int|bool The number of bytes sent to the server or false on error
+     */
+    public function client_send($data, $command = '')
+    {
+        //If SMTP transcripts are left enabled, or debug output is posted online
+        //it can leak credentials, so hide credentials in all but lowest level
+        if (
+            self::DEBUG_LOWLEVEL > $this->do_debug &&
+            in_array($command, ['User & Password', 'Username', 'Password'], true)
+        ) {
+            $this->edebug('CLIENT -> SERVER: [credentials hidden]', self::DEBUG_CLIENT);
+        } else {
+            $this->edebug('CLIENT -> SERVER: ' . $data, self::DEBUG_CLIENT);
+        }
+        set_error_handler([$this, 'errorHandler']);
+        $result = fwrite($this->smtp_conn, $data);
+        restore_error_handler();
+
+        return $result;
+    }
+
+    /**
+     * Get the latest error.
+     *
+     * @return array
+     */
+    public function getError()
+    {
+        return $this->error;
+    }
+
+    /**
+     * Get SMTP extensions available on the server.
+     *
+     * @return array|null
+     */
+    public function getServerExtList()
+    {
+        return $this->server_caps;
+    }
+
+    /**
+     * Get metadata about the SMTP server from its HELO/EHLO response.
+     * The method works in three ways, dependent on argument value and current state:
+     *   1. HELO/EHLO has not been sent - returns null and populates $this->error.
+     *   2. HELO has been sent -
+     *     $name == 'HELO': returns server name
+     *     $name == 'EHLO': returns boolean false
+     *     $name == any other string: returns null and populates $this->error
+     *   3. EHLO has been sent -
+     *     $name == 'HELO'|'EHLO': returns the server name
+     *     $name == any other string: if extension $name exists, returns True
+     *       or its options (e.g. AUTH mechanisms supported). Otherwise returns False.
+     *
+     * @param string $name Name of SMTP extension or 'HELO'|'EHLO'
+     *
+     * @return string|bool|null
+     */
+    public function getServerExt($name)
+    {
+        if (!$this->server_caps) {
+            $this->setError('No HELO/EHLO was sent');
+
+            return null;
+        }
+
+        if (!array_key_exists($name, $this->server_caps)) {
+            if ('HELO' === $name) {
+                return $this->server_caps['EHLO'];
+            }
+            if ('EHLO' === $name || array_key_exists('EHLO', $this->server_caps)) {
+                return false;
+            }
+            $this->setError('HELO handshake was used; No information about server extensions available');
+
+            return null;
+        }
+
+        return $this->server_caps[$name];
+    }
+
+    /**
+     * Get the last reply from the server.
+     *
+     * @return string
+     */
+    public function getLastReply()
+    {
+        return $this->last_reply;
+    }
+
+    /**
+     * Read the SMTP server's response.
+     * Either before eof or socket timeout occurs on the operation.
+     * With SMTP we can tell if we have more lines to read if the
+     * 4th character is '-' symbol. If it is a space then we don't
+     * need to read anything else.
+     *
+     * @return string
+     */
+    protected function get_lines()
+    {
+        //If the connection is bad, give up straight away
+        if (!is_resource($this->smtp_conn)) {
+            return '';
+        }
+        $data = '';
+        $endtime = 0;
+        stream_set_timeout($this->smtp_conn, $this->Timeout);
+        if ($this->Timelimit > 0) {
+            $endtime = time() + $this->Timelimit;
+        }
+        $selR = [$this->smtp_conn];
+        $selW = null;
+        while (is_resource($this->smtp_conn) && !feof($this->smtp_conn)) {
+            //Must pass vars in here as params are by reference
+            //solution for signals inspired by https://github.com/symfony/symfony/pull/6540
+            set_error_handler([$this, 'errorHandler']);
+            $n = stream_select($selR, $selW, $selW, $this->Timelimit);
+            restore_error_handler();
+
+            if ($n === false) {
+                $message = $this->getError()['detail'];
+
+                $this->edebug(
+                    'SMTP -> get_lines(): select failed (' . $message . ')',
+                    self::DEBUG_LOWLEVEL
+                );
+
+                //stream_select returns false when the `select` system call is interrupted
+                //by an incoming signal, try the select again
+                if (stripos($message, 'interrupted system call') !== false) {
+                    $this->edebug(
+                        'SMTP -> get_lines(): retrying stream_select',
+                        self::DEBUG_LOWLEVEL
+                    );
+                    $this->setError('');
+                    continue;
+                }
+
+                break;
+            }
+
+            if (!$n) {
+                $this->edebug(
+                    'SMTP -> get_lines(): select timed-out in (' . $this->Timelimit . ' sec)',
+                    self::DEBUG_LOWLEVEL
+                );
+                break;
+            }
+
+            //Deliberate noise suppression - errors are handled afterwards
+            $str = @fgets($this->smtp_conn, self::MAX_REPLY_LENGTH);
+            $this->edebug('SMTP INBOUND: "' . trim($str) . '"', self::DEBUG_LOWLEVEL);
+            $data .= $str;
+            //If response is only 3 chars (not valid, but RFC5321 S4.2 says it must be handled),
+            //or 4th character is a space or a line break char, we are done reading, break the loop.
+            //String array access is a significant micro-optimisation over strlen
+            if (!isset($str[3]) || $str[3] === ' ' || $str[3] === "\r" || $str[3] === "\n") {
+                break;
+            }
+            //Timed-out? Log and break
+            $info = stream_get_meta_data($this->smtp_conn);
+            if ($info['timed_out']) {
+                $this->edebug(
+                    'SMTP -> get_lines(): stream timed-out (' . $this->Timeout . ' sec)',
+                    self::DEBUG_LOWLEVEL
+                );
+                break;
+            }
+            //Now check if reads took too long
+            if ($endtime && time() > $endtime) {
+                $this->edebug(
+                    'SMTP -> get_lines(): timelimit reached (' .
+                    $this->Timelimit . ' sec)',
+                    self::DEBUG_LOWLEVEL
+                );
+                break;
+            }
+        }
+
+        return $data;
+    }
+
+    /**
+     * Enable or disable VERP address generation.
+     *
+     * @param bool $enabled
+     */
+    public function setVerp($enabled = false)
+    {
+        $this->do_verp = $enabled;
+    }
+
+    /**
+     * Get VERP address generation mode.
+     *
+     * @return bool
+     */
+    public function getVerp()
+    {
+        return $this->do_verp;
+    }
+
+    /**
+     * Set error messages and codes.
+     *
+     * @param string $message      The error message
+     * @param string $detail       Further detail on the error
+     * @param string $smtp_code    An associated SMTP error code
+     * @param string $smtp_code_ex Extended SMTP code
+     */
+    protected function setError($message, $detail = '', $smtp_code = '', $smtp_code_ex = '')
+    {
+        $this->error = [
+            'error' => $message,
+            'detail' => $detail,
+            'smtp_code' => $smtp_code,
+            'smtp_code_ex' => $smtp_code_ex,
+        ];
+    }
+
+    /**
+     * Set debug output method.
+     *
+     * @param string|callable $method The name of the mechanism to use for debugging output, or a callable to handle it
+     */
+    public function setDebugOutput($method = 'echo')
+    {
+        $this->Debugoutput = $method;
+    }
+
+    /**
+     * Get debug output method.
+     *
+     * @return string
+     */
+    public function getDebugOutput()
+    {
+        return $this->Debugoutput;
+    }
+
+    /**
+     * Set debug output level.
+     *
+     * @param int $level
+     */
+    public function setDebugLevel($level = 0)
+    {
+        $this->do_debug = $level;
+    }
+
+    /**
+     * Get debug output level.
+     *
+     * @return int
+     */
+    public function getDebugLevel()
+    {
+        return $this->do_debug;
+    }
+
+    /**
+     * Set SMTP timeout.
+     *
+     * @param int $timeout The timeout duration in seconds
+     */
+    public function setTimeout($timeout = 0)
+    {
+        $this->Timeout = $timeout;
+    }
+
+    /**
+     * Get SMTP timeout.
+     *
+     * @return int
+     */
+    public function getTimeout()
+    {
+        return $this->Timeout;
+    }
+
+    /**
+     * Reports an error number and string.
+     *
+     * @param int    $errno   The error number returned by PHP
+     * @param string $errmsg  The error message returned by PHP
+     * @param string $errfile The file the error occurred in
+     * @param int    $errline The line number the error occurred on
+     */
+    protected function errorHandler($errno, $errmsg, $errfile = '', $errline = 0)
+    {
+        $notice = 'Connection failed.';
+        $this->setError(
+            $notice,
+            $errmsg,
+            (string) $errno
+        );
+        $this->edebug(
+            "$notice Error #$errno: $errmsg [$errfile line $errline]",
+            self::DEBUG_CONNECTION
+        );
+    }
+
+    /**
+     * Extract and return the ID of the last SMTP transaction based on
+     * a list of patterns provided in SMTP::$smtp_transaction_id_patterns.
+     * Relies on the host providing the ID in response to a DATA command.
+     * If no reply has been received yet, it will return null.
+     * If no pattern was matched, it will return false.
+     *
+     * @return bool|string|null
+     */
+    protected function recordLastTransactionID()
+    {
+        $reply = $this->getLastReply();
+
+        if (empty($reply)) {
+            $this->last_smtp_transaction_id = null;
+        } else {
+            $this->last_smtp_transaction_id = false;
+            foreach ($this->smtp_transaction_id_patterns as $smtp_transaction_id_pattern) {
+                $matches = [];
+                if (preg_match($smtp_transaction_id_pattern, $reply, $matches)) {
+                    $this->last_smtp_transaction_id = trim($matches[1]);
+                    break;
+                }
+            }
+        }
+
+        return $this->last_smtp_transaction_id;
+    }
+
+    /**
+     * Get the queue/transaction ID of the last SMTP transaction
+     * If no reply has been received yet, it will return null.
+     * If no pattern was matched, it will return false.
+     *
+     * @return bool|string|null
+     *
+     * @see recordLastTransactionID()
+     */
+    public function getLastTransactionID()
+    {
+        return $this->last_smtp_transaction_id;
+    }
+}

+ 0 - 0
Parsedown.php


+ 0 - 0
ParsedownExtra.php


+ 1 - 0
all_subscribers.php

@@ -0,0 +1 @@
+<php

+ 12 - 0
archive/202003.md

@@ -0,0 +1,12 @@
+[LAMDWIKIPOST 20200306080514; VER 20200306080514; HASI 20211224061904.jpg; ]
+
+Gun Down![Images](images/20211224061904.jpg)
+
+[LAMDWIKIPOST 20200306080514; VER 20231008085054; HASI 20211224061904.jpg; ]
+
+Gun Down![Images](images/20211224061904.jpg)
+
+
+
+{slides}
+

+ 162 - 0
archive/202110.md

@@ -0,0 +1,162 @@
+[LAMDWIKIPOST 20211027112601; VER 20211027112601; ]
+
+# 大陆
+
+_短篇系列 2020年10月_
+
+## 1
+
+我从来没有去过大地之母雕塑。我不知道为什么所有来旅游的人都要爬上那个土破,哼吃哼吃地去它脚下,合影留念,然后继续拍着队往停车场的旅游大巴走,对我来说这件事情没有任何意义。柯总是说我不在乎习俗,整得好像没有上过大地之母就不配当北方平原的人似的,好吧其实我是北方平原的狼,但这件事跟这个没有关系,当然某种程度上也有,因为社会常规有他存在的范畴,这不重要。大地是一种和生命共同存在的东西,当你把一群一米七八的人丢到一个不知道掏成什么样的空心结构支撑起来的五十层楼高的女战士的脚下,这个就不是大地之母,应该叫上地之母。我一直跟柯讲这个问题,这个蛋疼的雕塑本身就应该作为城市的背景,而不是用来让朝觐它的人感到泰山压顶般的震撼,大地母亲不需要凌驾于生物之上……
+
+{read_more}
+
+最终都是借口,不过我好像也不能找出不是借口的借口,我就是不想去,随便了。
+
+“我不知道为什么就你对那玩意意见这么大。但我觉得她的形态就很不错啊。”柯一只手揣在兜里,把衣服前部扯得下坠,经常拉扯的部位在黑色的面料上留下两个柔和的浅色道道。那是我几年前的帽衫,现在他穿基本合适。他把手拿出来揉了揉鼻子,然后又揣回去,插在左侧,因为右侧的兜好象有个洞,所以他好像也不爱用右边那一侧。
+
+“不去,有什么意思,它完全就不是人的尺度,为标志而标志。”
+
+“你就是觉得大家都去所以你不去。”他吊着公交车的吊环,歪着脑袋朝我左转右转,翻白眼的样子,车到站了。
+
+“对,没错,那又怎么样。它没有什么吸引我的地方,以至于我需要专门换乘一趟公交车,买一个门票,然后再坐大巴车到它脚下,然后爬山,然后拍照,然后发现我的照相机广角不够以至于我不能在它脚下合理地将我和它放在一个画面里,然后浪费一天时间。”
+
+我本来想说就像挤这个该死的公交车一样,不过只是因为还在车上。
+
+“但之后你就可以说你去看了大地之母啊。”
+
+“这有什么好骄傲的,好像我没去过,就不能编造一个牛逼的故事让你们相信吗?再者说,你跑去之后看到了啥,除了人、自拍杆、三脚架,还有啥,偶尔可能有个风筝。在那么近的地方,你甚至不能很好地看见整个身体的比例,甚至看不到她的额头,所以这是为了什么?”
+
+很快车又开了,减震的钢板嘎吱嘎吱地弹,这段路被工地的大车压得碎裂,连发动机似乎都随着车辆的抖动而有规律地敲击着那些稍有晃动就会发生机械干涉的部件。这时候车里一定不能有人抱着水杯或者鱼缸,不然就会浪得到处都是。柯挤着我,然后我又挤着别的乘客,之后又反向挤回来。好事在于,今天并没有下雨,不然公交车上的人更多,而且它们中有一半都会穿着雨衣或者拿着还没有收好的折伞,再加上风从推拉窗和轨道之间不用肉眼都可见的空隙吹进来,所以但凡不下雨,我认为目前的公交还能凑合用,否则我就会直接穿防水的衣服和靴子上路,大概就像刚才路过的林业工人那样。噢,它们去过那个什么大地的母亲那儿吗?
+
+“外国的人到我们这来就是为了看这个。”
+
+“啊,可惜我不是外国人。就算我是,我也不会去。我会站在西边的山坡上,或者电信大楼顶上的餐厅里,这样什么都看得见,比例也合适,看上去就在市区高处一点,非常协调。”
+
+“你在锅沿生活这么几年了都没去过大地之母。”
+
+“我在九香生活了这么十几年了都没去过港口遗址呢。没意思。”
+
+“你这个人好犟啊。”
+
+“我不确定这句话是褒义还是贬义。”
+
+“这句话只是说你谁的话都不听。”
+
+“不,我听很多人的话,包括你的,只是我选择是否让我的行为匹配我听到的话而已。”
+
+柯用手肘拐了一下我的腰。
+
+“那好吧,听着,如果你一定要让我按照你所说的去做,那么你有两个选择,第一个选择是把我的神经与《超脑》里面的主机连接起来,让他灌输给我你那些无聊的思想和意志,然后我就会遵循指令完成必要的动作;第二个选择是,趁我洗澡的时候把我的手枪偷走,躲在门后,等我回来就扑到我的背上,箍住我的喉咙,然后把枪口按在我的太阳穴上愤怒地大吼‘亚!你今天要是再不把你的袜子都给我洗了我就打爆你的狗头!’。我认为这两种方法效果应该都不错,但估计对你来说都需要一定时间的练习才能够比较熟练地运用。”
+
+很显然,柯应该更喜欢第二种,因为从他的表情来看,离我的脸上立刻挨上一拳只差我再顶一个字的嘴。柯有点小生气时的样子看上去十分有意思。他的两只耳朵会非常用力地扣在肩膀上,随着视线的移动还会拍打几下。以前我试过在这种时候揪他耳朵末端那片半透明的肉,他很流畅地甩开了我的手,我仍然难以相信操纵耳朵的肌肉能这么灵活而有力,那动作看上去就像是从动画片里蹦出来的,而且还不能是任何动画片,至少都得是跳跳灯的级别。他仍然只是用手肘锤了下我,然后就开始抠手里那张老的公交卡,那个圆角两侧已经出现了浅浅的凹槽。
+
+“你愿意陪我练习第二种技术吗?”
+
+“当然,里面有很多技术要点。”
+
+“比如呢?”
+
+“如何能准确地蹦到我脖子的位置、还有怎么样恰到好处地使用手臂限制喉咙,还要稳住自己不要从我背上掉下去,这些都比较关键。如果任何一步失败了,局势就会反转。”
+
+“万一我不能成功地偷走你的手枪呢?这步有什么技术要点?”
+
+“我觉得这个问题不大,鉴于你总是可以很容易地利用我的信任。”
+
+他笑的时候耳朵会向后面和两侧翘起来一些,或许这就是他表示“摇尾巴”动作的方法,有时可能也代表“吐舌头”,我还没有完全搞清楚这两者在动作表现上的细微差别。
+
+我仍然不会去那个雕像那里,至少在不出现第二种情况的时候。它为什么在哪里?在那里多久了?谁修的?干嘛要修这么个东西?这么长时间以来似乎从来没有听人讨论到,这充分说明去那里的人基本上都不关心它的文化含义。车没多久就又堵在了河边。红绿灯一定是世界上最没有用的发明,因为凡是你希望红绿车发挥它应有的效果时,路口都会额外出现一名警察来指挥那反正也挪不开地方的路口。但凡这个时候,四个方向的行人就决定自己动脚解决问题,因为与其都停着,不如有人还可以走着赶路。之后整个马路就都变成了人行道,晚高峰的结束时间取决于路面上的行人到家的比例,剩下的人只能看着尾灯干等着。虽然是傍晚了,地面向天上散发的温度仍然把车灯扰动地一晃一晃的。而在坡路的顶部,她就不偏不倚出现了。大地之母,她的轮廓举着长剑向南方行进,长裙在热风中飘逸。
+
+[LAMDWIKIPOST 20211027112612; VER 20211027112612; HASP 20211028074051; ]
+
+# 大陆
+
+[和Alex一起射精](20211028074051)
+
+_短篇系列 2020年10月_
+
+## 1
+
+我从来没有去过大地之母雕塑。我不知道为什么所有来旅游的人都要爬上那个土破,哼吃哼吃地去它脚下,合影留念,然后继续拍着队往停车场的旅游大巴走,对我来说这件事情没有任何意义。柯总是说我不在乎习俗,整得好像没有上过大地之母就不配当北方平原的人似的,好吧其实我是北方平原的狼,但这件事跟这个没有关系,当然某种程度上也有,因为社会常规有他存在的范畴,这不重要。大地是一种和生命共同存在的东西,当你把一群一米七八的人丢到一个不知道掏成什么样的空心结构支撑起来的五十层楼高的女战士的脚下,这个就不是大地之母,应该叫上地之母。我一直跟柯讲这个问题,这个蛋疼的雕塑本身就应该作为城市的背景,而不是用来让朝觐它的人感到泰山压顶般的震撼,大地母亲不需要凌驾于生物之上……
+
+{read_more}
+
+最终都是借口,不过我好像也不能找出不是借口的借口,我就是不想去,随便了。
+
+“我不知道为什么就你对那玩意意见这么大。但我觉得她的形态就很不错啊。”柯一只手揣在兜里,把衣服前部扯得下坠,经常拉扯的部位在黑色的面料上留下两个柔和的浅色道道。那是我几年前的帽衫,现在他穿基本合适。他把手拿出来揉了揉鼻子,然后又揣回去,插在左侧,因为右侧的兜好象有个洞,所以他好像也不爱用右边那一侧。
+
+“不去,有什么意思,它完全就不是人的尺度,为标志而标志。”
+
+“你就是觉得大家都去所以你不去。”他吊着公交车的吊环,歪着脑袋朝我左转右转,翻白眼的样子,车到站了。
+
+“对,没错,那又怎么样。它没有什么吸引我的地方,以至于我需要专门换乘一趟公交车,买一个门票,然后再坐大巴车到它脚下,然后爬山,然后拍照,然后发现我的照相机广角不够以至于我不能在它脚下合理地将我和它放在一个画面里,然后浪费一天时间。”
+
+我本来想说就像挤这个该死的公交车一样,不过只是因为还在车上。
+
+“但之后你就可以说你去看了大地之母啊。”
+
+“这有什么好骄傲的,好像我没去过,就不能编造一个牛逼的故事让你们相信吗?再者说,你跑去之后看到了啥,除了人、自拍杆、三脚架,还有啥,偶尔可能有个风筝。在那么近的地方,你甚至不能很好地看见整个身体的比例,甚至看不到她的额头,所以这是为了什么?”
+
+很快车又开了,减震的钢板嘎吱嘎吱地弹,这段路被工地的大车压得碎裂,连发动机似乎都随着车辆的抖动而有规律地敲击着那些稍有晃动就会发生机械干涉的部件。这时候车里一定不能有人抱着水杯或者鱼缸,不然就会浪得到处都是。柯挤着我,然后我又挤着别的乘客,之后又反向挤回来。好事在于,今天并没有下雨,不然公交车上的人更多,而且它们中有一半都会穿着雨衣或者拿着还没有收好的折伞,再加上风从推拉窗和轨道之间不用肉眼都可见的空隙吹进来,所以但凡不下雨,我认为目前的公交还能凑合用,否则我就会直接穿防水的衣服和靴子上路,大概就像刚才路过的林业工人那样。噢,它们去过那个什么大地的母亲那儿吗?
+
+“外国的人到我们这来就是为了看这个。”
+
+“啊,可惜我不是外国人。就算我是,我也不会去。我会站在西边的山坡上,或者电信大楼顶上的餐厅里,这样什么都看得见,比例也合适,看上去就在市区高处一点,非常协调。”
+
+“你在锅沿生活这么几年了都没去过大地之母。”
+
+“我在九香生活了这么十几年了都没去过港口遗址呢。没意思。”
+
+“你这个人好犟啊。”
+
+“我不确定这句话是褒义还是贬义。”
+
+“这句话只是说你谁的话都不听。”
+
+“不,我听很多人的话,包括你的,只是我选择是否让我的行为匹配我听到的话而已。”
+
+柯用手肘拐了一下我的腰。
+
+“那好吧,听着,如果你一定要让我按照你所说的去做,那么你有两个选择,第一个选择是把我的神经与《超脑》里面的主机连接起来,让他灌输给我你那些无聊的思想和意志,然后我就会遵循指令完成必要的动作;第二个选择是,趁我洗澡的时候把我的手枪偷走,躲在门后,等我回来就扑到我的背上,箍住我的喉咙,然后把枪口按在我的太阳穴上愤怒地大吼‘亚!你今天要是再不把你的袜子都给我洗了我就打爆你的狗头!’。我认为这两种方法效果应该都不错,但估计对你来说都需要一定时间的练习才能够比较熟练地运用。”
+
+很显然,柯应该更喜欢第二种,因为从他的表情来看,离我的脸上立刻挨上一拳只差我再顶一个字的嘴。柯有点小生气时的样子看上去十分有意思。他的两只耳朵会非常用力地扣在肩膀上,随着视线的移动还会拍打几下。以前我试过在这种时候揪他耳朵末端那片半透明的肉,他很流畅地甩开了我的手,我仍然难以相信操纵耳朵的肌肉能这么灵活而有力,那动作看上去就像是从动画片里蹦出来的,而且还不能是任何动画片,至少都得是跳跳灯的级别。他仍然只是用手肘锤了下我,然后就开始抠手里那张老的公交卡,那个圆角两侧已经出现了浅浅的凹槽。
+
+“你愿意陪我练习第二种技术吗?”
+
+“当然,里面有很多技术要点。”
+
+“比如呢?”
+
+“如何能准确地蹦到我脖子的位置、还有怎么样恰到好处地使用手臂限制喉咙,还要稳住自己不要从我背上掉下去,这些都比较关键。如果任何一步失败了,局势就会反转。”
+
+“万一我不能成功地偷走你的手枪呢?这步有什么技术要点?”
+
+“我觉得这个问题不大,鉴于你总是可以很容易地利用我的信任。”
+
+他笑的时候耳朵会向后面和两侧翘起来一些,或许这就是他表示“摇尾巴”动作的方法,有时可能也代表“吐舌头”,我还没有完全搞清楚这两者在动作表现上的细微差别。
+
+我仍然不会去那个雕像那里,至少在不出现第二种情况的时候。它为什么在哪里?在那里多久了?谁修的?干嘛要修这么个东西?这么长时间以来似乎从来没有听人讨论到,这充分说明去那里的人基本上都不关心它的文化含义。车没多久就又堵在了河边。红绿灯一定是世界上最没有用的发明,因为凡是你希望红绿车发挥它应有的效果时,路口都会额外出现一名警察来指挥那反正也挪不开地方的路口。但凡这个时候,四个方向的行人就决定自己动脚解决问题,因为与其都停着,不如有人还可以走着赶路。之后整个马路就都变成了人行道,晚高峰的结束时间取决于路面上的行人到家的比例,剩下的人只能看着尾灯干等着。虽然是傍晚了,地面向天上散发的温度仍然把车灯扰动地一晃一晃的。而在坡路的顶部,她就不偏不倚出现了。大地之母,她的轮廓举着长剑向南方行进,长裙在热风中飘逸。
+
+[LAMDWIKIPOST 20211028074051; VER 20211028074051; HASI 20211008070157.jpg 20211028081604.jpg 20211109160348.jpg 20211109155604.jpg; ]
+
+| 图片 | 说明 | 精液浓度 | 火力 | 射速 |
+|-----|-----|-----|-----|-----|
+| ![图片 keep_inline](images/20211008070157.jpg) | 射精吧少年 | EX | EX | 800RPM |
+| ![图片 keep_inline](images/20211028081604.jpg) | 开火吧少年 | EX | EX | 300RPM |
+
+![图片 original](images/20211109160348.jpg)
+
+![图片](images/20211109155604.jpg)
+
+[LAMDWIKIPOST 20211028074051; VER 20231008085021; HASI 20211008070157.jpg 20211028081604.jpg 20211109160348.jpg 20211109155604.jpg; ]
+
+| 图片 | 说明 | 精液浓度 | 火力 | 射速 |
+|-----|-----|-----|-----|-----|
+| ![图片 keep_inline](images/20211008070157.jpg) | 射精吧少年 | EX | EX | 800RPM |
+| ![图片 keep_inline](images/20211028081604.jpg) | 开火吧少年 | EX | EX | 300RPM |
+
+![图片 original](images/20211109160348.jpg)
+
+![图片](images/20211109155604.jpg)
+
+{slides}
+

+ 4 - 0
archive/202111.md

@@ -0,0 +1,4 @@
+[LAMDWIKIPOST 20211109160252; VER 20211109160252; HASI 20211109160245.jpg; ]
+
+![图片](images/20211109160245.jpg)全0。5
+

+ 8 - 0
archive/202201.md

@@ -0,0 +1,8 @@
+[LAMDWIKIPOST 20220111164949; VER 20220111164949; ]
+
+123
+
+[LAMDWIKIPOST 20220111164954; VER 20220111164954; ]
+
+爆射111
+

+ 207 - 0
archive/202202.md

@@ -0,0 +1,207 @@
+[LAMDWIKIPOST 20220220072202; VER 20220220072202; ]
+
+哒哒哒
+
+[LAMDWIKIPOST 20220220072202; VER 20220221134040; ]
+
+pew pew pew
+
+[LAMDWIKIPOST 20220220072202; VER 20220221134051; ]
+
+pew pew pew brrrrt 131
+
+[LAMDWIKIPOST 20220220072202; VER 20220221150320; ]
+
+pew pew pew brrrrt 131 dadaddada
+
+[LAMDWIKIPOST 20220220072202; VER 20220221150823; ]
+
+修改后的历史记录
+
+Modified history
+
+[LAMDWIKIPOST 20220220072202; VER 20220221150942; ]
+
+Modified again?
+
+修改后的历史记录
+
+Modified history
+
+[LAMDWIKIPOST 20220221131557; VER 20220221131557; ]
+
+哒哒哒哒哒 123 111111 31231235475663525 13213123123123
+
+[LAMDWIKIPOST 20220221131557; VER 20220221133943; ]
+
+哒哒哒哒哒
+
+[LAMDWIKIPOST 20220221131557; VER 20220221133956; ]
+
+哒哒哒哒哒 123
+
+[LAMDWIKIPOST 20220221131557; VER 20220221134008; INTO 20220220072202V20220223150411; ]
+
+哒哒哒哒哒 123 dsfsafasfsdfas
+
+[LAMDWIKIPOST 20220222135527; VER 20220222135527; ]
+
+
+
+[LAMDWIKIPOST 20220222135527; VER 20220222135642; ]
+
+超精加特林猛烈开火
+
+[LAMDWIKIPOST 20220222135527; VER 20220222140019; ]
+
+超精加特林猛烈扫射
+
+[LAMDWIKIPOST 20220222135527; VER 20220222151601; ]
+
+超精加特林猛烈扫射2113
+
+[LAMDWIKIPOST 20220222135527; VER 20220223104003; ]
+
+超精加特林主炮爆射
+
+[LAMDWIKIPOST 20220222135527; VER 20220223104716; ]
+
+超精加特林主炮爆射
+
+[LAMDWIKIPOST 20220222135527; VER 20220223105059; FROM 20220223073321; HASTAG 20220223073321; ]
+
+超精加特林主炮爆射
+
+//20220223073321
+修改后的多改写
+
+[LAMDWIKIPOST 20220222135527; VER 20220223111025; INTO 20220220072202V20220223150411; HASTAG 20220223073321; ]
+
+超精加特林主炮爆射
+
+//20220223073321
+修改后的多改写
+
+1231231313123
+
+[LAMDWIKIPOST 20220223073321; VER 20220223101740; ]
+
+修改后的多改写 再改改
+
+[LAMDWIKIPOST 20220223073321; VER 20220223104716; INTO 20220222135527V20220223105059; ]
+
+修改后的多改写
+
+[LAMDWIKIPOST 20220223105336; VER 20220223105336; ]
+
+12312312312313
+
+[LAMDWIKIPOST 20220223131403; VER 20220223131403; ]
+
+qqqq
+
+[LAMDWIKIPOST 20220223131403; VER 20220223132206; FROM 20220223131405; INTO 20220220072202V20220223150411; HASTAG 20220223131405; ]
+
+qqqq
+
+//20220223131405
+aaaa
+
+[LAMDWIKIPOST 20220223131405; VER 20220223131405; INTO 20220223131403V20220223132206; ]
+
+aaaa
+
+[LAMDWIKIPOST 20220223150057; VER 20220223150057; ]
+
+Outstanding
+
+[LAMDWIKIPOST 20220223150057; VER 20220223154825; FROM 20220223153020 20220223153021; HASTAG 20220223153020 20220223153021 20220223153024 20220223153026; ]
+
+Outstanding
+
+//20220223153020
+1
+
+//20220223153021
+2
+
+//20220223153024
+4
+
+//20220223153026
+5
+
+[LAMDWIKIPOST 20220223150057; VER 20220223154901; FROM 20220223153023; HASTAG 20220223153020 20220223153021 20220223153024 20220223153026 20220223153023; ]
+
+Outstanding
+
+//20220223153020
+1
+
+//20220223153021
+2
+
+//20220223153024
+4
+
+//20220223153026
+5
+
+//20220223153023
+3 dadada
+
+[LAMDWIKIPOST 20220223150057; VER 20220223155301; HASTAG 20220223153020 20220223153021 20220223153024 20220223153026 20220223153023; ]
+
+Outstanding
+
+//20220223153020
+1
+
+//20220223153021
+2
+
+//20220223153024
+4
+
+//20220223153026
+5
+
+//20220223153023
+3 dadada
+
+modify
+
+[LAMDWIKIPOST 20220223153020; VER 20220223153020; INTO 20220223150057V20220223154825; ]
+
+1
+
+[LAMDWIKIPOST 20220223153021; VER 20220223153021; ]
+
+2
+
+[LAMDWIKIPOST 20220223153021; VER 20220223154458; FROM 20220223153024 20220223153026; INTO 20220223150057V20220223154825; HASTAG 20220223153024 20220223153026; ]
+
+2
+
+//20220223153024
+4
+
+//20220223153026
+5
+
+[LAMDWIKIPOST 20220223153023; VER 20220223153023; ]
+
+3
+
+[LAMDWIKIPOST 20220223153023; VER 20220223154816; INTO 20220223150057V20220223154901; ]
+
+3 dadada
+
+[LAMDWIKIPOST 20220223153024; VER 20220223153024; INTO 20220223153021V20220223154458; ]
+
+4
+
+[LAMDWIKIPOST 20220223153026; VER 20220223153026; INTO 20220223153021V20220223154458; ]
+
+5
+

+ 197 - 0
archive/202203.md

@@ -0,0 +1,197 @@
+[LAMDWIKIPOST 20220305061340; VER 20220305061340; ]
+
+射精!
+
+[LAMDWIKIPOST 20220305061340; VER 20220305061519; MTHREAD 20220305061355 | 20220305061340 20220305061347 20220305061403;]
+
+
+
+[LAMDWIKIPOST 20220305061340; VER 20220305061606; HASI 20211224061904.jpg; ]
+
+射精!![Images](images/20211224061904.jpg)
+
+[LAMDWIKIPOST 20220305061340; VER 20220305061703; FROM 20220305061355 20220305061403; HASP 20220305061403 20220305061414; HASI 20211224061904.jpg 20211109160348.jpg; HASTAG 20220305061355 20220305061414 20220305061613 20220305061403; ]
+
+射精!![Images](images/20211224061904.jpg)
+
+//20220305061355
+绝射
+
+//20220305061414
+鸡巴射
+
+[123](20220305061403)
+
+//20220305061613
+开火!![Images](images/20211109160348.jpg)
+
+//20220305061403
+爆射
+
+[333](20220305061414)
+
+[LAMDWIKIPOST 20220305061340; VER 20220305061842; FROM 20220305061347; HASP 20220305061403 20220305061414; HASI 20211224061904.jpg 20211109160348.jpg; HASTAG 20220305061355 20220305061414 20220305061613 20220305061403 20220305061347; ]
+
+射精!![Images](images/20211224061904.jpg)
+
+//20220305061355
+绝射
+
+//20220305061414
+鸡巴射
+
+[123](20220305061403)
+
+//20220305061613
+开火!![Images](images/20211109160348.jpg)
+
+//20220305061403
+爆射
+
+[333](20220305061414)
+
+//20220305061347
+超射
+
+[LAMDWIKIPOST 20220305061340; VER 20220314163334; HASP 20220305061403 20220305061414; HASI 20211224061904.jpg 20211109160348.jpg; HASTAG 20220305061355 20220305061414 20220305061613 20220305061403 20220305061347; ]
+
+射精!![Images](images/20211224061904.jpg)
+
+//20220305061355
+绝射
+
+//20220305061414
+鸡巴射
+
+[123](20220305061403)
+
+//20220305061613
+开火!![Images](images/20211109160348.jpg)
+
+{reversed}
+
+//20220305061403
+爆射
+
+[333](20220305061414)
+
+//20220305061347
+超射
+
+[LAMDWIKIPOST 20220305061340; VER 20220728105225; HASP 20220305061403 20220305061414; HASI 20211224061904.jpg 20211109160348.jpg; HASTAG 20220305061355 20220305061414 20220305061613 20220305061403 20220305061347; ]
+
+@射精
+
+射精!![Images](images/20211224061904.jpg)
+
+//20220305061355
+绝射
+
+//20220305061414
+鸡巴射
+
+[123](20220305061403)
+
+//20220305061613
+开火!![Images](images/20211109160348.jpg)
+
+//20220305061403
+爆射
+
+[333](20220305061414)
+
+//20220305061347
+超射
+
+[LAMDWIKIPOST 20220305061340; VER 20231008085217; HASP 20220305061403 20220305061414; HASI 20211224061904.jpg 20211109160348.jpg; HASTAG 20220305061355 20220305061414 20220305061613 20220305061403 20220305061347; ]
+
+射精!![Images](images/20211224061904.jpg)
+
+//20220305061355
+绝射
+
+//20220305061414
+鸡巴射
+
+[123](20220305061403)
+
+//20220305061613
+开火!![Images](images/20211109160348.jpg)
+
+{slides}
+
+//20220305061403
+爆射
+
+[333](20220305061414)
+
+//20220305061347
+超射
+
+[LAMDWIKIPOST 20220305061347; VER 20220305061347; INTO 20220305061340V20220305061842; ]
+
+超射
+
+[LAMDWIKIPOST 20220305061355; VER 20220305061355; ]
+
+绝射
+
+[LAMDWIKIPOST 20220305061355; VER 20220305061440; FROM 20220305061414; HASP 20220305061403; HASTAG 20220305061414; ]
+
+绝射
+
+//20220305061414
+鸡巴射
+
+[123](20220305061403)
+
+[LAMDWIKIPOST 20220305061355; VER 20220305061634; FROM 20220305061613; INTO 20220305061340V20220305061703; HASP 20220305061403; HASI 20211109160348.jpg; HASTAG 20220305061414 20220305061613; ]
+
+绝射
+
+//20220305061414
+鸡巴射
+
+[123](20220305061403)
+
+//20220305061613
+开火!![Images](images/20211109160348.jpg)
+
+[LAMDWIKIPOST 20220305061403; VER 20220305061403; ]
+
+爆射
+
+[LAMDWIKIPOST 20220305061403; VER 20220305061506; INTO 20220305061340V20220305061703; HASP 20220305061414; ]
+
+爆射
+
+[333](20220305061414)
+
+[LAMDWIKIPOST 20220305061414; VER 20220305061414; ]
+
+鸡巴射
+
+[LAMDWIKIPOST 20220305061414; VER 20220305061428; INTO 20220305061355V20220305061440; HASP 20220305061403; ]
+
+鸡巴射
+
+[123](20220305061403)
+
+[LAMDWIKIPOST 20220305061613; VER 20220305061613; ]
+
+开火!
+
+[LAMDWIKIPOST 20220305061613; VER 20220305061622; INTO 20220305061355V20220305061634; HASI 20211109160348.jpg; ]
+
+开火!![Images](images/20211109160348.jpg)
+
+[LAMDWIKIPOST 20220306080501; VER 20220306080501; HASI 20211109160348.jpg 20211028081604.jpg 20211224061904.jpg; ]
+
+Fuck you![Images](images/20211109160348.jpg)![Images](images/20211028081604.jpg)![Images](images/20211224061904.jpg)
+
+[LAMDWIKIPOST 20220306080501; VER 20231008085140; HASI 20211109160348.jpg 20211028081604.jpg 20211224061904.jpg; ]
+
+Fuck you![Images](images/20211109160348.jpg)![Images](images/20211028081604.jpg)![Images](images/20211224061904.jpg)
+
+{slides}
+

+ 62 - 0
archive/202205.md

@@ -0,0 +1,62 @@
+[LAMDWIKIPOST 20220508111437; VER 20220508111437; ]
+
+# Hyper en|zh 超高速射精 zh|en ejaculation
+
+zh|en
+
+who wants to shoot some loads?
+
+[LAMDWIKIPOST 20220508111437; VER 20220508113102; ]
+
+# Hyper en|zh 超高速射精 zh|en ejaculation
+
+zh|en
+
+who wants to shoot some loads?
+
+en|zh
+
+我要射爆!
+
+[LAMDWIKIPOST 20220508111437; VER 20220508120622; ]
+
+# Hyper en|zh 超高速射精 zh|en ejaculation
+
+any|en
+
+who wants to shoot some loads?
+
+en|zh
+
+我要射爆!
+
+[LAMDWIKIPOST 20220522154046; VER 20220522154046; HASI 20211109160323.jpg; ]
+
+123123123132131233
+
+![图片](images/20211109160323.jpg)
+
+- here: 23,34 images/20211109160245.jpg
+
+3
+
+[LAMDWIKIPOST 20220522154046; VER 20220522161909; HASI 20211109160323.jpg; ]
+
+123123123132131233
+
+![图片](images/20211109160323.jpg)
+
+- here: 23,34 images/20211109160245.jpg
+
+3
+
+[LAMDWIKIPOST 20220522154046; VER 20220522161956; HASI 20211109160323.jpg; ]
+
+123123123132131233
+
+![图片](images/20211109160323.jpg)
+
+- here: 10,34 images/20211109160245.jpg
+
+3
+

+ 28 - 0
archive/202206.md

@@ -0,0 +1,28 @@
+[LAMDWIKIPOST 20220624091130; VER 20220624091130; ]
+
+123 ![图片](images/20220624090942.mp4)
+
+[LAMDWIKIPOST 20220624091130; VER 20220624091447; ]
+
+123 ![图片](images/20220624090942.mp4)
+
+[LAMDWIKIPOST 20220624091130; VER 20220624140440; ]
+
+射精机关枪 ![图片](images/20220624090942.mp4)
+
+[LAMDWIKIPOST 20220624104248; VER 20220624104248; ]
+
+- 123 eh|zh 456
+
+[LAMDWIKIPOST 20220624104248; VER 20220624104257; ]
+
+- 123 en|zh 456
+
+[LAMDWIKIPOST 20220624104248; VER 20220624104458; HASP 20220402213054; ]
+
+- [可运用在关键帧动画物体上的全自动发射物效果工具](20220402213054),基于Blender。 zh|en [Keyable fully-automatic projectile launching effect tool](20220402213054), based on Blender.
+
+[LAMDWIKIPOST 20220624104248; VER 20220624105047; HASP 20220402213054; ]
+
+- [可运用在关键帧动画物体上的全自动发射物效果工具](20220402213054) zh|en [Keyable fully-automatic projectile launching effect tool](20220402213054)
+

+ 3024 - 0
archive/202207.md

@@ -0,0 +1,3024 @@
+[LAMDWIKIPOST 20220728104251; VER 20220728104251; ]
+
+{interesting 123}
+
+#啊
+
+[LAMDWIKIPOST 20220728104251; VER 20220728105049; MTHREAD 20220728104808 | 20220728104251;]
+
+
+
+[LAMDWIKIPOST 20220728104808; VER 20220728104808; ]
+
+{interesting 123}
+
+#啊
+
+[LAMDWIKIPOST 20220728104808; VER 20220728105101; ]
+
+# 有趣吗
+
+[LAMDWIKIPOST 20220728104808; VER 20220729041432; ]
+
+# 有趣吗
+
+123
+
+## 额
+
+[LAMDWIKIPOST 20220729043213; VER 20220729043213; ]
+
+射精
+
+射精
+
+射精
+
+射精
+
+射射射射射射射射
+
+[LAMDWIKIPOST 20220729043213; VER 20220729043250; ]
+
+射精
+
+射精
+
+射精
+
+射精
+
+射射射射射射射射
+
+{wide}
+
+[LAMDWIKIPOST 20220729043419; VER 20220729043419; ]
+
+![图片](images/20220624090942.mp4)
+
+[LAMDWIKIPOST 20220729043419; VER 20220729043512; ]
+
+![图片](images/20220624090942.mp4)
+
+射精
+
+[LAMDWIKIPOST 20220729043419; VER 20220730053758; HASI 20211109160348.jpg; ]
+
+![图片](images/20220624090942.mp4)
+![Images](images/20211109160348.jpg)
+
+射精
+
+[LAMDWIKIPOST 20220729043434; VER 20220729043434; HASI 20211028081604.jpg; ]
+
+![图片](images/20211028081604.jpg)
+
+[LAMDWIKIPOST 20220729095433; VER 20220729095433; ]
+
+# Lorem Ipsum
+
+Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus in placerat lacus. Pellentesque ac nibh in enim sodales ullamcorper eget at arcu. Morbi imperdiet neque tempor, cursus purus ut, congue mi. Morbi sit amet erat nibh. Pellentesque at odio ut odio elementum porta. Etiam volutpat purus ac dolor elementum hendrerit quis laoreet nisi. Curabitur porttitor aliquet aliquet. Duis tristique felis arcu, a vestibulum nisl dignissim ac. Nam laoreet lacinia erat id suscipit. Suspendisse iaculis lacinia eleifend. Nullam ut turpis ante.
+
+Nullam ipsum nulla, pellentesque tristique ultrices id, posuere vitae erat. Donec turpis purus, hendrerit at lectus eu, lobortis vestibulum orci. Quisque vel ligula nec odio faucibus elementum mollis at nulla. Praesent efficitur metus in purus egestas sodales. Integer finibus mattis odio ut accumsan. Donec convallis, erat ut consectetur hendrerit, urna augue dignissim velit, non mattis diam justo sed magna. Fusce ut velit ut massa porta egestas a at massa. Aenean fermentum varius purus a congue. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.
+
+Fusce tempus a tortor non facilisis. Vestibulum consectetur bibendum sagittis. Suspendisse nec urna sit amet massa pulvinar pretium. Maecenas suscipit accumsan mi eu dignissim. In dictum pellentesque gravida. Etiam posuere diam quis ultricies ultricies. Nunc tortor ligula, dapibus vitae tortor eget, consequat varius lorem. Sed hendrerit et lacus egestas malesuada. Nulla facilisi. Nullam quam ipsum, molestie vitae nibh id, molestie blandit sapien. Etiam sed erat scelerisque libero vehicula porttitor a id arcu. Nullam tincidunt nisi sapien, vitae lobortis odio semper vel. Quisque malesuada imperdiet velit, et cursus tortor imperdiet ac.
+
+Nullam porta mi in libero pellentesque, non pellentesque massa venenatis. Proin nunc mauris, malesuada non nulla eget, pellentesque varius urna. Nulla at pellentesque arcu, eget gravida ipsum. Aliquam feugiat neque in augue fringilla placerat. Donec ac efficitur tellus. Nullam ullamcorper, neque eu tempor pellentesque, enim lectus fringilla ipsum, et efficitur eros nibh quis ligula. Maecenas gravida maximus sem ut posuere. Praesent hendrerit ante vitae elit fermentum vulputate. Quisque auctor, tellus sed mollis ullamcorper, dolor lacus pellentesque mi, hendrerit viverra felis nunc eget purus. Praesent cursus, risus non dapibus pellentesque, dolor lacus finibus lacus, et ultrices erat felis et felis. Mauris ultrices orci nec neque faucibus elementum. Vivamus mauris lectus, rutrum ac aliquet id, venenatis bibendum odio. Nunc vitae mollis libero. Nullam aliquet, leo non tempor posuere, sem quam aliquam leo, sed porta leo lorem eget tellus. Quisque consequat purus ut porta placerat.
+
+Donec erat mi, tincidunt nec diam non, lacinia molestie velit. Quisque quis mattis odio. Mauris non posuere libero. Mauris posuere consectetur aliquam. Donec lacus lectus, cursus ut fermentum fringilla, malesuada a lacus. Sed nec blandit nisi, at eleifend metus. Morbi feugiat purus nec enim mollis, id volutpat lectus dignissim.
+
+[LAMDWIKIPOST 20220729095433; VER 20220729095623; ]
+
+# Lorem Ipsum
+
+Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus in placerat lacus. Pellentesque ac nibh in enim sodales ullamcorper eget at arcu. Morbi imperdiet neque tempor, cursus purus ut, congue mi. Morbi sit amet erat nibh. Pellentesque at odio ut odio elementum porta. Etiam volutpat purus ac dolor elementum hendrerit quis laoreet nisi. Curabitur porttitor aliquet aliquet. Duis tristique felis arcu, a vestibulum nisl dignissim ac. Nam laoreet lacinia erat id suscipit. Suspendisse iaculis lacinia eleifend. Nullam ut turpis ante.
+
+Nullam ipsum nulla, pellentesque tristique ultrices id, posuere vitae erat. Donec turpis purus, hendrerit at lectus eu, lobortis vestibulum orci. Quisque vel ligula nec odio faucibus elementum mollis at nulla. Praesent efficitur metus in purus egestas sodales. Integer finibus mattis odio ut accumsan. Donec convallis, erat ut consectetur hendrerit, urna augue dignissim velit, non mattis diam justo sed magna. Fusce ut velit ut massa porta egestas a at massa. Aenean fermentum varius purus a congue. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.
+
+Fusce tempus a tortor non facilisis. Vestibulum consectetur bibendum sagittis. Suspendisse nec urna sit amet massa pulvinar pretium. Maecenas suscipit accumsan mi eu dignissim. In dictum pellentesque gravida. Etiam posuere diam quis ultricies ultricies. Nunc tortor ligula, dapibus vitae tortor eget, consequat varius lorem. Sed hendrerit et lacus egestas malesuada. Nulla facilisi. Nullam quam ipsum, molestie vitae nibh id, molestie blandit sapien. Etiam sed erat scelerisque libero vehicula porttitor a id arcu. Nullam tincidunt nisi sapien, vitae lobortis odio semper vel. Quisque malesuada imperdiet velit, et cursus tortor imperdiet ac.
+
+Nullam porta mi in libero pellentesque, non pellentesque massa venenatis. Proin nunc mauris, malesuada non nulla eget, pellentesque varius urna. Nulla at pellentesque arcu, eget gravida ipsum. Aliquam feugiat neque in augue fringilla placerat. Donec ac efficitur tellus. Nullam ullamcorper, neque eu tempor pellentesque, enim lectus fringilla ipsum, et efficitur eros nibh quis ligula. Maecenas gravida maximus sem ut posuere. Praesent hendrerit ante vitae elit fermentum vulputate. Quisque auctor, tellus sed mollis ullamcorper, dolor lacus pellentesque mi, hendrerit viverra felis nunc eget purus. Praesent cursus, risus non dapibus pellentesque, dolor lacus finibus lacus, et ultrices erat felis et felis. Mauris ultrices orci nec neque faucibus elementum. Vivamus mauris lectus, rutrum ac aliquet id, venenatis bibendum odio. Nunc vitae mollis libero. Nullam aliquet, leo non tempor posuere, sem quam aliquam leo, sed porta leo lorem eget tellus. Quisque consequat purus ut porta placerat.
+
+Donec erat mi, tincidunt nec diam non, lacinia molestie velit. Quisque quis mattis odio. Mauris non posuere libero. Mauris posuere consectetur aliquam. Donec lacus lectus, cursus ut fermentum fringilla, malesuada a lacus. Sed nec blandit nisi, at eleifend metus. Morbi feugiat purus nec enim mollis, id volutpat lectus dignissim.
+
+ Nunc et tellus libero. Aliquam nec augue interdum, laoreet eros vehicula, euismod tellus. Aenean vestibulum ipsum sed enim interdum, placerat consequat sem mollis. Curabitur varius aliquet efficitur. Vivamus cursus cursus ornare. Morbi rhoncus eros a ligula suscipit eleifend. Curabitur laoreet tempus tellus ac semper. Integer at nunc ac arcu dictum vehicula. Nulla facilisi. In iaculis risus elementum, blandit tellus eu, gravida massa. Ut mattis odio ut commodo ullamcorper. Nunc mauris ante, fringilla ut porta a, sagittis rutrum lectus. Suspendisse lobortis velit mi, in imperdiet nulla consequat a. Nam faucibus facilisis volutpat. Maecenas augue odio, ornare at cursus id, faucibus sed nulla. Aenean non magna sagittis, rhoncus orci eu, finibus lectus.
+
+Interdum et malesuada fames ac ante ipsum primis in faucibus. Vivamus sagittis aliquam odio, a iaculis orci. Donec tellus sapien, mollis ut orci mollis, viverra blandit mauris. Nunc nisl magna, tempor porta viverra ut, feugiat sed metus. Cras eu orci ultrices, vehicula magna ut, auctor mi. Pellentesque vestibulum at lacus ac consequat. Ut dictum ligula id condimentum maximus. Pellentesque consequat libero lacus, a tempor lectus finibus quis. Etiam blandit a arcu sed condimentum. Cras ac scelerisque justo, vel imperdiet felis. Phasellus id nisl volutpat nibh fermentum condimentum nec ut orci.
+
+## Morbi vulputate
+
+Aliquet varius. Cras sem quam, pulvinar sed semper nec, molestie nec nisl. Nam vehicula quam et magna luctus venenatis. Sed porttitor nisl eu cursus consectetur. Etiam vel pretium leo. Integer nec condimentum ex, vitae feugiat quam. Vivamus vel eros vel elit commodo sagittis. Cras tempus urna et nisl ornare, id sagittis quam porta. Curabitur facilisis malesuada diam, nec pellentesque sem pellentesque feugiat. Aliquam blandit massa augue, vitae malesuada orci iaculis in. Praesent dignissim mi quis odio ornare fringilla. Integer consectetur varius fringilla. Nulla molestie egestas risus quis molestie. Vestibulum porta orci feugiat feugiat rutrum.
+
+Vivamus quis sagittis metus. Integer nec commodo magna. Sed aliquet augue vulputate volutpat aliquet. Nunc eu nisi elit. Duis ut risus nec risus venenatis ornare. Vestibulum malesuada ultrices varius. Etiam consectetur ullamcorper elit, at fermentum purus semper ut. Quisque venenatis tempor blandit.
+
+Nunc nec tincidunt felis. Nulla ac enim consequat, maximus elit id, molestie ligula. Donec tortor lacus, mollis et orci ac, mollis fringilla metus. Sed ac feugiat elit. Mauris commodo nulla et ipsum scelerisque, non pretium ipsum egestas. Mauris fringilla mattis turpis. Donec dignissim lectus eget lacus lobortis, non egestas justo dictum. Interdum et malesuada fames ac ante ipsum primis in faucibus. Vivamus tortor elit, elementum lacinia malesuada ac, venenatis vitae velit.
+
+Proin ipsum orci, rutrum nec neque nec, tempor efficitur sem. Phasellus in elit iaculis dolor cursus efficitur. Donec quis vulputate elit. Ut at odio mauris. Pellentesque sit amet maximus velit. Aliquam ac ex a neque ullamcorper venenatis eget et massa. Donec convallis felis eget mauris consectetur, in convallis justo semper. Donec dignissim consequat eleifend. Pellentesque augue nulla, placerat vitae urna a, mattis sodales erat. Nullam et malesuada risus, et ultrices ipsum. Integer ac placerat enim. Integer vel faucibus nisi.
+
+## Orci varius natoque
+
+Penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed venenatis dolor ac enim pulvinar cursus a non nulla. Aliquam commodo, nisi a fermentum mollis, risus justo volutpat diam, id varius felis magna feugiat tellus. Sed at porta metus. Praesent fermentum, nisl vel lobortis venenatis, odio turpis malesuada tortor, ultrices pellentesque quam ex eu orci. Sed laoreet non lorem eu gravida. Morbi quis felis a massa facilisis accumsan. Suspendisse rhoncus purus neque, in eleifend augue hendrerit id. Phasellus luctus a sem sit amet accumsan. Quisque tempor velit at pulvinar varius. Pellentesque ex felis, imperdiet gravida neque non, congue venenatis ex. Aenean iaculis cursus turpis eget luctus. Praesent nec eleifend lectus.
+
+Aenean luctus quam dolor, et ultrices odio rutrum nec. Sed pulvinar, felis at elementum venenatis, lacus purus convallis mi, sed ultrices nisl orci id urna. Curabitur dapibus volutpat hendrerit. In molestie, purus eu sollicitudin semper, leo ante placerat odio, at sollicitudin diam tellus sit amet metus. Morbi hendrerit est ac erat scelerisque condimentum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Proin egestas orci ac sapien blandit laoreet.
+
+Vivamus sollicitudin sed turpis non placerat. Cras mollis lobortis elit vitae vestibulum. Donec ornare, tortor interdum lacinia egestas, sem arcu sodales ipsum, ac ultrices justo elit sit amet ipsum. Cras sapien leo, tincidunt pretium pretium quis, egestas non mauris. Aenean ac fermentum ante. Mauris rhoncus enim ante, a pellentesque mauris mattis sed. Nam mattis, ante eu pretium tempus, risus magna iaculis erat, eget imperdiet nulla neque sed nulla. Cras gravida libero neque, quis pulvinar ante fringilla quis. In ut lobortis velit. Suspendisse congue arcu ligula, at aliquam ipsum pharetra sit amet. Mauris ut enim eget massa dignissim bibendum. Vivamus purus nisl, fermentum efficitur elementum nec, lacinia at tortor. Donec aliquam tellus vel nunc tristique, id pulvinar sem facilisis.
+
+Phasellus convallis, velit eget blandit tempus, leo nisi placerat leo, ac blandit metus ligula vitae enim. Duis a felis in dui venenatis tristique ac quis mauris. Sed interdum dui sit amet tincidunt imperdiet. Integer consequat purus ut porta ornare. Morbi vel sem interdum, tempor lacus interdum, eleifend ligula. Vivamus augue nulla, finibus eget sapien nec, euismod maximus nunc. Aliquam non hendrerit massa.
+
+Vestibulum at justo metus. Duis at ultrices magna, vitae varius neque. Donec et tellus vitae turpis bibendum posuere. Nunc et sagittis nulla. Phasellus egestas feugiat mattis. Fusce eu felis ac tellus tempus egestas vel non eros. Integer faucibus euismod purus, non euismod sem. Cras sodales risus in libero eleifend molestie. Mauris convallis accumsan lectus pellentesque laoreet. Aenean sagittis enim sit amet orci ullamcorper, in semper tellus blandit. Pellentesque orci ex, tempor sed semper et, vehicula at turpis. Sed id finibus ex. Nullam metus neque, sollicitudin id urna eget, commodo tincidunt est. Donec porttitor nisi sit amet arcu dapibus rutrum.
+
+Sed ultrices tempus purus, ullamcorper sodales libero vestibulum luctus. Proin ut commodo justo, vitae dignissim leo. Mauris condimentum dolor ut purus ultricies, at consequat eros gravida. Phasellus quis eros sed tellus molestie venenatis ut vitae nibh. Maecenas mattis commodo neque vel bibendum. Curabitur eu porta risus. Maecenas mollis dignissim interdum. Integer ac pharetra lacus, vitae condimentum ex. Mauris at quam ornare, sollicitudin turpis vitae, aliquam tellus. Nam lacinia sem at lobortis ultrices. In vulputate odio a quam semper maximus at et massa. Nam massa diam, tincidunt at ex sit amet, placerat fringilla eros. Etiam quis semper turpis. Aliquam laoreet, nisi tempor dignissim suscipit, tellus nunc mattis tellus, luctus euismod massa leo id ex.
+
+Nullam placerat malesuada dui, vitae feugiat velit. Fusce finibus neque non massa tincidunt imperdiet. Nam scelerisque bibendum neque. In hac habitasse platea dictumst. Donec erat odio, rutrum mollis justo eu, interdum pharetra urna. Mauris eros felis, vulputate a lacus eu, euismod pulvinar felis. Curabitur euismod nulla ut auctor mattis. Sed molestie ligula ac lacus commodo, vitae vehicula leo blandit. Nullam et finibus turpis. Integer posuere diam urna, ultrices suscipit augue tincidunt vel.
+
+Maecenas consequat velit nec orci vestibulum, in interdum magna condimentum. Duis faucibus tellus in elit pharetra, ut pulvinar libero vulputate. Curabitur in eros a urna condimentum iaculis elementum at elit. In id ullamcorper risus, et iaculis est. Vestibulum ac tellus vitae neque volutpat vehicula. Donec pulvinar nibh vitae eros ullamcorper consequat. Fusce vitae quam blandit est blandit convallis. Donec non dignissim ipsum. Donec libero velit, fermentum quis leo a, tempor rutrum nunc. Nunc tempor ligula sapien, id pellentesque sapien dapibus vitae. Pellentesque nisl elit, varius elementum sapien sed, tempus dictum leo. Fusce magna orci, ornare eu luctus ac, feugiat vitae urna. Vestibulum vel metus ornare, varius velit et, congue risus. Quisque id purus sit amet mi consectetur interdum eget id magna.
+
+[LAMDWIKIPOST 20220729095433; VER 20220729095711; ]
+
+# Lorem Ipsum
+
+## at porta metus
+
+Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus in placerat lacus. Pellentesque ac nibh in enim sodales ullamcorper eget at arcu. Morbi imperdiet neque tempor, cursus purus ut, congue mi. Morbi sit amet erat nibh. Pellentesque at odio ut odio elementum porta. Etiam volutpat purus ac dolor elementum hendrerit quis laoreet nisi. Curabitur porttitor aliquet aliquet. Duis tristique felis arcu, a vestibulum nisl dignissim ac. Nam laoreet lacinia erat id suscipit. Suspendisse iaculis lacinia eleifend. Nullam ut turpis ante.
+
+Nullam ipsum nulla, pellentesque tristique ultrices id, posuere vitae erat. Donec turpis purus, hendrerit at lectus eu, lobortis vestibulum orci. Quisque vel ligula nec odio faucibus elementum mollis at nulla. Praesent efficitur metus in purus egestas sodales. Integer finibus mattis odio ut accumsan. Donec convallis, erat ut consectetur hendrerit, urna augue dignissim velit, non mattis diam justo sed magna. Fusce ut velit ut massa porta egestas a at massa. Aenean fermentum varius purus a congue. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.
+
+Fusce tempus a tortor non facilisis. Vestibulum consectetur bibendum sagittis. Suspendisse nec urna sit amet massa pulvinar pretium. Maecenas suscipit accumsan mi eu dignissim. In dictum pellentesque gravida. Etiam posuere diam quis ultricies ultricies. Nunc tortor ligula, dapibus vitae tortor eget, consequat varius lorem. Sed hendrerit et lacus egestas malesuada. Nulla facilisi. Nullam quam ipsum, molestie vitae nibh id, molestie blandit sapien. Etiam sed erat scelerisque libero vehicula porttitor a id arcu. Nullam tincidunt nisi sapien, vitae lobortis odio semper vel. Quisque malesuada imperdiet velit, et cursus tortor imperdiet ac.
+
+Nullam porta mi in libero pellentesque, non pellentesque massa venenatis. Proin nunc mauris, malesuada non nulla eget, pellentesque varius urna. Nulla at pellentesque arcu, eget gravida ipsum. Aliquam feugiat neque in augue fringilla placerat. Donec ac efficitur tellus. Nullam ullamcorper, neque eu tempor pellentesque, enim lectus fringilla ipsum, et efficitur eros nibh quis ligula. Maecenas gravida maximus sem ut posuere. Praesent hendrerit ante vitae elit fermentum vulputate. Quisque auctor, tellus sed mollis ullamcorper, dolor lacus pellentesque mi, hendrerit viverra felis nunc eget purus. Praesent cursus, risus non dapibus pellentesque, dolor lacus finibus lacus, et ultrices erat felis et felis. Mauris ultrices orci nec neque faucibus elementum. Vivamus mauris lectus, rutrum ac aliquet id, venenatis bibendum odio. Nunc vitae mollis libero. Nullam aliquet, leo non tempor posuere, sem quam aliquam leo, sed porta leo lorem eget tellus. Quisque consequat purus ut porta placerat.
+
+##  eleifend metus
+
+Donec erat mi, tincidunt nec diam non, lacinia molestie velit. Quisque quis mattis odio. Mauris non posuere libero. Mauris posuere consectetur aliquam. Donec lacus lectus, cursus ut fermentum fringilla, malesuada a lacus. Sed nec blandit nisi, at eleifend metus. Morbi feugiat purus nec enim mollis, id volutpat lectus dignissim.
+
+ Nunc et tellus libero. Aliquam nec augue interdum, laoreet eros vehicula, euismod tellus. Aenean vestibulum ipsum sed enim interdum, placerat consequat sem mollis. Curabitur varius aliquet efficitur. Vivamus cursus cursus ornare. Morbi rhoncus eros a ligula suscipit eleifend. Curabitur laoreet tempus tellus ac semper. Integer at nunc ac arcu dictum vehicula. Nulla facilisi. In iaculis risus elementum, blandit tellus eu, gravida massa. Ut mattis odio ut commodo ullamcorper. Nunc mauris ante, fringilla ut porta a, sagittis rutrum lectus. Suspendisse lobortis velit mi, in imperdiet nulla consequat a. Nam faucibus facilisis volutpat. Maecenas augue odio, ornare at cursus id, faucibus sed nulla. Aenean non magna sagittis, rhoncus orci eu, finibus lectus.
+
+Interdum et malesuada fames ac ante ipsum primis in faucibus. Vivamus sagittis aliquam odio, a iaculis orci. Donec tellus sapien, mollis ut orci mollis, viverra blandit mauris. Nunc nisl magna, tempor porta viverra ut, feugiat sed metus. Cras eu orci ultrices, vehicula magna ut, auctor mi. Pellentesque vestibulum at lacus ac consequat. Ut dictum ligula id condimentum maximus. Pellentesque consequat libero lacus, a tempor lectus finibus quis. Etiam blandit a arcu sed condimentum. Cras ac scelerisque justo, vel imperdiet felis. Phasellus id nisl volutpat nibh fermentum condimentum nec ut orci.
+
+## Morbi vulputate
+
+Aliquet varius. Cras sem quam, pulvinar sed semper nec, molestie nec nisl. Nam vehicula quam et magna luctus venenatis. Sed porttitor nisl eu cursus consectetur. Etiam vel pretium leo. Integer nec condimentum ex, vitae feugiat quam. Vivamus vel eros vel elit commodo sagittis. Cras tempus urna et nisl ornare, id sagittis quam porta. Curabitur facilisis malesuada diam, nec pellentesque sem pellentesque feugiat. Aliquam blandit massa augue, vitae malesuada orci iaculis in. Praesent dignissim mi quis odio ornare fringilla. Integer consectetur varius fringilla. Nulla molestie egestas risus quis molestie. Vestibulum porta orci feugiat feugiat rutrum.
+
+Vivamus quis sagittis metus. Integer nec commodo magna. Sed aliquet augue vulputate volutpat aliquet. Nunc eu nisi elit. Duis ut risus nec risus venenatis ornare. Vestibulum malesuada ultrices varius. Etiam consectetur ullamcorper elit, at fermentum purus semper ut. Quisque venenatis tempor blandit.
+
+Nunc nec tincidunt felis. Nulla ac enim consequat, maximus elit id, molestie ligula. Donec tortor lacus, mollis et orci ac, mollis fringilla metus. Sed ac feugiat elit. Mauris commodo nulla et ipsum scelerisque, non pretium ipsum egestas. Mauris fringilla mattis turpis. Donec dignissim lectus eget lacus lobortis, non egestas justo dictum. Interdum et malesuada fames ac ante ipsum primis in faucibus. Vivamus tortor elit, elementum lacinia malesuada ac, venenatis vitae velit.
+
+Proin ipsum orci, rutrum nec neque nec, tempor efficitur sem. Phasellus in elit iaculis dolor cursus efficitur. Donec quis vulputate elit. Ut at odio mauris. Pellentesque sit amet maximus velit. Aliquam ac ex a neque ullamcorper venenatis eget et massa. Donec convallis felis eget mauris consectetur, in convallis justo semper. Donec dignissim consequat eleifend. Pellentesque augue nulla, placerat vitae urna a, mattis sodales erat. Nullam et malesuada risus, et ultrices ipsum. Integer ac placerat enim. Integer vel faucibus nisi.
+
+## Orci varius natoque
+
+Penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed venenatis dolor ac enim pulvinar cursus a non nulla. Aliquam commodo, nisi a fermentum mollis, risus justo volutpat diam, id varius felis magna feugiat tellus. Sed at porta metus. Praesent fermentum, nisl vel lobortis venenatis, odio turpis malesuada tortor, ultrices pellentesque quam ex eu orci. Sed laoreet non lorem eu gravida. Morbi quis felis a massa facilisis accumsan. Suspendisse rhoncus purus neque, in eleifend augue hendrerit id. Phasellus luctus a sem sit amet accumsan. Quisque tempor velit at pulvinar varius. Pellentesque ex felis, imperdiet gravida neque non, congue venenatis ex. Aenean iaculis cursus turpis eget luctus. Praesent nec eleifend lectus.
+
+Aenean luctus quam dolor, et ultrices odio rutrum nec. Sed pulvinar, felis at elementum venenatis, lacus purus convallis mi, sed ultrices nisl orci id urna. Curabitur dapibus volutpat hendrerit. In molestie, purus eu sollicitudin semper, leo ante placerat odio, at sollicitudin diam tellus sit amet metus. Morbi hendrerit est ac erat scelerisque condimentum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Proin egestas orci ac sapien blandit laoreet.
+
+Vivamus sollicitudin sed turpis non placerat. Cras mollis lobortis elit vitae vestibulum. Donec ornare, tortor interdum lacinia egestas, sem arcu sodales ipsum, ac ultrices justo elit sit amet ipsum. Cras sapien leo, tincidunt pretium pretium quis, egestas non mauris. Aenean ac fermentum ante. Mauris rhoncus enim ante, a pellentesque mauris mattis sed. Nam mattis, ante eu pretium tempus, risus magna iaculis erat, eget imperdiet nulla neque sed nulla. Cras gravida libero neque, quis pulvinar ante fringilla quis. In ut lobortis velit. Suspendisse congue arcu ligula, at aliquam ipsum pharetra sit amet. Mauris ut enim eget massa dignissim bibendum. Vivamus purus nisl, fermentum efficitur elementum nec, lacinia at tortor. Donec aliquam tellus vel nunc tristique, id pulvinar sem facilisis.
+
+Phasellus convallis, velit eget blandit tempus, leo nisi placerat leo, ac blandit metus ligula vitae enim. Duis a felis in dui venenatis tristique ac quis mauris. Sed interdum dui sit amet tincidunt imperdiet. Integer consequat purus ut porta ornare. Morbi vel sem interdum, tempor lacus interdum, eleifend ligula. Vivamus augue nulla, finibus eget sapien nec, euismod maximus nunc. Aliquam non hendrerit massa.
+
+Vestibulum at justo metus. Duis at ultrices magna, vitae varius neque. Donec et tellus vitae turpis bibendum posuere. Nunc et sagittis nulla. Phasellus egestas feugiat mattis. Fusce eu felis ac tellus tempus egestas vel non eros. Integer faucibus euismod purus, non euismod sem. Cras sodales risus in libero eleifend molestie. Mauris convallis accumsan lectus pellentesque laoreet. Aenean sagittis enim sit amet orci ullamcorper, in semper tellus blandit. Pellentesque orci ex, tempor sed semper et, vehicula at turpis. Sed id finibus ex. Nullam metus neque, sollicitudin id urna eget, commodo tincidunt est. Donec porttitor nisi sit amet arcu dapibus rutrum.
+
+Sed ultrices tempus purus, ullamcorper sodales libero vestibulum luctus. Proin ut commodo justo, vitae dignissim leo. Mauris condimentum dolor ut purus ultricies, at consequat eros gravida. Phasellus quis eros sed tellus molestie venenatis ut vitae nibh. Maecenas mattis commodo neque vel bibendum. Curabitur eu porta risus. Maecenas mollis dignissim interdum. Integer ac pharetra lacus, vitae condimentum ex. Mauris at quam ornare, sollicitudin turpis vitae, aliquam tellus. Nam lacinia sem at lobortis ultrices. In vulputate odio a quam semper maximus at et massa. Nam massa diam, tincidunt at ex sit amet, placerat fringilla eros. Etiam quis semper turpis. Aliquam laoreet, nisi tempor dignissim suscipit, tellus nunc mattis tellus, luctus euismod massa leo id ex.
+
+Nullam placerat malesuada dui, vitae feugiat velit. Fusce finibus neque non massa tincidunt imperdiet. Nam scelerisque bibendum neque. In hac habitasse platea dictumst. Donec erat odio, rutrum mollis justo eu, interdum pharetra urna. Mauris eros felis, vulputate a lacus eu, euismod pulvinar felis. Curabitur euismod nulla ut auctor mattis. Sed molestie ligula ac lacus commodo, vitae vehicula leo blandit. Nullam et finibus turpis. Integer posuere diam urna, ultrices suscipit augue tincidunt vel.
+
+Maecenas consequat velit nec orci vestibulum, in interdum magna condimentum. Duis faucibus tellus in elit pharetra, ut pulvinar libero vulputate. Curabitur in eros a urna condimentum iaculis elementum at elit. In id ullamcorper risus, et iaculis est. Vestibulum ac tellus vitae neque volutpat vehicula. Donec pulvinar nibh vitae eros ullamcorper consequat. Fusce vitae quam blandit est blandit convallis. Donec non dignissim ipsum. Donec libero velit, fermentum quis leo a, tempor rutrum nunc. Nunc tempor ligula sapien, id pellentesque sapien dapibus vitae. Pellentesque nisl elit, varius elementum sapien sed, tempus dictum leo. Fusce magna orci, ornare eu luctus ac, feugiat vitae urna. Vestibulum vel metus ornare, varius velit et, congue risus. Quisque id purus sit amet mi consectetur interdum eget id magna.
+
+[LAMDWIKIPOST 20220729095433; VER 20220729101005; ]
+
+# Lorem Ipsum
+
+## at porta metus
+
+Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus in placerat lacus. Pellentesque ac nibh in enim sodales ullamcorper eget at arcu. Morbi imperdiet neque tempor, cursus purus ut, congue mi. Morbi sit amet erat nibh. Pellentesque at odio ut odio elementum porta. Etiam volutpat purus ac dolor elementum hendrerit quis laoreet nisi. Curabitur porttitor aliquet aliquet. Duis tristique felis arcu, a vestibulum nisl dignissim ac. Nam laoreet lacinia erat id suscipit. Suspendisse iaculis lacinia eleifend. Nullam ut turpis ante.
+
+Nullam ipsum nulla, pellentesque tristique ultrices id, posuere vitae erat. Donec turpis purus, hendrerit at lectus eu, lobortis vestibulum orci. Quisque vel ligula nec odio faucibus elementum mollis at nulla. Praesent efficitur metus in purus egestas sodales. Integer finibus mattis odio ut accumsan. Donec convallis, erat ut consectetur hendrerit, urna augue dignissim velit, non mattis diam justo sed magna. Fusce ut velit ut massa porta egestas a at massa. Aenean fermentum varius purus a congue. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.
+
+Fusce tempus a tortor non facilisis. Vestibulum consectetur bibendum sagittis. Suspendisse nec urna sit amet massa pulvinar pretium. Maecenas suscipit accumsan mi eu dignissim. In dictum pellentesque gravida. Etiam posuere diam quis ultricies ultricies. Nunc tortor ligula, dapibus vitae tortor eget, consequat varius lorem. Sed hendrerit et lacus egestas malesuada. Nulla facilisi. Nullam quam ipsum, molestie vitae nibh id, molestie blandit sapien. Etiam sed erat scelerisque libero vehicula porttitor a id arcu. Nullam tincidunt nisi sapien, vitae lobortis odio semper vel. Quisque malesuada imperdiet velit, et cursus tortor imperdiet ac.
+
+Nullam porta mi in libero pellentesque, non pellentesque massa venenatis. Proin nunc mauris, malesuada non nulla eget, pellentesque varius urna. Nulla at pellentesque arcu, eget gravida ipsum. Aliquam feugiat neque in augue fringilla placerat. Donec ac efficitur tellus. Nullam ullamcorper, neque eu tempor pellentesque, enim lectus fringilla ipsum, et efficitur eros nibh quis ligula. Maecenas gravida maximus sem ut posuere. Praesent hendrerit ante vitae elit fermentum vulputate. Quisque auctor, tellus sed mollis ullamcorper, dolor lacus pellentesque mi, hendrerit viverra felis nunc eget purus. Praesent cursus, risus non dapibus pellentesque, dolor lacus finibus lacus, et ultrices erat felis et felis. Mauris ultrices orci nec neque faucibus elementum. Vivamus mauris lectus, rutrum ac aliquet id, venenatis bibendum odio. Nunc vitae mollis libero. Nullam aliquet, leo non tempor posuere, sem quam aliquam leo, sed porta leo lorem eget tellus. Quisque consequat purus ut porta placerat.
+
+##  eleifend metus
+
+Donec erat mi, tincidunt nec diam non, lacinia molestie velit. Quisque quis mattis odio. Mauris non posuere libero. Mauris posuere consectetur aliquam. Donec lacus lectus, cursus ut fermentum fringilla, malesuada a lacus. Sed nec blandit nisi, at eleifend metus. Morbi feugiat purus nec enim mollis, id volutpat lectus dignissim.
+
+ Nunc et tellus libero. Aliquam nec augue interdum, laoreet eros vehicula, euismod tellus. Aenean vestibulum ipsum sed enim interdum, placerat consequat sem mollis. Curabitur varius aliquet efficitur. Vivamus cursus cursus ornare. Morbi rhoncus eros a ligula suscipit eleifend. Curabitur laoreet tempus tellus ac semper. Integer at nunc ac arcu dictum vehicula. Nulla facilisi. In iaculis risus elementum, blandit tellus eu, gravida massa. Ut mattis odio ut commodo ullamcorper. Nunc mauris ante, fringilla ut porta a, sagittis rutrum lectus. Suspendisse lobortis velit mi, in imperdiet nulla consequat a. Nam faucibus facilisis volutpat. Maecenas augue odio, ornare at cursus id, faucibus sed nulla. Aenean non magna sagittis, rhoncus orci eu, finibus lectus.
+
+Interdum et malesuada fames ac ante ipsum primis in faucibus. Vivamus sagittis aliquam odio, a iaculis orci. Donec tellus sapien, mollis ut orci mollis, viverra blandit mauris. Nunc nisl magna, tempor porta viverra ut, feugiat sed metus. Cras eu orci ultrices, vehicula magna ut, auctor mi. Pellentesque vestibulum at lacus ac consequat. Ut dictum ligula id condimentum maximus. Pellentesque consequat libero lacus, a tempor lectus finibus quis. Etiam blandit a arcu sed condimentum. Cras ac scelerisque justo, vel imperdiet felis. Phasellus id nisl volutpat nibh fermentum condimentum nec ut orci.
+
+## Morbi vulputate
+
+Aliquet varius. Cras sem quam, pulvinar sed semper nec, molestie nec nisl. Nam vehicula quam et magna luctus venenatis. Sed porttitor nisl eu cursus consectetur. Etiam vel pretium leo. Integer nec condimentum ex, vitae feugiat quam. Vivamus vel eros vel elit commodo sagittis. Cras tempus urna et nisl ornare, id sagittis quam porta. Curabitur facilisis malesuada diam, nec pellentesque sem pellentesque feugiat. Aliquam blandit massa augue, vitae malesuada orci iaculis in. Praesent dignissim mi quis odio ornare fringilla. Integer consectetur varius fringilla. Nulla molestie egestas risus quis molestie. Vestibulum porta orci feugiat feugiat rutrum.
+
+Vivamus quis sagittis metus. Integer nec commodo magna. Sed aliquet augue vulputate volutpat aliquet. Nunc eu nisi elit. Duis ut risus nec risus venenatis ornare. Vestibulum malesuada ultrices varius. Etiam consectetur ullamcorper elit, at fermentum purus semper ut. Quisque venenatis tempor blandit.
+
+Nunc nec tincidunt felis. Nulla ac enim consequat, maximus elit id, molestie ligula. Donec tortor lacus, mollis et orci ac, mollis fringilla metus. Sed ac feugiat elit. Mauris commodo nulla et ipsum scelerisque, non pretium ipsum egestas. Mauris fringilla mattis turpis. Donec dignissim lectus eget lacus lobortis, non egestas justo dictum. Interdum et malesuada fames ac ante ipsum primis in faucibus. Vivamus tortor elit, elementum lacinia malesuada ac, venenatis vitae velit.
+
+Proin ipsum orci, rutrum nec neque nec, tempor efficitur sem. Phasellus in elit iaculis dolor cursus efficitur. Donec quis vulputate elit. Ut at odio mauris. Pellentesque sit amet maximus velit. Aliquam ac ex a neque ullamcorper venenatis eget et massa. Donec convallis felis eget mauris consectetur, in convallis justo semper. Donec dignissim consequat eleifend. Pellentesque augue nulla, placerat vitae urna a, mattis sodales erat. Nullam et malesuada risus, et ultrices ipsum. Integer ac placerat enim. Integer vel faucibus nisi.
+
+## Orci varius natoque
+
+Penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed venenatis dolor ac enim pulvinar cursus a non nulla. Aliquam commodo, nisi a fermentum mollis, risus justo volutpat diam, id varius felis magna feugiat tellus. Sed at porta metus. Praesent fermentum, nisl vel lobortis venenatis, odio turpis malesuada tortor, ultrices pellentesque quam ex eu orci. Sed laoreet non lorem eu gravida. Morbi quis felis a massa facilisis accumsan. Suspendisse rhoncus purus neque, in eleifend augue hendrerit id. Phasellus luctus a sem sit amet accumsan. Quisque tempor velit at pulvinar varius. Pellentesque ex felis, imperdiet gravida neque non, congue venenatis ex. Aenean iaculis cursus turpis eget luctus. Praesent nec eleifend lectus.
+
+Aenean luctus quam dolor, et ultrices odio rutrum nec. Sed pulvinar, felis at elementum venenatis, lacus purus convallis mi, sed ultrices nisl orci id urna. Curabitur dapibus volutpat hendrerit. In molestie, purus eu sollicitudin semper, leo ante placerat odio, at sollicitudin diam tellus sit amet metus. Morbi hendrerit est ac erat scelerisque condimentum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Proin egestas orci ac sapien blandit laoreet.
+
+Vivamus sollicitudin sed turpis non placerat. Cras mollis lobortis elit vitae vestibulum. Donec ornare, tortor interdum lacinia egestas, sem arcu sodales ipsum, ac ultrices justo elit sit amet ipsum. Cras sapien leo, tincidunt pretium pretium quis, egestas non mauris. Aenean ac fermentum ante. Mauris rhoncus enim ante, a pellentesque mauris mattis sed. Nam mattis, ante eu pretium tempus, risus magna iaculis erat, eget imperdiet nulla neque sed nulla. Cras gravida libero neque, quis pulvinar ante fringilla quis. In ut lobortis velit. Suspendisse congue arcu ligula, at aliquam ipsum pharetra sit amet. Mauris ut enim eget massa dignissim bibendum. Vivamus purus nisl, fermentum efficitur elementum nec, lacinia at tortor. Donec aliquam tellus vel nunc tristique, id pulvinar sem facilisis.
+
+| 123123 | 123131 |
+|---|---|
+|  Donec ornare, tortor interdum lacinia egestas, sem arcu sodales ipsum, ac ultrices justo elit sit amet ipsum. Cras sapien leo, tincidunt pretium pretium quis, egestas non mauris. Aenean ac fermentum ante. Mauris rhoncus enim ante, a pellentesque mauris mattis sed. Nam mattis, ante eu pretium tempus, risus magna iaculis erat, eget imperdiet nulla neque sed nulla. Cras gravida libero neque, quis pulvinar ante fringilla quis. In ut lobortis velit. Suspendisse congue arcu ligula, at aliquam ipsum pharetra sit amet. Mauris ut enim eget massa dignissim bibendum. Vivamus purus nisl, fermentum efficitur elementum nec, lacinia at tortor. Donec aliquam tellus vel nunc tristique, id pulvinar sem facilisis. | Curabitur dapibus volutpat hendrerit. In molestie, purus eu sollicitudin semper, leo ante placerat odio, at sollicitudin diam tellus sit amet metus. Morbi hendrerit est ac erat scelerisque condimentum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Proin egestas orci ac sapien blandit laoreet. |
+|  Donec ornare, tortor interdum lacinia egestas, sem arcu sodales ipsum, ac ultrices justo elit sit amet ipsum. Cras sapien leo, tincidunt pretium pretium quis, egestas non mauris. Aenean ac fermentum ante. Mauris rhoncus enim ante, a pellentesque mauris mattis sed. Nam mattis, ante eu pretium tempus, risus magna iaculis erat, eget imperdiet nulla neque sed nulla. Cras gravida libero neque, quis pulvinar ante fringilla quis. In ut lobortis velit. Suspendisse congue arcu ligula, at aliquam ipsum pharetra sit amet. Mauris ut enim eget massa dignissim bibendum. Vivamus purus nisl, fermentum efficitur elementum nec, lacinia at tortor. Donec aliquam tellus vel nunc tristique, id pulvinar sem facilisis. | Curabitur dapibus volutpat hendrerit. In molestie, purus eu sollicitudin semper, leo ante placerat odio, at sollicitudin diam tellus sit amet metus. Morbi hendrerit est ac erat scelerisque condimentum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Proin egestas orci ac sapien blandit laoreet. |
+|  Donec ornare, tortor interdum lacinia egestas, sem arcu sodales ipsum, ac ultrices justo elit sit amet ipsum. Cras sapien leo, tincidunt pretium pretium quis, egestas non mauris. Aenean ac fermentum ante. Mauris rhoncus enim ante, a pellentesque mauris mattis sed. Nam mattis, ante eu pretium tempus, risus magna iaculis erat, eget imperdiet nulla neque sed nulla. Cras gravida libero neque, quis pulvinar ante fringilla quis. In ut lobortis velit. Suspendisse congue arcu ligula, at aliquam ipsum pharetra sit amet. Mauris ut enim eget massa dignissim bibendum. Vivamus purus nisl, fermentum efficitur elementum nec, lacinia at tortor. Donec aliquam tellus vel nunc tristique, id pulvinar sem facilisis. | Curabitur dapibus volutpat hendrerit. In molestie, purus eu sollicitudin semper, leo ante placerat odio, at sollicitudin diam tellus sit amet metus. Morbi hendrerit est ac erat scelerisque condimentum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Proin egestas orci ac sapien blandit laoreet. |
+|  Donec ornare, tortor interdum lacinia egestas, sem arcu sodales ipsum, ac ultrices justo elit sit amet ipsum. Cras sapien leo, tincidunt pretium pretium quis, egestas non mauris. Aenean ac fermentum ante. Mauris rhoncus enim ante, a pellentesque mauris mattis sed. Nam mattis, ante eu pretium tempus, risus magna iaculis erat, eget imperdiet nulla neque sed nulla. Cras gravida libero neque, quis pulvinar ante fringilla quis. In ut lobortis velit. Suspendisse congue arcu ligula, at aliquam ipsum pharetra sit amet. Mauris ut enim eget massa dignissim bibendum. Vivamus purus nisl, fermentum efficitur elementum nec, lacinia at tortor. Donec aliquam tellus vel nunc tristique, id pulvinar sem facilisis. | Curabitur dapibus volutpat hendrerit. In molestie, purus eu sollicitudin semper, leo ante placerat odio, at sollicitudin diam tellus sit amet metus. Morbi hendrerit est ac erat scelerisque condimentum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Proin egestas orci ac sapien blandit laoreet. |
+
+Phasellus convallis, velit eget blandit tempus, leo nisi placerat leo, ac blandit metus ligula vitae enim. Duis a felis in dui venenatis tristique ac quis mauris. Sed interdum dui sit amet tincidunt imperdiet. Integer consequat purus ut porta ornare. Morbi vel sem interdum, tempor lacus interdum, eleifend ligula. Vivamus augue nulla, finibus eget sapien nec, euismod maximus nunc. Aliquam non hendrerit massa.
+
+Vestibulum at justo metus. Duis at ultrices magna, vitae varius neque. Donec et tellus vitae turpis bibendum posuere. Nunc et sagittis nulla. Phasellus egestas feugiat mattis. Fusce eu felis ac tellus tempus egestas vel non eros. Integer faucibus euismod purus, non euismod sem. Cras sodales risus in libero eleifend molestie. Mauris convallis accumsan lectus pellentesque laoreet. Aenean sagittis enim sit amet orci ullamcorper, in semper tellus blandit. Pellentesque orci ex, tempor sed semper et, vehicula at turpis. Sed id finibus ex. Nullam metus neque, sollicitudin id urna eget, commodo tincidunt est. Donec porttitor nisi sit amet arcu dapibus rutrum.
+
+Sed ultrices tempus purus, ullamcorper sodales libero vestibulum luctus. Proin ut commodo justo, vitae dignissim leo. Mauris condimentum dolor ut purus ultricies, at consequat eros gravida. Phasellus quis eros sed tellus molestie venenatis ut vitae nibh. Maecenas mattis commodo neque vel bibendum. Curabitur eu porta risus. Maecenas mollis dignissim interdum. Integer ac pharetra lacus, vitae condimentum ex. Mauris at quam ornare, sollicitudin turpis vitae, aliquam tellus. Nam lacinia sem at lobortis ultrices. In vulputate odio a quam semper maximus at et massa. Nam massa diam, tincidunt at ex sit amet, placerat fringilla eros. Etiam quis semper turpis. Aliquam laoreet, nisi tempor dignissim suscipit, tellus nunc mattis tellus, luctus euismod massa leo id ex.
+
+Nullam placerat malesuada dui, vitae feugiat velit. Fusce finibus neque non massa tincidunt imperdiet. Nam scelerisque bibendum neque. In hac habitasse platea dictumst. Donec erat odio, rutrum mollis justo eu, interdum pharetra urna. Mauris eros felis, vulputate a lacus eu, euismod pulvinar felis. Curabitur euismod nulla ut auctor mattis. Sed molestie ligula ac lacus commodo, vitae vehicula leo blandit. Nullam et finibus turpis. Integer posuere diam urna, ultrices suscipit augue tincidunt vel.
+
+Maecenas consequat velit nec orci vestibulum, in interdum magna condimentum. Duis faucibus tellus in elit pharetra, ut pulvinar libero vulputate. Curabitur in eros a urna condimentum iaculis elementum at elit. In id ullamcorper risus, et iaculis est. Vestibulum ac tellus vitae neque volutpat vehicula. Donec pulvinar nibh vitae eros ullamcorper consequat. Fusce vitae quam blandit est blandit convallis. Donec non dignissim ipsum. Donec libero velit, fermentum quis leo a, tempor rutrum nunc. Nunc tempor ligula sapien, id pellentesque sapien dapibus vitae. Pellentesque nisl elit, varius elementum sapien sed, tempus dictum leo. Fusce magna orci, ornare eu luctus ac, feugiat vitae urna. Vestibulum vel metus ornare, varius velit et, congue risus. Quisque id purus sit amet mi consectetur interdum eget id magna.
+
+[LAMDWIKIPOST 20220729095433; VER 20220729101418; ]
+
+# Lorem Ipsum
+
+{wide}
+
+## at porta metus
+
+Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus in placerat lacus. Pellentesque ac nibh in enim sodales ullamcorper eget at arcu. Morbi imperdiet neque tempor, cursus purus ut, congue mi. Morbi sit amet erat nibh. Pellentesque at odio ut odio elementum porta. Etiam volutpat purus ac dolor elementum hendrerit quis laoreet nisi. Curabitur porttitor aliquet aliquet. Duis tristique felis arcu, a vestibulum nisl dignissim ac. Nam laoreet lacinia erat id suscipit. Suspendisse iaculis lacinia eleifend. Nullam ut turpis ante.
+
+Nullam ipsum nulla, pellentesque tristique ultrices id, posuere vitae erat. Donec turpis purus, hendrerit at lectus eu, lobortis vestibulum orci. Quisque vel ligula nec odio faucibus elementum mollis at nulla. Praesent efficitur metus in purus egestas sodales. Integer finibus mattis odio ut accumsan. Donec convallis, erat ut consectetur hendrerit, urna augue dignissim velit, non mattis diam justo sed magna. Fusce ut velit ut massa porta egestas a at massa. Aenean fermentum varius purus a congue. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.
+
+Fusce tempus a tortor non facilisis. Vestibulum consectetur bibendum sagittis. Suspendisse nec urna sit amet massa pulvinar pretium. Maecenas suscipit accumsan mi eu dignissim. In dictum pellentesque gravida. Etiam posuere diam quis ultricies ultricies. Nunc tortor ligula, dapibus vitae tortor eget, consequat varius lorem. Sed hendrerit et lacus egestas malesuada. Nulla facilisi. Nullam quam ipsum, molestie vitae nibh id, molestie blandit sapien. Etiam sed erat scelerisque libero vehicula porttitor a id arcu. Nullam tincidunt nisi sapien, vitae lobortis odio semper vel. Quisque malesuada imperdiet velit, et cursus tortor imperdiet ac.
+
+Nullam porta mi in libero pellentesque, non pellentesque massa venenatis. Proin nunc mauris, malesuada non nulla eget, pellentesque varius urna. Nulla at pellentesque arcu, eget gravida ipsum. Aliquam feugiat neque in augue fringilla placerat. Donec ac efficitur tellus. Nullam ullamcorper, neque eu tempor pellentesque, enim lectus fringilla ipsum, et efficitur eros nibh quis ligula. Maecenas gravida maximus sem ut posuere. Praesent hendrerit ante vitae elit fermentum vulputate. Quisque auctor, tellus sed mollis ullamcorper, dolor lacus pellentesque mi, hendrerit viverra felis nunc eget purus. Praesent cursus, risus non dapibus pellentesque, dolor lacus finibus lacus, et ultrices erat felis et felis. Mauris ultrices orci nec neque faucibus elementum. Vivamus mauris lectus, rutrum ac aliquet id, venenatis bibendum odio. Nunc vitae mollis libero. Nullam aliquet, leo non tempor posuere, sem quam aliquam leo, sed porta leo lorem eget tellus. Quisque consequat purus ut porta placerat.
+
+##  eleifend metus
+
+Donec erat mi, tincidunt nec diam non, lacinia molestie velit. Quisque quis mattis odio. Mauris non posuere libero. Mauris posuere consectetur aliquam. Donec lacus lectus, cursus ut fermentum fringilla, malesuada a lacus. Sed nec blandit nisi, at eleifend metus. Morbi feugiat purus nec enim mollis, id volutpat lectus dignissim.
+
+ Nunc et tellus libero. Aliquam nec augue interdum, laoreet eros vehicula, euismod tellus. Aenean vestibulum ipsum sed enim interdum, placerat consequat sem mollis. Curabitur varius aliquet efficitur. Vivamus cursus cursus ornare. Morbi rhoncus eros a ligula suscipit eleifend. Curabitur laoreet tempus tellus ac semper. Integer at nunc ac arcu dictum vehicula. Nulla facilisi. In iaculis risus elementum, blandit tellus eu, gravida massa. Ut mattis odio ut commodo ullamcorper. Nunc mauris ante, fringilla ut porta a, sagittis rutrum lectus. Suspendisse lobortis velit mi, in imperdiet nulla consequat a. Nam faucibus facilisis volutpat. Maecenas augue odio, ornare at cursus id, faucibus sed nulla. Aenean non magna sagittis, rhoncus orci eu, finibus lectus.
+
+Interdum et malesuada fames ac ante ipsum primis in faucibus. Vivamus sagittis aliquam odio, a iaculis orci. Donec tellus sapien, mollis ut orci mollis, viverra blandit mauris. Nunc nisl magna, tempor porta viverra ut, feugiat sed metus. Cras eu orci ultrices, vehicula magna ut, auctor mi. Pellentesque vestibulum at lacus ac consequat. Ut dictum ligula id condimentum maximus. Pellentesque consequat libero lacus, a tempor lectus finibus quis. Etiam blandit a arcu sed condimentum. Cras ac scelerisque justo, vel imperdiet felis. Phasellus id nisl volutpat nibh fermentum condimentum nec ut orci.
+
+## Morbi vulputate
+
+Aliquet varius. Cras sem quam, pulvinar sed semper nec, molestie nec nisl. Nam vehicula quam et magna luctus venenatis. Sed porttitor nisl eu cursus consectetur. Etiam vel pretium leo. Integer nec condimentum ex, vitae feugiat quam. Vivamus vel eros vel elit commodo sagittis. Cras tempus urna et nisl ornare, id sagittis quam porta. Curabitur facilisis malesuada diam, nec pellentesque sem pellentesque feugiat. Aliquam blandit massa augue, vitae malesuada orci iaculis in. Praesent dignissim mi quis odio ornare fringilla. Integer consectetur varius fringilla. Nulla molestie egestas risus quis molestie. Vestibulum porta orci feugiat feugiat rutrum.
+
+Vivamus quis sagittis metus. Integer nec commodo magna. Sed aliquet augue vulputate volutpat aliquet. Nunc eu nisi elit. Duis ut risus nec risus venenatis ornare. Vestibulum malesuada ultrices varius. Etiam consectetur ullamcorper elit, at fermentum purus semper ut. Quisque venenatis tempor blandit.
+
+Nunc nec tincidunt felis. Nulla ac enim consequat, maximus elit id, molestie ligula. Donec tortor lacus, mollis et orci ac, mollis fringilla metus. Sed ac feugiat elit. Mauris commodo nulla et ipsum scelerisque, non pretium ipsum egestas. Mauris fringilla mattis turpis. Donec dignissim lectus eget lacus lobortis, non egestas justo dictum. Interdum et malesuada fames ac ante ipsum primis in faucibus. Vivamus tortor elit, elementum lacinia malesuada ac, venenatis vitae velit.
+
+Proin ipsum orci, rutrum nec neque nec, tempor efficitur sem. Phasellus in elit iaculis dolor cursus efficitur. Donec quis vulputate elit. Ut at odio mauris. Pellentesque sit amet maximus velit. Aliquam ac ex a neque ullamcorper venenatis eget et massa. Donec convallis felis eget mauris consectetur, in convallis justo semper. Donec dignissim consequat eleifend. Pellentesque augue nulla, placerat vitae urna a, mattis sodales erat. Nullam et malesuada risus, et ultrices ipsum. Integer ac placerat enim. Integer vel faucibus nisi.
+
+## Orci varius natoque
+
+Penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed venenatis dolor ac enim pulvinar cursus a non nulla. Aliquam commodo, nisi a fermentum mollis, risus justo volutpat diam, id varius felis magna feugiat tellus. Sed at porta metus. Praesent fermentum, nisl vel lobortis venenatis, odio turpis malesuada tortor, ultrices pellentesque quam ex eu orci. Sed laoreet non lorem eu gravida. Morbi quis felis a massa facilisis accumsan. Suspendisse rhoncus purus neque, in eleifend augue hendrerit id. Phasellus luctus a sem sit amet accumsan. Quisque tempor velit at pulvinar varius. Pellentesque ex felis, imperdiet gravida neque non, congue venenatis ex. Aenean iaculis cursus turpis eget luctus. Praesent nec eleifend lectus.
+
+Aenean luctus quam dolor, et ultrices odio rutrum nec. Sed pulvinar, felis at elementum venenatis, lacus purus convallis mi, sed ultrices nisl orci id urna. Curabitur dapibus volutpat hendrerit. In molestie, purus eu sollicitudin semper, leo ante placerat odio, at sollicitudin diam tellus sit amet metus. Morbi hendrerit est ac erat scelerisque condimentum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Proin egestas orci ac sapien blandit laoreet.
+
+Vivamus sollicitudin sed turpis non placerat. Cras mollis lobortis elit vitae vestibulum. Donec ornare, tortor interdum lacinia egestas, sem arcu sodales ipsum, ac ultrices justo elit sit amet ipsum. Cras sapien leo, tincidunt pretium pretium quis, egestas non mauris. Aenean ac fermentum ante. Mauris rhoncus enim ante, a pellentesque mauris mattis sed. Nam mattis, ante eu pretium tempus, risus magna iaculis erat, eget imperdiet nulla neque sed nulla. Cras gravida libero neque, quis pulvinar ante fringilla quis. In ut lobortis velit. Suspendisse congue arcu ligula, at aliquam ipsum pharetra sit amet. Mauris ut enim eget massa dignissim bibendum. Vivamus purus nisl, fermentum efficitur elementum nec, lacinia at tortor. Donec aliquam tellus vel nunc tristique, id pulvinar sem facilisis.
+
+| 123123 | 123131 |
+|---|---|
+|  Donec ornare, tortor interdum lacinia egestas, sem arcu sodales ipsum, ac ultrices justo elit sit amet ipsum. Cras sapien leo, tincidunt pretium pretium quis, egestas non mauris. Aenean ac fermentum ante. Mauris rhoncus enim ante, a pellentesque mauris mattis sed. Nam mattis, ante eu pretium tempus, risus magna iaculis erat, eget imperdiet nulla neque sed nulla. Cras gravida libero neque, quis pulvinar ante fringilla quis. In ut lobortis velit. Suspendisse congue arcu ligula, at aliquam ipsum pharetra sit amet. Mauris ut enim eget massa dignissim bibendum. Vivamus purus nisl, fermentum efficitur elementum nec, lacinia at tortor. Donec aliquam tellus vel nunc tristique, id pulvinar sem facilisis. | Curabitur dapibus volutpat hendrerit. In molestie, purus eu sollicitudin semper, leo ante placerat odio, at sollicitudin diam tellus sit amet metus. Morbi hendrerit est ac erat scelerisque condimentum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Proin egestas orci ac sapien blandit laoreet. |
+|  Donec ornare, tortor interdum lacinia egestas, sem arcu sodales ipsum, ac ultrices justo elit sit amet ipsum. Cras sapien leo, tincidunt pretium pretium quis, egestas non mauris. Aenean ac fermentum ante. Mauris rhoncus enim ante, a pellentesque mauris mattis sed. Nam mattis, ante eu pretium tempus, risus magna iaculis erat, eget imperdiet nulla neque sed nulla. Cras gravida libero neque, quis pulvinar ante fringilla quis. In ut lobortis velit. Suspendisse congue arcu ligula, at aliquam ipsum pharetra sit amet. Mauris ut enim eget massa dignissim bibendum. Vivamus purus nisl, fermentum efficitur elementum nec, lacinia at tortor. Donec aliquam tellus vel nunc tristique, id pulvinar sem facilisis. | Curabitur dapibus volutpat hendrerit. In molestie, purus eu sollicitudin semper, leo ante placerat odio, at sollicitudin diam tellus sit amet metus. Morbi hendrerit est ac erat scelerisque condimentum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Proin egestas orci ac sapien blandit laoreet. |
+|  Donec ornare, tortor interdum lacinia egestas, sem arcu sodales ipsum, ac ultrices justo elit sit amet ipsum. Cras sapien leo, tincidunt pretium pretium quis, egestas non mauris. Aenean ac fermentum ante. Mauris rhoncus enim ante, a pellentesque mauris mattis sed. Nam mattis, ante eu pretium tempus, risus magna iaculis erat, eget imperdiet nulla neque sed nulla. Cras gravida libero neque, quis pulvinar ante fringilla quis. In ut lobortis velit. Suspendisse congue arcu ligula, at aliquam ipsum pharetra sit amet. Mauris ut enim eget massa dignissim bibendum. Vivamus purus nisl, fermentum efficitur elementum nec, lacinia at tortor. Donec aliquam tellus vel nunc tristique, id pulvinar sem facilisis. | Curabitur dapibus volutpat hendrerit. In molestie, purus eu sollicitudin semper, leo ante placerat odio, at sollicitudin diam tellus sit amet metus. Morbi hendrerit est ac erat scelerisque condimentum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Proin egestas orci ac sapien blandit laoreet. |
+|  Donec ornare, tortor interdum lacinia egestas, sem arcu sodales ipsum, ac ultrices justo elit sit amet ipsum. Cras sapien leo, tincidunt pretium pretium quis, egestas non mauris. Aenean ac fermentum ante. Mauris rhoncus enim ante, a pellentesque mauris mattis sed. Nam mattis, ante eu pretium tempus, risus magna iaculis erat, eget imperdiet nulla neque sed nulla. Cras gravida libero neque, quis pulvinar ante fringilla quis. In ut lobortis velit. Suspendisse congue arcu ligula, at aliquam ipsum pharetra sit amet. Mauris ut enim eget massa dignissim bibendum. Vivamus purus nisl, fermentum efficitur elementum nec, lacinia at tortor. Donec aliquam tellus vel nunc tristique, id pulvinar sem facilisis. | Curabitur dapibus volutpat hendrerit. In molestie, purus eu sollicitudin semper, leo ante placerat odio, at sollicitudin diam tellus sit amet metus. Morbi hendrerit est ac erat scelerisque condimentum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Proin egestas orci ac sapien blandit laoreet. |
+
+Phasellus convallis, velit eget blandit tempus, leo nisi placerat leo, ac blandit metus ligula vitae enim. Duis a felis in dui venenatis tristique ac quis mauris. Sed interdum dui sit amet tincidunt imperdiet. Integer consequat purus ut porta ornare. Morbi vel sem interdum, tempor lacus interdum, eleifend ligula. Vivamus augue nulla, finibus eget sapien nec, euismod maximus nunc. Aliquam non hendrerit massa.
+
+Vestibulum at justo metus. Duis at ultrices magna, vitae varius neque. Donec et tellus vitae turpis bibendum posuere. Nunc et sagittis nulla. Phasellus egestas feugiat mattis. Fusce eu felis ac tellus tempus egestas vel non eros. Integer faucibus euismod purus, non euismod sem. Cras sodales risus in libero eleifend molestie. Mauris convallis accumsan lectus pellentesque laoreet. Aenean sagittis enim sit amet orci ullamcorper, in semper tellus blandit. Pellentesque orci ex, tempor sed semper et, vehicula at turpis. Sed id finibus ex. Nullam metus neque, sollicitudin id urna eget, commodo tincidunt est. Donec porttitor nisi sit amet arcu dapibus rutrum.
+
+Sed ultrices tempus purus, ullamcorper sodales libero vestibulum luctus. Proin ut commodo justo, vitae dignissim leo. Mauris condimentum dolor ut purus ultricies, at consequat eros gravida. Phasellus quis eros sed tellus molestie venenatis ut vitae nibh. Maecenas mattis commodo neque vel bibendum. Curabitur eu porta risus. Maecenas mollis dignissim interdum. Integer ac pharetra lacus, vitae condimentum ex. Mauris at quam ornare, sollicitudin turpis vitae, aliquam tellus. Nam lacinia sem at lobortis ultrices. In vulputate odio a quam semper maximus at et massa. Nam massa diam, tincidunt at ex sit amet, placerat fringilla eros. Etiam quis semper turpis. Aliquam laoreet, nisi tempor dignissim suscipit, tellus nunc mattis tellus, luctus euismod massa leo id ex.
+
+Nullam placerat malesuada dui, vitae feugiat velit. Fusce finibus neque non massa tincidunt imperdiet. Nam scelerisque bibendum neque. In hac habitasse platea dictumst. Donec erat odio, rutrum mollis justo eu, interdum pharetra urna. Mauris eros felis, vulputate a lacus eu, euismod pulvinar felis. Curabitur euismod nulla ut auctor mattis. Sed molestie ligula ac lacus commodo, vitae vehicula leo blandit. Nullam et finibus turpis. Integer posuere diam urna, ultrices suscipit augue tincidunt vel.
+
+Maecenas consequat velit nec orci vestibulum, in interdum magna condimentum. Duis faucibus tellus in elit pharetra, ut pulvinar libero vulputate. Curabitur in eros a urna condimentum iaculis elementum at elit. In id ullamcorper risus, et iaculis est. Vestibulum ac tellus vitae neque volutpat vehicula. Donec pulvinar nibh vitae eros ullamcorper consequat. Fusce vitae quam blandit est blandit convallis. Donec non dignissim ipsum. Donec libero velit, fermentum quis leo a, tempor rutrum nunc. Nunc tempor ligula sapien, id pellentesque sapien dapibus vitae. Pellentesque nisl elit, varius elementum sapien sed, tempus dictum leo. Fusce magna orci, ornare eu luctus ac, feugiat vitae urna. Vestibulum vel metus ornare, varius velit et, congue risus. Quisque id purus sit amet mi consectetur interdum eget id magna.
+
+[LAMDWIKIPOST 20220729095433; VER 20220729101916; ]
+
+# Lorem Ipsum
+
+{wide}
+
+## at porta metus
+
+{read_more}
+
+Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus in placerat lacus. Pellentesque ac nibh in enim sodales ullamcorper eget at arcu. Morbi imperdiet neque tempor, cursus purus ut, congue mi. Morbi sit amet erat nibh. Pellentesque at odio ut odio elementum porta. Etiam volutpat purus ac dolor elementum hendrerit quis laoreet nisi. Curabitur porttitor aliquet aliquet. Duis tristique felis arcu, a vestibulum nisl dignissim ac. Nam laoreet lacinia erat id suscipit. Suspendisse iaculis lacinia eleifend. Nullam ut turpis ante.
+
+Nullam ipsum nulla, pellentesque tristique ultrices id, posuere vitae erat. Donec turpis purus, hendrerit at lectus eu, lobortis vestibulum orci. Quisque vel ligula nec odio faucibus elementum mollis at nulla. Praesent efficitur metus in purus egestas sodales. Integer finibus mattis odio ut accumsan. Donec convallis, erat ut consectetur hendrerit, urna augue dignissim velit, non mattis diam justo sed magna. Fusce ut velit ut massa porta egestas a at massa. Aenean fermentum varius purus a congue. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.
+
+Fusce tempus a tortor non facilisis. Vestibulum consectetur bibendum sagittis. Suspendisse nec urna sit amet massa pulvinar pretium. Maecenas suscipit accumsan mi eu dignissim. In dictum pellentesque gravida. Etiam posuere diam quis ultricies ultricies. Nunc tortor ligula, dapibus vitae tortor eget, consequat varius lorem. Sed hendrerit et lacus egestas malesuada. Nulla facilisi. Nullam quam ipsum, molestie vitae nibh id, molestie blandit sapien. Etiam sed erat scelerisque libero vehicula porttitor a id arcu. Nullam tincidunt nisi sapien, vitae lobortis odio semper vel. Quisque malesuada imperdiet velit, et cursus tortor imperdiet ac.
+
+Nullam porta mi in libero pellentesque, non pellentesque massa venenatis. Proin nunc mauris, malesuada non nulla eget, pellentesque varius urna. Nulla at pellentesque arcu, eget gravida ipsum. Aliquam feugiat neque in augue fringilla placerat. Donec ac efficitur tellus. Nullam ullamcorper, neque eu tempor pellentesque, enim lectus fringilla ipsum, et efficitur eros nibh quis ligula. Maecenas gravida maximus sem ut posuere. Praesent hendrerit ante vitae elit fermentum vulputate. Quisque auctor, tellus sed mollis ullamcorper, dolor lacus pellentesque mi, hendrerit viverra felis nunc eget purus. Praesent cursus, risus non dapibus pellentesque, dolor lacus finibus lacus, et ultrices erat felis et felis. Mauris ultrices orci nec neque faucibus elementum. Vivamus mauris lectus, rutrum ac aliquet id, venenatis bibendum odio. Nunc vitae mollis libero. Nullam aliquet, leo non tempor posuere, sem quam aliquam leo, sed porta leo lorem eget tellus. Quisque consequat purus ut porta placerat.
+
+##  eleifend metus
+
+Donec erat mi, tincidunt nec diam non, lacinia molestie velit. Quisque quis mattis odio. Mauris non posuere libero. Mauris posuere consectetur aliquam. Donec lacus lectus, cursus ut fermentum fringilla, malesuada a lacus. Sed nec blandit nisi, at eleifend metus. Morbi feugiat purus nec enim mollis, id volutpat lectus dignissim.
+
+ Nunc et tellus libero. Aliquam nec augue interdum, laoreet eros vehicula, euismod tellus. Aenean vestibulum ipsum sed enim interdum, placerat consequat sem mollis. Curabitur varius aliquet efficitur. Vivamus cursus cursus ornare. Morbi rhoncus eros a ligula suscipit eleifend. Curabitur laoreet tempus tellus ac semper. Integer at nunc ac arcu dictum vehicula. Nulla facilisi. In iaculis risus elementum, blandit tellus eu, gravida massa. Ut mattis odio ut commodo ullamcorper. Nunc mauris ante, fringilla ut porta a, sagittis rutrum lectus. Suspendisse lobortis velit mi, in imperdiet nulla consequat a. Nam faucibus facilisis volutpat. Maecenas augue odio, ornare at cursus id, faucibus sed nulla. Aenean non magna sagittis, rhoncus orci eu, finibus lectus.
+
+Interdum et malesuada fames ac ante ipsum primis in faucibus. Vivamus sagittis aliquam odio, a iaculis orci. Donec tellus sapien, mollis ut orci mollis, viverra blandit mauris. Nunc nisl magna, tempor porta viverra ut, feugiat sed metus. Cras eu orci ultrices, vehicula magna ut, auctor mi. Pellentesque vestibulum at lacus ac consequat. Ut dictum ligula id condimentum maximus. Pellentesque consequat libero lacus, a tempor lectus finibus quis. Etiam blandit a arcu sed condimentum. Cras ac scelerisque justo, vel imperdiet felis. Phasellus id nisl volutpat nibh fermentum condimentum nec ut orci.
+
+## Morbi vulputate
+
+Aliquet varius. Cras sem quam, pulvinar sed semper nec, molestie nec nisl. Nam vehicula quam et magna luctus venenatis. Sed porttitor nisl eu cursus consectetur. Etiam vel pretium leo. Integer nec condimentum ex, vitae feugiat quam. Vivamus vel eros vel elit commodo sagittis. Cras tempus urna et nisl ornare, id sagittis quam porta. Curabitur facilisis malesuada diam, nec pellentesque sem pellentesque feugiat. Aliquam blandit massa augue, vitae malesuada orci iaculis in. Praesent dignissim mi quis odio ornare fringilla. Integer consectetur varius fringilla. Nulla molestie egestas risus quis molestie. Vestibulum porta orci feugiat feugiat rutrum.
+
+Vivamus quis sagittis metus. Integer nec commodo magna. Sed aliquet augue vulputate volutpat aliquet. Nunc eu nisi elit. Duis ut risus nec risus venenatis ornare. Vestibulum malesuada ultrices varius. Etiam consectetur ullamcorper elit, at fermentum purus semper ut. Quisque venenatis tempor blandit.
+
+Nunc nec tincidunt felis. Nulla ac enim consequat, maximus elit id, molestie ligula. Donec tortor lacus, mollis et orci ac, mollis fringilla metus. Sed ac feugiat elit. Mauris commodo nulla et ipsum scelerisque, non pretium ipsum egestas. Mauris fringilla mattis turpis. Donec dignissim lectus eget lacus lobortis, non egestas justo dictum. Interdum et malesuada fames ac ante ipsum primis in faucibus. Vivamus tortor elit, elementum lacinia malesuada ac, venenatis vitae velit.
+
+Proin ipsum orci, rutrum nec neque nec, tempor efficitur sem. Phasellus in elit iaculis dolor cursus efficitur. Donec quis vulputate elit. Ut at odio mauris. Pellentesque sit amet maximus velit. Aliquam ac ex a neque ullamcorper venenatis eget et massa. Donec convallis felis eget mauris consectetur, in convallis justo semper. Donec dignissim consequat eleifend. Pellentesque augue nulla, placerat vitae urna a, mattis sodales erat. Nullam et malesuada risus, et ultrices ipsum. Integer ac placerat enim. Integer vel faucibus nisi.
+
+## Orci varius natoque
+
+Penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed venenatis dolor ac enim pulvinar cursus a non nulla. Aliquam commodo, nisi a fermentum mollis, risus justo volutpat diam, id varius felis magna feugiat tellus. Sed at porta metus. Praesent fermentum, nisl vel lobortis venenatis, odio turpis malesuada tortor, ultrices pellentesque quam ex eu orci. Sed laoreet non lorem eu gravida. Morbi quis felis a massa facilisis accumsan. Suspendisse rhoncus purus neque, in eleifend augue hendrerit id. Phasellus luctus a sem sit amet accumsan. Quisque tempor velit at pulvinar varius. Pellentesque ex felis, imperdiet gravida neque non, congue venenatis ex. Aenean iaculis cursus turpis eget luctus. Praesent nec eleifend lectus.
+
+Aenean luctus quam dolor, et ultrices odio rutrum nec. Sed pulvinar, felis at elementum venenatis, lacus purus convallis mi, sed ultrices nisl orci id urna. Curabitur dapibus volutpat hendrerit. In molestie, purus eu sollicitudin semper, leo ante placerat odio, at sollicitudin diam tellus sit amet metus. Morbi hendrerit est ac erat scelerisque condimentum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Proin egestas orci ac sapien blandit laoreet.
+
+Vivamus sollicitudin sed turpis non placerat. Cras mollis lobortis elit vitae vestibulum. Donec ornare, tortor interdum lacinia egestas, sem arcu sodales ipsum, ac ultrices justo elit sit amet ipsum. Cras sapien leo, tincidunt pretium pretium quis, egestas non mauris. Aenean ac fermentum ante. Mauris rhoncus enim ante, a pellentesque mauris mattis sed. Nam mattis, ante eu pretium tempus, risus magna iaculis erat, eget imperdiet nulla neque sed nulla. Cras gravida libero neque, quis pulvinar ante fringilla quis. In ut lobortis velit. Suspendisse congue arcu ligula, at aliquam ipsum pharetra sit amet. Mauris ut enim eget massa dignissim bibendum. Vivamus purus nisl, fermentum efficitur elementum nec, lacinia at tortor. Donec aliquam tellus vel nunc tristique, id pulvinar sem facilisis.
+
+| 123123 | 123131 |
+|---|---|
+|  Donec ornare, tortor interdum lacinia egestas, sem arcu sodales ipsum, ac ultrices justo elit sit amet ipsum. Cras sapien leo, tincidunt pretium pretium quis, egestas non mauris. Aenean ac fermentum ante. Mauris rhoncus enim ante, a pellentesque mauris mattis sed. Nam mattis, ante eu pretium tempus, risus magna iaculis erat, eget imperdiet nulla neque sed nulla. Cras gravida libero neque, quis pulvinar ante fringilla quis. In ut lobortis velit. Suspendisse congue arcu ligula, at aliquam ipsum pharetra sit amet. Mauris ut enim eget massa dignissim bibendum. Vivamus purus nisl, fermentum efficitur elementum nec, lacinia at tortor. Donec aliquam tellus vel nunc tristique, id pulvinar sem facilisis. | Curabitur dapibus volutpat hendrerit. In molestie, purus eu sollicitudin semper, leo ante placerat odio, at sollicitudin diam tellus sit amet metus. Morbi hendrerit est ac erat scelerisque condimentum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Proin egestas orci ac sapien blandit laoreet. |
+|  Donec ornare, tortor interdum lacinia egestas, sem arcu sodales ipsum, ac ultrices justo elit sit amet ipsum. Cras sapien leo, tincidunt pretium pretium quis, egestas non mauris. Aenean ac fermentum ante. Mauris rhoncus enim ante, a pellentesque mauris mattis sed. Nam mattis, ante eu pretium tempus, risus magna iaculis erat, eget imperdiet nulla neque sed nulla. Cras gravida libero neque, quis pulvinar ante fringilla quis. In ut lobortis velit. Suspendisse congue arcu ligula, at aliquam ipsum pharetra sit amet. Mauris ut enim eget massa dignissim bibendum. Vivamus purus nisl, fermentum efficitur elementum nec, lacinia at tortor. Donec aliquam tellus vel nunc tristique, id pulvinar sem facilisis. | Curabitur dapibus volutpat hendrerit. In molestie, purus eu sollicitudin semper, leo ante placerat odio, at sollicitudin diam tellus sit amet metus. Morbi hendrerit est ac erat scelerisque condimentum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Proin egestas orci ac sapien blandit laoreet. |
+|  Donec ornare, tortor interdum lacinia egestas, sem arcu sodales ipsum, ac ultrices justo elit sit amet ipsum. Cras sapien leo, tincidunt pretium pretium quis, egestas non mauris. Aenean ac fermentum ante. Mauris rhoncus enim ante, a pellentesque mauris mattis sed. Nam mattis, ante eu pretium tempus, risus magna iaculis erat, eget imperdiet nulla neque sed nulla. Cras gravida libero neque, quis pulvinar ante fringilla quis. In ut lobortis velit. Suspendisse congue arcu ligula, at aliquam ipsum pharetra sit amet. Mauris ut enim eget massa dignissim bibendum. Vivamus purus nisl, fermentum efficitur elementum nec, lacinia at tortor. Donec aliquam tellus vel nunc tristique, id pulvinar sem facilisis. | Curabitur dapibus volutpat hendrerit. In molestie, purus eu sollicitudin semper, leo ante placerat odio, at sollicitudin diam tellus sit amet metus. Morbi hendrerit est ac erat scelerisque condimentum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Proin egestas orci ac sapien blandit laoreet. |
+|  Donec ornare, tortor interdum lacinia egestas, sem arcu sodales ipsum, ac ultrices justo elit sit amet ipsum. Cras sapien leo, tincidunt pretium pretium quis, egestas non mauris. Aenean ac fermentum ante. Mauris rhoncus enim ante, a pellentesque mauris mattis sed. Nam mattis, ante eu pretium tempus, risus magna iaculis erat, eget imperdiet nulla neque sed nulla. Cras gravida libero neque, quis pulvinar ante fringilla quis. In ut lobortis velit. Suspendisse congue arcu ligula, at aliquam ipsum pharetra sit amet. Mauris ut enim eget massa dignissim bibendum. Vivamus purus nisl, fermentum efficitur elementum nec, lacinia at tortor. Donec aliquam tellus vel nunc tristique, id pulvinar sem facilisis. | Curabitur dapibus volutpat hendrerit. In molestie, purus eu sollicitudin semper, leo ante placerat odio, at sollicitudin diam tellus sit amet metus. Morbi hendrerit est ac erat scelerisque condimentum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Proin egestas orci ac sapien blandit laoreet. |
+
+Phasellus convallis, velit eget blandit tempus, leo nisi placerat leo, ac blandit metus ligula vitae enim. Duis a felis in dui venenatis tristique ac quis mauris. Sed interdum dui sit amet tincidunt imperdiet. Integer consequat purus ut porta ornare. Morbi vel sem interdum, tempor lacus interdum, eleifend ligula. Vivamus augue nulla, finibus eget sapien nec, euismod maximus nunc. Aliquam non hendrerit massa.
+
+Vestibulum at justo metus. Duis at ultrices magna, vitae varius neque. Donec et tellus vitae turpis bibendum posuere. Nunc et sagittis nulla. Phasellus egestas feugiat mattis. Fusce eu felis ac tellus tempus egestas vel non eros. Integer faucibus euismod purus, non euismod sem. Cras sodales risus in libero eleifend molestie. Mauris convallis accumsan lectus pellentesque laoreet. Aenean sagittis enim sit amet orci ullamcorper, in semper tellus blandit. Pellentesque orci ex, tempor sed semper et, vehicula at turpis. Sed id finibus ex. Nullam metus neque, sollicitudin id urna eget, commodo tincidunt est. Donec porttitor nisi sit amet arcu dapibus rutrum.
+
+Sed ultrices tempus purus, ullamcorper sodales libero vestibulum luctus. Proin ut commodo justo, vitae dignissim leo. Mauris condimentum dolor ut purus ultricies, at consequat eros gravida. Phasellus quis eros sed tellus molestie venenatis ut vitae nibh. Maecenas mattis commodo neque vel bibendum. Curabitur eu porta risus. Maecenas mollis dignissim interdum. Integer ac pharetra lacus, vitae condimentum ex. Mauris at quam ornare, sollicitudin turpis vitae, aliquam tellus. Nam lacinia sem at lobortis ultrices. In vulputate odio a quam semper maximus at et massa. Nam massa diam, tincidunt at ex sit amet, placerat fringilla eros. Etiam quis semper turpis. Aliquam laoreet, nisi tempor dignissim suscipit, tellus nunc mattis tellus, luctus euismod massa leo id ex.
+
+Nullam placerat malesuada dui, vitae feugiat velit. Fusce finibus neque non massa tincidunt imperdiet. Nam scelerisque bibendum neque. In hac habitasse platea dictumst. Donec erat odio, rutrum mollis justo eu, interdum pharetra urna. Mauris eros felis, vulputate a lacus eu, euismod pulvinar felis. Curabitur euismod nulla ut auctor mattis. Sed molestie ligula ac lacus commodo, vitae vehicula leo blandit. Nullam et finibus turpis. Integer posuere diam urna, ultrices suscipit augue tincidunt vel.
+
+Maecenas consequat velit nec orci vestibulum, in interdum magna condimentum. Duis faucibus tellus in elit pharetra, ut pulvinar libero vulputate. Curabitur in eros a urna condimentum iaculis elementum at elit. In id ullamcorper risus, et iaculis est. Vestibulum ac tellus vitae neque volutpat vehicula. Donec pulvinar nibh vitae eros ullamcorper consequat. Fusce vitae quam blandit est blandit convallis. Donec non dignissim ipsum. Donec libero velit, fermentum quis leo a, tempor rutrum nunc. Nunc tempor ligula sapien, id pellentesque sapien dapibus vitae. Pellentesque nisl elit, varius elementum sapien sed, tempus dictum leo. Fusce magna orci, ornare eu luctus ac, feugiat vitae urna. Vestibulum vel metus ornare, varius velit et, congue risus. Quisque id purus sit amet mi consectetur interdum eget id magna.
+
+[LAMDWIKIPOST 20220729095433; VER 20220729101932; ]
+
+# Lorem Ipsum
+
+@ 射精
+
+{wide}
+
+## at porta metus
+
+{read_more}
+
+Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus in placerat lacus. Pellentesque ac nibh in enim sodales ullamcorper eget at arcu. Morbi imperdiet neque tempor, cursus purus ut, congue mi. Morbi sit amet erat nibh. Pellentesque at odio ut odio elementum porta. Etiam volutpat purus ac dolor elementum hendrerit quis laoreet nisi. Curabitur porttitor aliquet aliquet. Duis tristique felis arcu, a vestibulum nisl dignissim ac. Nam laoreet lacinia erat id suscipit. Suspendisse iaculis lacinia eleifend. Nullam ut turpis ante.
+
+Nullam ipsum nulla, pellentesque tristique ultrices id, posuere vitae erat. Donec turpis purus, hendrerit at lectus eu, lobortis vestibulum orci. Quisque vel ligula nec odio faucibus elementum mollis at nulla. Praesent efficitur metus in purus egestas sodales. Integer finibus mattis odio ut accumsan. Donec convallis, erat ut consectetur hendrerit, urna augue dignissim velit, non mattis diam justo sed magna. Fusce ut velit ut massa porta egestas a at massa. Aenean fermentum varius purus a congue. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.
+
+Fusce tempus a tortor non facilisis. Vestibulum consectetur bibendum sagittis. Suspendisse nec urna sit amet massa pulvinar pretium. Maecenas suscipit accumsan mi eu dignissim. In dictum pellentesque gravida. Etiam posuere diam quis ultricies ultricies. Nunc tortor ligula, dapibus vitae tortor eget, consequat varius lorem. Sed hendrerit et lacus egestas malesuada. Nulla facilisi. Nullam quam ipsum, molestie vitae nibh id, molestie blandit sapien. Etiam sed erat scelerisque libero vehicula porttitor a id arcu. Nullam tincidunt nisi sapien, vitae lobortis odio semper vel. Quisque malesuada imperdiet velit, et cursus tortor imperdiet ac.
+
+Nullam porta mi in libero pellentesque, non pellentesque massa venenatis. Proin nunc mauris, malesuada non nulla eget, pellentesque varius urna. Nulla at pellentesque arcu, eget gravida ipsum. Aliquam feugiat neque in augue fringilla placerat. Donec ac efficitur tellus. Nullam ullamcorper, neque eu tempor pellentesque, enim lectus fringilla ipsum, et efficitur eros nibh quis ligula. Maecenas gravida maximus sem ut posuere. Praesent hendrerit ante vitae elit fermentum vulputate. Quisque auctor, tellus sed mollis ullamcorper, dolor lacus pellentesque mi, hendrerit viverra felis nunc eget purus. Praesent cursus, risus non dapibus pellentesque, dolor lacus finibus lacus, et ultrices erat felis et felis. Mauris ultrices orci nec neque faucibus elementum. Vivamus mauris lectus, rutrum ac aliquet id, venenatis bibendum odio. Nunc vitae mollis libero. Nullam aliquet, leo non tempor posuere, sem quam aliquam leo, sed porta leo lorem eget tellus. Quisque consequat purus ut porta placerat.
+
+##  eleifend metus
+
+Donec erat mi, tincidunt nec diam non, lacinia molestie velit. Quisque quis mattis odio. Mauris non posuere libero. Mauris posuere consectetur aliquam. Donec lacus lectus, cursus ut fermentum fringilla, malesuada a lacus. Sed nec blandit nisi, at eleifend metus. Morbi feugiat purus nec enim mollis, id volutpat lectus dignissim.
+
+ Nunc et tellus libero. Aliquam nec augue interdum, laoreet eros vehicula, euismod tellus. Aenean vestibulum ipsum sed enim interdum, placerat consequat sem mollis. Curabitur varius aliquet efficitur. Vivamus cursus cursus ornare. Morbi rhoncus eros a ligula suscipit eleifend. Curabitur laoreet tempus tellus ac semper. Integer at nunc ac arcu dictum vehicula. Nulla facilisi. In iaculis risus elementum, blandit tellus eu, gravida massa. Ut mattis odio ut commodo ullamcorper. Nunc mauris ante, fringilla ut porta a, sagittis rutrum lectus. Suspendisse lobortis velit mi, in imperdiet nulla consequat a. Nam faucibus facilisis volutpat. Maecenas augue odio, ornare at cursus id, faucibus sed nulla. Aenean non magna sagittis, rhoncus orci eu, finibus lectus.
+
+Interdum et malesuada fames ac ante ipsum primis in faucibus. Vivamus sagittis aliquam odio, a iaculis orci. Donec tellus sapien, mollis ut orci mollis, viverra blandit mauris. Nunc nisl magna, tempor porta viverra ut, feugiat sed metus. Cras eu orci ultrices, vehicula magna ut, auctor mi. Pellentesque vestibulum at lacus ac consequat. Ut dictum ligula id condimentum maximus. Pellentesque consequat libero lacus, a tempor lectus finibus quis. Etiam blandit a arcu sed condimentum. Cras ac scelerisque justo, vel imperdiet felis. Phasellus id nisl volutpat nibh fermentum condimentum nec ut orci.
+
+## Morbi vulputate
+
+Aliquet varius. Cras sem quam, pulvinar sed semper nec, molestie nec nisl. Nam vehicula quam et magna luctus venenatis. Sed porttitor nisl eu cursus consectetur. Etiam vel pretium leo. Integer nec condimentum ex, vitae feugiat quam. Vivamus vel eros vel elit commodo sagittis. Cras tempus urna et nisl ornare, id sagittis quam porta. Curabitur facilisis malesuada diam, nec pellentesque sem pellentesque feugiat. Aliquam blandit massa augue, vitae malesuada orci iaculis in. Praesent dignissim mi quis odio ornare fringilla. Integer consectetur varius fringilla. Nulla molestie egestas risus quis molestie. Vestibulum porta orci feugiat feugiat rutrum.
+
+Vivamus quis sagittis metus. Integer nec commodo magna. Sed aliquet augue vulputate volutpat aliquet. Nunc eu nisi elit. Duis ut risus nec risus venenatis ornare. Vestibulum malesuada ultrices varius. Etiam consectetur ullamcorper elit, at fermentum purus semper ut. Quisque venenatis tempor blandit.
+
+Nunc nec tincidunt felis. Nulla ac enim consequat, maximus elit id, molestie ligula. Donec tortor lacus, mollis et orci ac, mollis fringilla metus. Sed ac feugiat elit. Mauris commodo nulla et ipsum scelerisque, non pretium ipsum egestas. Mauris fringilla mattis turpis. Donec dignissim lectus eget lacus lobortis, non egestas justo dictum. Interdum et malesuada fames ac ante ipsum primis in faucibus. Vivamus tortor elit, elementum lacinia malesuada ac, venenatis vitae velit.
+
+Proin ipsum orci, rutrum nec neque nec, tempor efficitur sem. Phasellus in elit iaculis dolor cursus efficitur. Donec quis vulputate elit. Ut at odio mauris. Pellentesque sit amet maximus velit. Aliquam ac ex a neque ullamcorper venenatis eget et massa. Donec convallis felis eget mauris consectetur, in convallis justo semper. Donec dignissim consequat eleifend. Pellentesque augue nulla, placerat vitae urna a, mattis sodales erat. Nullam et malesuada risus, et ultrices ipsum. Integer ac placerat enim. Integer vel faucibus nisi.
+
+## Orci varius natoque
+
+Penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed venenatis dolor ac enim pulvinar cursus a non nulla. Aliquam commodo, nisi a fermentum mollis, risus justo volutpat diam, id varius felis magna feugiat tellus. Sed at porta metus. Praesent fermentum, nisl vel lobortis venenatis, odio turpis malesuada tortor, ultrices pellentesque quam ex eu orci. Sed laoreet non lorem eu gravida. Morbi quis felis a massa facilisis accumsan. Suspendisse rhoncus purus neque, in eleifend augue hendrerit id. Phasellus luctus a sem sit amet accumsan. Quisque tempor velit at pulvinar varius. Pellentesque ex felis, imperdiet gravida neque non, congue venenatis ex. Aenean iaculis cursus turpis eget luctus. Praesent nec eleifend lectus.
+
+Aenean luctus quam dolor, et ultrices odio rutrum nec. Sed pulvinar, felis at elementum venenatis, lacus purus convallis mi, sed ultrices nisl orci id urna. Curabitur dapibus volutpat hendrerit. In molestie, purus eu sollicitudin semper, leo ante placerat odio, at sollicitudin diam tellus sit amet metus. Morbi hendrerit est ac erat scelerisque condimentum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Proin egestas orci ac sapien blandit laoreet.
+
+Vivamus sollicitudin sed turpis non placerat. Cras mollis lobortis elit vitae vestibulum. Donec ornare, tortor interdum lacinia egestas, sem arcu sodales ipsum, ac ultrices justo elit sit amet ipsum. Cras sapien leo, tincidunt pretium pretium quis, egestas non mauris. Aenean ac fermentum ante. Mauris rhoncus enim ante, a pellentesque mauris mattis sed. Nam mattis, ante eu pretium tempus, risus magna iaculis erat, eget imperdiet nulla neque sed nulla. Cras gravida libero neque, quis pulvinar ante fringilla quis. In ut lobortis velit. Suspendisse congue arcu ligula, at aliquam ipsum pharetra sit amet. Mauris ut enim eget massa dignissim bibendum. Vivamus purus nisl, fermentum efficitur elementum nec, lacinia at tortor. Donec aliquam tellus vel nunc tristique, id pulvinar sem facilisis.
+
+| 123123 | 123131 |
+|---|---|
+|  Donec ornare, tortor interdum lacinia egestas, sem arcu sodales ipsum, ac ultrices justo elit sit amet ipsum. Cras sapien leo, tincidunt pretium pretium quis, egestas non mauris. Aenean ac fermentum ante. Mauris rhoncus enim ante, a pellentesque mauris mattis sed. Nam mattis, ante eu pretium tempus, risus magna iaculis erat, eget imperdiet nulla neque sed nulla. Cras gravida libero neque, quis pulvinar ante fringilla quis. In ut lobortis velit. Suspendisse congue arcu ligula, at aliquam ipsum pharetra sit amet. Mauris ut enim eget massa dignissim bibendum. Vivamus purus nisl, fermentum efficitur elementum nec, lacinia at tortor. Donec aliquam tellus vel nunc tristique, id pulvinar sem facilisis. | Curabitur dapibus volutpat hendrerit. In molestie, purus eu sollicitudin semper, leo ante placerat odio, at sollicitudin diam tellus sit amet metus. Morbi hendrerit est ac erat scelerisque condimentum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Proin egestas orci ac sapien blandit laoreet. |
+|  Donec ornare, tortor interdum lacinia egestas, sem arcu sodales ipsum, ac ultrices justo elit sit amet ipsum. Cras sapien leo, tincidunt pretium pretium quis, egestas non mauris. Aenean ac fermentum ante. Mauris rhoncus enim ante, a pellentesque mauris mattis sed. Nam mattis, ante eu pretium tempus, risus magna iaculis erat, eget imperdiet nulla neque sed nulla. Cras gravida libero neque, quis pulvinar ante fringilla quis. In ut lobortis velit. Suspendisse congue arcu ligula, at aliquam ipsum pharetra sit amet. Mauris ut enim eget massa dignissim bibendum. Vivamus purus nisl, fermentum efficitur elementum nec, lacinia at tortor. Donec aliquam tellus vel nunc tristique, id pulvinar sem facilisis. | Curabitur dapibus volutpat hendrerit. In molestie, purus eu sollicitudin semper, leo ante placerat odio, at sollicitudin diam tellus sit amet metus. Morbi hendrerit est ac erat scelerisque condimentum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Proin egestas orci ac sapien blandit laoreet. |
+|  Donec ornare, tortor interdum lacinia egestas, sem arcu sodales ipsum, ac ultrices justo elit sit amet ipsum. Cras sapien leo, tincidunt pretium pretium quis, egestas non mauris. Aenean ac fermentum ante. Mauris rhoncus enim ante, a pellentesque mauris mattis sed. Nam mattis, ante eu pretium tempus, risus magna iaculis erat, eget imperdiet nulla neque sed nulla. Cras gravida libero neque, quis pulvinar ante fringilla quis. In ut lobortis velit. Suspendisse congue arcu ligula, at aliquam ipsum pharetra sit amet. Mauris ut enim eget massa dignissim bibendum. Vivamus purus nisl, fermentum efficitur elementum nec, lacinia at tortor. Donec aliquam tellus vel nunc tristique, id pulvinar sem facilisis. | Curabitur dapibus volutpat hendrerit. In molestie, purus eu sollicitudin semper, leo ante placerat odio, at sollicitudin diam tellus sit amet metus. Morbi hendrerit est ac erat scelerisque condimentum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Proin egestas orci ac sapien blandit laoreet. |
+|  Donec ornare, tortor interdum lacinia egestas, sem arcu sodales ipsum, ac ultrices justo elit sit amet ipsum. Cras sapien leo, tincidunt pretium pretium quis, egestas non mauris. Aenean ac fermentum ante. Mauris rhoncus enim ante, a pellentesque mauris mattis sed. Nam mattis, ante eu pretium tempus, risus magna iaculis erat, eget imperdiet nulla neque sed nulla. Cras gravida libero neque, quis pulvinar ante fringilla quis. In ut lobortis velit. Suspendisse congue arcu ligula, at aliquam ipsum pharetra sit amet. Mauris ut enim eget massa dignissim bibendum. Vivamus purus nisl, fermentum efficitur elementum nec, lacinia at tortor. Donec aliquam tellus vel nunc tristique, id pulvinar sem facilisis. | Curabitur dapibus volutpat hendrerit. In molestie, purus eu sollicitudin semper, leo ante placerat odio, at sollicitudin diam tellus sit amet metus. Morbi hendrerit est ac erat scelerisque condimentum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Proin egestas orci ac sapien blandit laoreet. |
+
+Phasellus convallis, velit eget blandit tempus, leo nisi placerat leo, ac blandit metus ligula vitae enim. Duis a felis in dui venenatis tristique ac quis mauris. Sed interdum dui sit amet tincidunt imperdiet. Integer consequat purus ut porta ornare. Morbi vel sem interdum, tempor lacus interdum, eleifend ligula. Vivamus augue nulla, finibus eget sapien nec, euismod maximus nunc. Aliquam non hendrerit massa.
+
+Vestibulum at justo metus. Duis at ultrices magna, vitae varius neque. Donec et tellus vitae turpis bibendum posuere. Nunc et sagittis nulla. Phasellus egestas feugiat mattis. Fusce eu felis ac tellus tempus egestas vel non eros. Integer faucibus euismod purus, non euismod sem. Cras sodales risus in libero eleifend molestie. Mauris convallis accumsan lectus pellentesque laoreet. Aenean sagittis enim sit amet orci ullamcorper, in semper tellus blandit. Pellentesque orci ex, tempor sed semper et, vehicula at turpis. Sed id finibus ex. Nullam metus neque, sollicitudin id urna eget, commodo tincidunt est. Donec porttitor nisi sit amet arcu dapibus rutrum.
+
+Sed ultrices tempus purus, ullamcorper sodales libero vestibulum luctus. Proin ut commodo justo, vitae dignissim leo. Mauris condimentum dolor ut purus ultricies, at consequat eros gravida. Phasellus quis eros sed tellus molestie venenatis ut vitae nibh. Maecenas mattis commodo neque vel bibendum. Curabitur eu porta risus. Maecenas mollis dignissim interdum. Integer ac pharetra lacus, vitae condimentum ex. Mauris at quam ornare, sollicitudin turpis vitae, aliquam tellus. Nam lacinia sem at lobortis ultrices. In vulputate odio a quam semper maximus at et massa. Nam massa diam, tincidunt at ex sit amet, placerat fringilla eros. Etiam quis semper turpis. Aliquam laoreet, nisi tempor dignissim suscipit, tellus nunc mattis tellus, luctus euismod massa leo id ex.
+
+Nullam placerat malesuada dui, vitae feugiat velit. Fusce finibus neque non massa tincidunt imperdiet. Nam scelerisque bibendum neque. In hac habitasse platea dictumst. Donec erat odio, rutrum mollis justo eu, interdum pharetra urna. Mauris eros felis, vulputate a lacus eu, euismod pulvinar felis. Curabitur euismod nulla ut auctor mattis. Sed molestie ligula ac lacus commodo, vitae vehicula leo blandit. Nullam et finibus turpis. Integer posuere diam urna, ultrices suscipit augue tincidunt vel.
+
+Maecenas consequat velit nec orci vestibulum, in interdum magna condimentum. Duis faucibus tellus in elit pharetra, ut pulvinar libero vulputate. Curabitur in eros a urna condimentum iaculis elementum at elit. In id ullamcorper risus, et iaculis est. Vestibulum ac tellus vitae neque volutpat vehicula. Donec pulvinar nibh vitae eros ullamcorper consequat. Fusce vitae quam blandit est blandit convallis. Donec non dignissim ipsum. Donec libero velit, fermentum quis leo a, tempor rutrum nunc. Nunc tempor ligula sapien, id pellentesque sapien dapibus vitae. Pellentesque nisl elit, varius elementum sapien sed, tempus dictum leo. Fusce magna orci, ornare eu luctus ac, feugiat vitae urna. Vestibulum vel metus ornare, varius velit et, congue risus. Quisque id purus sit amet mi consectetur interdum eget id magna.
+
+[LAMDWIKIPOST 20220729095433; VER 20220730103314; ]
+
+# Lorem Ipsum
+
+@ 射精
+
+{wide}{header INV0001}{footer watever you want to add}{show_email}
+
+## at porta metus
+
+{read_more}
+
+Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus in placerat lacus. Pellentesque ac nibh in enim sodales ullamcorper eget at arcu. Morbi imperdiet neque tempor, cursus purus ut, congue mi. Morbi sit amet erat nibh. Pellentesque at odio ut odio elementum porta. Etiam volutpat purus ac dolor elementum hendrerit quis laoreet nisi. Curabitur porttitor aliquet aliquet. Duis tristique felis arcu, a vestibulum nisl dignissim ac. Nam laoreet lacinia erat id suscipit. Suspendisse iaculis lacinia eleifend. Nullam ut turpis ante.
+
+Nullam ipsum nulla, pellentesque tristique ultrices id, posuere vitae erat. Donec turpis purus, hendrerit at lectus eu, lobortis vestibulum orci. Quisque vel ligula nec odio faucibus elementum mollis at nulla. Praesent efficitur metus in purus egestas sodales. Integer finibus mattis odio ut accumsan. Donec convallis, erat ut consectetur hendrerit, urna augue dignissim velit, non mattis diam justo sed magna. Fusce ut velit ut massa porta egestas a at massa. Aenean fermentum varius purus a congue. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.
+
+Fusce tempus a tortor non facilisis. Vestibulum consectetur bibendum sagittis. Suspendisse nec urna sit amet massa pulvinar pretium. Maecenas suscipit accumsan mi eu dignissim. In dictum pellentesque gravida. Etiam posuere diam quis ultricies ultricies. Nunc tortor ligula, dapibus vitae tortor eget, consequat varius lorem. Sed hendrerit et lacus egestas malesuada. Nulla facilisi. Nullam quam ipsum, molestie vitae nibh id, molestie blandit sapien. Etiam sed erat scelerisque libero vehicula porttitor a id arcu. Nullam tincidunt nisi sapien, vitae lobortis odio semper vel. Quisque malesuada imperdiet velit, et cursus tortor imperdiet ac.
+
+Nullam porta mi in libero pellentesque, non pellentesque massa venenatis. Proin nunc mauris, malesuada non nulla eget, pellentesque varius urna. Nulla at pellentesque arcu, eget gravida ipsum. Aliquam feugiat neque in augue fringilla placerat. Donec ac efficitur tellus. Nullam ullamcorper, neque eu tempor pellentesque, enim lectus fringilla ipsum, et efficitur eros nibh quis ligula. Maecenas gravida maximus sem ut posuere. Praesent hendrerit ante vitae elit fermentum vulputate. Quisque auctor, tellus sed mollis ullamcorper, dolor lacus pellentesque mi, hendrerit viverra felis nunc eget purus. Praesent cursus, risus non dapibus pellentesque, dolor lacus finibus lacus, et ultrices erat felis et felis. Mauris ultrices orci nec neque faucibus elementum. Vivamus mauris lectus, rutrum ac aliquet id, venenatis bibendum odio. Nunc vitae mollis libero. Nullam aliquet, leo non tempor posuere, sem quam aliquam leo, sed porta leo lorem eget tellus. Quisque consequat purus ut porta placerat.
+
+##  eleifend metus
+
+Donec erat mi, tincidunt nec diam non, lacinia molestie velit. Quisque quis mattis odio. Mauris non posuere libero. Mauris posuere consectetur aliquam. Donec lacus lectus, cursus ut fermentum fringilla, malesuada a lacus. Sed nec blandit nisi, at eleifend metus. Morbi feugiat purus nec enim mollis, id volutpat lectus dignissim.
+
+ Nunc et tellus libero. Aliquam nec augue interdum, laoreet eros vehicula, euismod tellus. Aenean vestibulum ipsum sed enim interdum, placerat consequat sem mollis. Curabitur varius aliquet efficitur. Vivamus cursus cursus ornare. Morbi rhoncus eros a ligula suscipit eleifend. Curabitur laoreet tempus tellus ac semper. Integer at nunc ac arcu dictum vehicula. Nulla facilisi. In iaculis risus elementum, blandit tellus eu, gravida massa. Ut mattis odio ut commodo ullamcorper. Nunc mauris ante, fringilla ut porta a, sagittis rutrum lectus. Suspendisse lobortis velit mi, in imperdiet nulla consequat a. Nam faucibus facilisis volutpat. Maecenas augue odio, ornare at cursus id, faucibus sed nulla. Aenean non magna sagittis, rhoncus orci eu, finibus lectus.
+
+Interdum et malesuada fames ac ante ipsum primis in faucibus. Vivamus sagittis aliquam odio, a iaculis orci. Donec tellus sapien, mollis ut orci mollis, viverra blandit mauris. Nunc nisl magna, tempor porta viverra ut, feugiat sed metus. Cras eu orci ultrices, vehicula magna ut, auctor mi. Pellentesque vestibulum at lacus ac consequat. Ut dictum ligula id condimentum maximus. Pellentesque consequat libero lacus, a tempor lectus finibus quis. Etiam blandit a arcu sed condimentum. Cras ac scelerisque justo, vel imperdiet felis. Phasellus id nisl volutpat nibh fermentum condimentum nec ut orci.
+
+## Morbi vulputate
+
+Aliquet varius. Cras sem quam, pulvinar sed semper nec, molestie nec nisl. Nam vehicula quam et magna luctus venenatis. Sed porttitor nisl eu cursus consectetur. Etiam vel pretium leo. Integer nec condimentum ex, vitae feugiat quam. Vivamus vel eros vel elit commodo sagittis. Cras tempus urna et nisl ornare, id sagittis quam porta. Curabitur facilisis malesuada diam, nec pellentesque sem pellentesque feugiat. Aliquam blandit massa augue, vitae malesuada orci iaculis in. Praesent dignissim mi quis odio ornare fringilla. Integer consectetur varius fringilla. Nulla molestie egestas risus quis molestie. Vestibulum porta orci feugiat feugiat rutrum.
+
+Vivamus quis sagittis metus. Integer nec commodo magna. Sed aliquet augue vulputate volutpat aliquet. Nunc eu nisi elit. Duis ut risus nec risus venenatis ornare. Vestibulum malesuada ultrices varius. Etiam consectetur ullamcorper elit, at fermentum purus semper ut. Quisque venenatis tempor blandit.
+
+Nunc nec tincidunt felis. Nulla ac enim consequat, maximus elit id, molestie ligula. Donec tortor lacus, mollis et orci ac, mollis fringilla metus. Sed ac feugiat elit. Mauris commodo nulla et ipsum scelerisque, non pretium ipsum egestas. Mauris fringilla mattis turpis. Donec dignissim lectus eget lacus lobortis, non egestas justo dictum. Interdum et malesuada fames ac ante ipsum primis in faucibus. Vivamus tortor elit, elementum lacinia malesuada ac, venenatis vitae velit.
+
+Proin ipsum orci, rutrum nec neque nec, tempor efficitur sem. Phasellus in elit iaculis dolor cursus efficitur. Donec quis vulputate elit. Ut at odio mauris. Pellentesque sit amet maximus velit. Aliquam ac ex a neque ullamcorper venenatis eget et massa. Donec convallis felis eget mauris consectetur, in convallis justo semper. Donec dignissim consequat eleifend. Pellentesque augue nulla, placerat vitae urna a, mattis sodales erat. Nullam et malesuada risus, et ultrices ipsum. Integer ac placerat enim. Integer vel faucibus nisi.
+
+## Orci varius natoque
+
+Penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed venenatis dolor ac enim pulvinar cursus a non nulla. Aliquam commodo, nisi a fermentum mollis, risus justo volutpat diam, id varius felis magna feugiat tellus. Sed at porta metus. Praesent fermentum, nisl vel lobortis venenatis, odio turpis malesuada tortor, ultrices pellentesque quam ex eu orci. Sed laoreet non lorem eu gravida. Morbi quis felis a massa facilisis accumsan. Suspendisse rhoncus purus neque, in eleifend augue hendrerit id. Phasellus luctus a sem sit amet accumsan. Quisque tempor velit at pulvinar varius. Pellentesque ex felis, imperdiet gravida neque non, congue venenatis ex. Aenean iaculis cursus turpis eget luctus. Praesent nec eleifend lectus.
+
+Aenean luctus quam dolor, et ultrices odio rutrum nec. Sed pulvinar, felis at elementum venenatis, lacus purus convallis mi, sed ultrices nisl orci id urna. Curabitur dapibus volutpat hendrerit. In molestie, purus eu sollicitudin semper, leo ante placerat odio, at sollicitudin diam tellus sit amet metus. Morbi hendrerit est ac erat scelerisque condimentum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Proin egestas orci ac sapien blandit laoreet.
+
+Vivamus sollicitudin sed turpis non placerat. Cras mollis lobortis elit vitae vestibulum. Donec ornare, tortor interdum lacinia egestas, sem arcu sodales ipsum, ac ultrices justo elit sit amet ipsum. Cras sapien leo, tincidunt pretium pretium quis, egestas non mauris. Aenean ac fermentum ante. Mauris rhoncus enim ante, a pellentesque mauris mattis sed. Nam mattis, ante eu pretium tempus, risus magna iaculis erat, eget imperdiet nulla neque sed nulla. Cras gravida libero neque, quis pulvinar ante fringilla quis. In ut lobortis velit. Suspendisse congue arcu ligula, at aliquam ipsum pharetra sit amet. Mauris ut enim eget massa dignissim bibendum. Vivamus purus nisl, fermentum efficitur elementum nec, lacinia at tortor. Donec aliquam tellus vel nunc tristique, id pulvinar sem facilisis.
+
+| 123123 | 123131 |
+|---|---|
+|  Donec ornare, tortor interdum lacinia egestas, sem arcu sodales ipsum, ac ultrices justo elit sit amet ipsum. Cras sapien leo, tincidunt pretium pretium quis, egestas non mauris. Aenean ac fermentum ante. Mauris rhoncus enim ante, a pellentesque mauris mattis sed. Nam mattis, ante eu pretium tempus, risus magna iaculis erat, eget imperdiet nulla neque sed nulla. Cras gravida libero neque, quis pulvinar ante fringilla quis. In ut lobortis velit. Suspendisse congue arcu ligula, at aliquam ipsum pharetra sit amet. Mauris ut enim eget massa dignissim bibendum. Vivamus purus nisl, fermentum efficitur elementum nec, lacinia at tortor. Donec aliquam tellus vel nunc tristique, id pulvinar sem facilisis. | Curabitur dapibus volutpat hendrerit. In molestie, purus eu sollicitudin semper, leo ante placerat odio, at sollicitudin diam tellus sit amet metus. Morbi hendrerit est ac erat scelerisque condimentum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Proin egestas orci ac sapien blandit laoreet. |
+|  Donec ornare, tortor interdum lacinia egestas, sem arcu sodales ipsum, ac ultrices justo elit sit amet ipsum. Cras sapien leo, tincidunt pretium pretium quis, egestas non mauris. Aenean ac fermentum ante. Mauris rhoncus enim ante, a pellentesque mauris mattis sed. Nam mattis, ante eu pretium tempus, risus magna iaculis erat, eget imperdiet nulla neque sed nulla. Cras gravida libero neque, quis pulvinar ante fringilla quis. In ut lobortis velit. Suspendisse congue arcu ligula, at aliquam ipsum pharetra sit amet. Mauris ut enim eget massa dignissim bibendum. Vivamus purus nisl, fermentum efficitur elementum nec, lacinia at tortor. Donec aliquam tellus vel nunc tristique, id pulvinar sem facilisis. | Curabitur dapibus volutpat hendrerit. In molestie, purus eu sollicitudin semper, leo ante placerat odio, at sollicitudin diam tellus sit amet metus. Morbi hendrerit est ac erat scelerisque condimentum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Proin egestas orci ac sapien blandit laoreet. |
+|  Donec ornare, tortor interdum lacinia egestas, sem arcu sodales ipsum, ac ultrices justo elit sit amet ipsum. Cras sapien leo, tincidunt pretium pretium quis, egestas non mauris. Aenean ac fermentum ante. Mauris rhoncus enim ante, a pellentesque mauris mattis sed. Nam mattis, ante eu pretium tempus, risus magna iaculis erat, eget imperdiet nulla neque sed nulla. Cras gravida libero neque, quis pulvinar ante fringilla quis. In ut lobortis velit. Suspendisse congue arcu ligula, at aliquam ipsum pharetra sit amet. Mauris ut enim eget massa dignissim bibendum. Vivamus purus nisl, fermentum efficitur elementum nec, lacinia at tortor. Donec aliquam tellus vel nunc tristique, id pulvinar sem facilisis. | Curabitur dapibus volutpat hendrerit. In molestie, purus eu sollicitudin semper, leo ante placerat odio, at sollicitudin diam tellus sit amet metus. Morbi hendrerit est ac erat scelerisque condimentum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Proin egestas orci ac sapien blandit laoreet. |
+|  Donec ornare, tortor interdum lacinia egestas, sem arcu sodales ipsum, ac ultrices justo elit sit amet ipsum. Cras sapien leo, tincidunt pretium pretium quis, egestas non mauris. Aenean ac fermentum ante. Mauris rhoncus enim ante, a pellentesque mauris mattis sed. Nam mattis, ante eu pretium tempus, risus magna iaculis erat, eget imperdiet nulla neque sed nulla. Cras gravida libero neque, quis pulvinar ante fringilla quis. In ut lobortis velit. Suspendisse congue arcu ligula, at aliquam ipsum pharetra sit amet. Mauris ut enim eget massa dignissim bibendum. Vivamus purus nisl, fermentum efficitur elementum nec, lacinia at tortor. Donec aliquam tellus vel nunc tristique, id pulvinar sem facilisis. | Curabitur dapibus volutpat hendrerit. In molestie, purus eu sollicitudin semper, leo ante placerat odio, at sollicitudin diam tellus sit amet metus. Morbi hendrerit est ac erat scelerisque condimentum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Proin egestas orci ac sapien blandit laoreet. |
+
+Phasellus convallis, velit eget blandit tempus, leo nisi placerat leo, ac blandit metus ligula vitae enim. Duis a felis in dui venenatis tristique ac quis mauris. Sed interdum dui sit amet tincidunt imperdiet. Integer consequat purus ut porta ornare. Morbi vel sem interdum, tempor lacus interdum, eleifend ligula. Vivamus augue nulla, finibus eget sapien nec, euismod maximus nunc. Aliquam non hendrerit massa.
+
+Vestibulum at justo metus. Duis at ultrices magna, vitae varius neque. Donec et tellus vitae turpis bibendum posuere. Nunc et sagittis nulla. Phasellus egestas feugiat mattis. Fusce eu felis ac tellus tempus egestas vel non eros. Integer faucibus euismod purus, non euismod sem. Cras sodales risus in libero eleifend molestie. Mauris convallis accumsan lectus pellentesque laoreet. Aenean sagittis enim sit amet orci ullamcorper, in semper tellus blandit. Pellentesque orci ex, tempor sed semper et, vehicula at turpis. Sed id finibus ex. Nullam metus neque, sollicitudin id urna eget, commodo tincidunt est. Donec porttitor nisi sit amet arcu dapibus rutrum.
+
+Sed ultrices tempus purus, ullamcorper sodales libero vestibulum luctus. Proin ut commodo justo, vitae dignissim leo. Mauris condimentum dolor ut purus ultricies, at consequat eros gravida. Phasellus quis eros sed tellus molestie venenatis ut vitae nibh. Maecenas mattis commodo neque vel bibendum. Curabitur eu porta risus. Maecenas mollis dignissim interdum. Integer ac pharetra lacus, vitae condimentum ex. Mauris at quam ornare, sollicitudin turpis vitae, aliquam tellus. Nam lacinia sem at lobortis ultrices. In vulputate odio a quam semper maximus at et massa. Nam massa diam, tincidunt at ex sit amet, placerat fringilla eros. Etiam quis semper turpis. Aliquam laoreet, nisi tempor dignissim suscipit, tellus nunc mattis tellus, luctus euismod massa leo id ex.
+
+Nullam placerat malesuada dui, vitae feugiat velit. Fusce finibus neque non massa tincidunt imperdiet. Nam scelerisque bibendum neque. In hac habitasse platea dictumst. Donec erat odio, rutrum mollis justo eu, interdum pharetra urna. Mauris eros felis, vulputate a lacus eu, euismod pulvinar felis. Curabitur euismod nulla ut auctor mattis. Sed molestie ligula ac lacus commodo, vitae vehicula leo blandit. Nullam et finibus turpis. Integer posuere diam urna, ultrices suscipit augue tincidunt vel.
+
+Maecenas consequat velit nec orci vestibulum, in interdum magna condimentum. Duis faucibus tellus in elit pharetra, ut pulvinar libero vulputate. Curabitur in eros a urna condimentum iaculis elementum at elit. In id ullamcorper risus, et iaculis est. Vestibulum ac tellus vitae neque volutpat vehicula. Donec pulvinar nibh vitae eros ullamcorper consequat. Fusce vitae quam blandit est blandit convallis. Donec non dignissim ipsum. Donec libero velit, fermentum quis leo a, tempor rutrum nunc. Nunc tempor ligula sapien, id pellentesque sapien dapibus vitae. Pellentesque nisl elit, varius elementum sapien sed, tempus dictum leo. Fusce magna orci, ornare eu luctus ac, feugiat vitae urna. Vestibulum vel metus ornare, varius velit et, congue risus. Quisque id purus sit amet mi consectetur interdum eget id magna.
+
+[LAMDWIKIPOST 20220730070458; VER 20220730070458; ]
+
+射 
+
+{interesting}
+
+[LAMDWIKIPOST 20220730070458; VER 20220730070718; FROM 20220730070502; HASTAG 20220730070502; ]
+
+射 
+
+{interesting}
+
+//20220730070502
+123
+
+[LAMDWIKIPOST 20220730070502; VER 20220730070502; INTO 20220730070458V20220730070718; ]
+
+123
+
+[LAMDWIKIPOST 20220730071131; VER 20220730071131; INTO 20220305061340V20220809092318; ]
+
+123
+
+[LAMDWIKIPOST 20220730105254; VER 20220730105254; ]
+
+# 什么模版
+
+{wide}
+
+<table><tr>
+<td>1</td><td>2</td>
+<tr/></table>
+
+[LAMDWIKIPOST 20220730105254; VER 20220730111818; ]
+
+# 什么模版
+
+{wide}
+
+<table class='clean_table'><tr>
+<td>1</td><td>2</td>
+<tr/></table>
+
+[LAMDWIKIPOST 20220730105254; VER 20220730111833; ]
+
+# INVOICE
+
+{wide}
+
+<table class='clean_table'><tr>
+<td>1</td><td>2</td>
+<tr/></table>
+
+[LAMDWIKIPOST 20220730105254; VER 20220730111946; ]
+
+### INVOICE
+
+INV0000
+
+{wide}{header INV0000}
+
+<table class='clean_table'><tr>
+<td>1</td><td>2</td>
+<tr/></table>
+
+[LAMDWIKIPOST 20220730105254; VER 20220730112019; ]
+
+### INVOICE
+
+INV0000
+
+{wide}{header INV0000}
+
+<table class='clean_table'><tr>
+<td>
+
+whatever *123*
+
+</td><td>
+
+</td>
+<tr/></table>
+
+[LAMDWIKIPOST 20220730105254; VER 20220730112030; ]
+
+### INVOICE
+
+INV0000
+
+{wide}{header INV0000}
+
+<table class='clean_table'><tr>
+<td>
+
+whatever *123*
+
+</td><td>
+e
+</td>
+<tr/></table>
+
+[LAMDWIKIPOST 20220730105254; VER 20220730112143; ]
+
+## INVOICE
+
+**Wu Yiming**
+
+Apartment C11, Xi'an Jiaotong University iHarbor Campus, Qingdu District
+XianYang City, ShannXi Province, China
+710000
++86 15198063018
+xp8110@outlook.com
+
+INV0000
+
+{wide}{header INV0000}
+
+<table class='clean_table'><tr>
+<td>
+
+whatever *123*
+
+</td><td>
+e
+</td>
+<tr/></table>
+
+[LAMDWIKIPOST 20220730105254; VER 20220730112232; ]
+
+## INVOICE
+
+**Wu Yiming**
+
+- Apartment C11, Xi'an Jiaotong University iHarbor Campus, Qingdu District
+- XianYang City, ShannXi Province, China
+- 710000
+- +86 15198063018
+- xp8110@outlook.com
+
+INV0000
+
+{wide}{header INV0000}
+
+<table class='clean_table'><tr>
+<td>
+
+whatever *123*
+
+</td><td>
+e
+</td>
+<tr/></table>
+
+[LAMDWIKIPOST 20220730105254; VER 20220730112253; ]
+
+## INVOICE
+
+INV0000
+
+**Wu Yiming**
+
+Apartment C11, Xi'an Jiaotong University iHarbor Campus, Qingdu District
+
+XianYang City, ShannXi Province, China
+
+710000
+
++86 15198063018
+
+xp8110@outlook.com
+
+INV0000
+
+{wide}{header INV0000}
+
+<table class='clean_table'><tr>
+<td>
+
+whatever *123*
+
+</td><td>
+e
+</td>
+<tr/></table>
+
+[LAMDWIKIPOST 20220730105254; VER 20220730112525; ]
+
+## INVOICE
+
+INV0000
+
+### Wu Yiming
+
+Apartment C11, Xi'an Jiaotong University iHarbor Campus, Qingdu District
+
+XianYang City, ShannXi Province, China
+
+710000
+
++86 15198063018
+
+xp8110@outlook.com
+
+INV0000
+
+{wide}{header INV0000}
+
+<table class='clean_table'><tr>
+<td>
+
+whatever *123*
+
+</td><td>
+e
+</td>
+<tr/></table>
+
+[LAMDWIKIPOST 20220730105254; VER 20220730112653; ]
+
+## INVOICE
+
+INV0000
+
+### Wu Yiming
+
+Apartment C11, Xi'an Jiaotong University iHarbor Campus, Qingdu District
+
+XianYang City, ShannXi Province, China
+
+710000
+
++86 15198063018
+
+xp8110@outlook.com
+
+------
+
+Bill To
+
+### Blender Foundation
+
+Buikslotermeerplein 161
+
+1025 ET Amsterdam, the Netherlands
+
+foundation@blender.org
+
+{wide}{header INV0000}
+
+<table class='clean_table'><tr>
+<td>
+
+whatever *123*
+
+</td><td>
+e
+</td>
+<tr/></table>
+
+[LAMDWIKIPOST 20220730105254; VER 20220730112729; ]
+
+## INVOICE
+
+INV0000
+
+### Wu Yiming
+
+Apartment C11, Xi'an Jiaotong University iHarbor Campus, Qingdu District
+
+XianYang City, ShannXi Province, China
+
+710000
+
++86 15198063018
+
+xp8110@outlook.com
+
+<br />
+
+
+
+#### BILL TO: Blender Foundation
+
+Buikslotermeerplein 161
+
+1025 ET Amsterdam, the Netherlands
+
+foundation@blender.org
+
+{wide}{header INV0000}
+
+<table class='clean_table'><tr>
+<td>
+
+whatever *123*
+
+</td><td>
+e
+</td>
+<tr/></table>
+
+[LAMDWIKIPOST 20220730105254; VER 20220730113057; ]
+
+## INVOICE
+
+{wide}{header INV0000}
+
+INV0000
+
+### Wu Yiming
+
+Apartment C11, Xi'an Jiaotong University iHarbor Campus, Qingdu District
+
+XianYang City, ShannXi Province, China
+
+710000
+
++86 15198063018
+
+xp8110@outlook.com
+
+<br />
+
+
+
+#### BILL TO: Blender Foundation
+
+Buikslotermeerplein 161
+
+1025 ET Amsterdam, the Netherlands
+
+foundation@blender.org
+
+| DESCRIPTION | RATE | QTY | AMOUNT |
+|---|---|
+| August Developer Grant | EUR 1250 | 1 | EUR 1250 |
+
+<table class='clean_table'><tr>
+<td>
+
+whatever *123*
+
+</td><td>
+e
+</td>
+<tr/></table>
+
+[LAMDWIKIPOST 20220730105254; VER 20220730113118; ]
+
+## INVOICE
+
+{wide}{header INV0000}
+
+INV0000
+
+### Wu Yiming
+
+Apartment C11, Xi'an Jiaotong University iHarbor Campus, Qingdu District
+
+XianYang City, ShannXi Province, China
+
+710000
+
++86 15198063018
+
+xp8110@outlook.com
+
+#### BILL TO: Blender Foundation
+
+Buikslotermeerplein 161
+
+1025 ET Amsterdam, the Netherlands
+
+foundation@blender.org
+
+| DESCRIPTION | RATE | QTY | AMOUNT |
+|---|---|
+| August Developer Grant | EUR 1250 | 1 | EUR 1250 |
+
+<table class='clean_table'><tr>
+<td>
+
+whatever *123*
+
+</td><td>
+e
+</td>
+<tr/></table>
+
+[LAMDWIKIPOST 20220730105254; VER 20220730113139; ]
+
+## INVOICE
+
+{wide}{header INV0000}
+
+INV0000
+
+### Wu Yiming
+
+Apartment C11, Xi'an Jiaotong University iHarbor Campus, Qingdu District
+
+XianYang City, ShannXi Province, China
+
+710000
+
++86 15198063018
+
+xp8110@outlook.com
+
+### BILL TO: Blender Foundation
+
+Buikslotermeerplein 161
+
+1025 ET Amsterdam, the Netherlands
+
+foundation@blender.org
+
+| DESCRIPTION | RATE | QTY | AMOUNT |
+|---|---|---|---|
+| August Developer Grant | EUR 1250 | 1 | EUR 1250 |
+
+<table class='clean_table'><tr>
+<td>
+
+whatever *123*
+
+</td><td>
+e
+</td>
+<tr/></table>
+
+[LAMDWIKIPOST 20220730105254; VER 20220730113221; ]
+
+## INVOICE
+
+{wide}{header INV0000}
+
+INV0000
+
+### Wu Yiming
+
+Apartment C11, Xi'an Jiaotong University iHarbor Campus, Qingdu District
+
+XianYang City, ShannXi Province, China
+
+710000
+
++86 15198063018
+
+xp8110@outlook.com
+
+#### BILL TO
+
+### Blender Foundation
+
+Buikslotermeerplein 161
+
+1025 ET Amsterdam, the Netherlands
+
+foundation@blender.org
+
+| DESCRIPTION | RATE | QTY | AMOUNT |
+|---|---|---|---|
+| August Developer Grant | EUR 1250 | 1 | EUR 1250 |
+
+<table class='clean_table'><tr>
+<td>
+
+whatever *123*
+
+</td><td>
+e
+</td>
+<tr/></table>
+
+[LAMDWIKIPOST 20220730105254; VER 20220730113312; ]
+
+## INVOICE
+
+{wide}{header INV0000}
+
+INV0000
+
+### Wu Yiming
+
+Apartment C11, Xi'an Jiaotong University iHarbor Campus, Qingdu District
+
+XianYang City, ShannXi Province, China
+
+710000
+
++86 15198063018
+
+xp8110@outlook.com
+
+### <span class='gray'>BILL TO</span> Blender Foundation
+
+Buikslotermeerplein 161
+
+1025 ET Amsterdam, the Netherlands
+
+foundation@blender.org
+
+| DESCRIPTION | RATE | QTY | AMOUNT |
+|---|---|---|---|
+| August Developer Grant | EUR 1250 | 1 | EUR 1250 |
+
+<table class='clean_table'><tr>
+<td>
+
+whatever *123*
+
+</td><td>
+e
+</td>
+<tr/></table>
+
+[LAMDWIKIPOST 20220730105254; VER 20220730113335; ]
+
+## INVOICE
+
+{wide}{header INV0000}
+
+INV0000
+
+### Wu Yiming
+
+Apartment C11, Xi'an Jiaotong University iHarbor Campus, Qingdu District
+
+XianYang City, ShannXi Province, China
+
+710000
+
++86 15198063018
+
+xp8110@outlook.com
+
+### <span class='gray smaller'>BILL TO</span><br /> Blender Foundation
+
+Buikslotermeerplein 161
+
+1025 ET Amsterdam, the Netherlands
+
+foundation@blender.org
+
+| DESCRIPTION | RATE | QTY | AMOUNT |
+|---|---|---|---|
+| August Developer Grant | EUR 1250 | 1 | EUR 1250 |
+
+<table class='clean_table'><tr>
+<td>
+
+whatever *123*
+
+</td><td>
+e
+</td>
+<tr/></table>
+
+[LAMDWIKIPOST 20220730105254; VER 20220730113423; ]
+
+## INVOICE
+
+{wide}{header INV0000}
+
+INV0000
+
+### Wu Yiming
+
+Apartment C11, Xi'an Jiaotong University iHarbor Campus, Qingdu District
+
+XianYang City, ShannXi Province, China
+
+710000
+
++86 15198063018
+
+xp8110@outlook.com
+
+#### <span class='gray smaller'>BILL TO</span><br /> Blender Foundation
+
+Buikslotermeerplein 161
+
+1025 ET Amsterdam, the Netherlands
+
+foundation@blender.org
+
+| DESCRIPTION | RATE | QTY | AMOUNT |
+|---|---|---|---|
+| August Developer Grant | EUR 1250 | 1 | EUR 1250 |
+
+<table class='clean_table'><tr>
+<td>
+
+whatever *123*
+
+</td><td>
+e
+</td>
+<tr/></table>
+
+[LAMDWIKIPOST 20220730105254; VER 20220730113529; ]
+
+## INVOICE
+
+{wide}{header INV0000}
+
+INV0000
+
+### Wu Yiming
+
+Apartment C11, Xi'an Jiaotong University iHarbor Campus, Qingdu District
+
+XianYang City, ShannXi Province, China
+
+710000
+
++86 15198063018
+
+xp8110@outlook.com
+
+### <span class='gray smaller'>BILL TO</span><br /> Blender Foundation
+
+Buikslotermeerplein 161
+
+1025 ET Amsterdam, the Netherlands
+
+foundation@blender.org
+
+<br />
+
+| DESCRIPTION | RATE | QTY | AMOUNT |
+|---|---|---|---|
+| August Developer Grant | EUR 1250 | 1 | EUR 1250 |
+
+<table class='clean_table'><tr>
+<td>
+
+whatever *123*
+
+</td><td>
+e
+</td>
+<tr/></table>
+
+[LAMDWIKIPOST 20220730105254; VER 20220730113606; ]
+
+## INVOICE
+
+{wide}{header INV0000}
+
+INV0000
+
+### Wu Yiming
+
+Apartment C11, Xi'an Jiaotong University iHarbor Campus, Qingdu District
+
+XianYang City, ShannXi Province, China
+
+710000
+
++86 15198063018
+
+xp8110@outlook.com
+
+### <span class='gray smaller'>BILL TO</span><br /> Blender Foundation
+
+Buikslotermeerplein 161
+
+1025 ET Amsterdam, the Netherlands
+
+foundation@blender.org
+
+<br />
+
+| DESCRIPTION | RATE | QTY | AMOUNT |
+|---|---|---|---|
+| August Developer Grant | EUR 1250 | 1 | EUR 1250 |
+|---|---|---|---|
+|---|---|---|---|
+|---|---|---|---|
+
+<table class='clean_table'><tr>
+<td>
+
+whatever *123*
+
+</td><td>
+e
+</td>
+<tr/></table>
+
+[LAMDWIKIPOST 20220730105254; VER 20220730113631; ]
+
+## INVOICE
+
+{wide}{header INV0000}
+
+INV0000
+
+### Wu Yiming
+
+Apartment C11, Xi'an Jiaotong University iHarbor Campus, Qingdu District
+
+XianYang City, ShannXi Province, China
+
+710000
+
++86 15198063018
+
+xp8110@outlook.com
+
+### <span class='gray smaller'>BILL TO</span><br /> Blender Foundation
+
+Buikslotermeerplein 161
+
+1025 ET Amsterdam, the Netherlands
+
+foundation@blender.org
+
+<br />
+
+| DESCRIPTION | RATE | QTY | AMOUNT |
+|---|---|---|---|
+| August Developer Grant | EUR 1250 | 1 | EUR 1250 |
+|<hr />|<hr />|<hr />|
+|---|---|---|---|
+
+<table class='clean_table'><tr>
+<td>
+
+whatever *123*
+
+</td><td>
+e
+</td>
+<tr/></table>
+
+[LAMDWIKIPOST 20220730105254; VER 20220730114029; ]
+
+## INVOICE
+
+{wide}{header INV0000}
+
+INV0000
+
+### Wu Yiming
+
+Apartment C11, Xi'an Jiaotong University iHarbor Campus, Qingdu District
+
+XianYang City, ShannXi Province, China
+
+710000
+
++86 15198063018
+
+xp8110@outlook.com
+
+### <span class='gray smaller'>BILL TO</span><br /> Blender Foundation
+
+Buikslotermeerplein 161
+
+1025 ET Amsterdam, the Netherlands
+
+foundation@blender.org
+
+<br />
+
+| DESCRIPTION | RATE | QTY | AMOUNT |
+|---|---|---|---|
+| August Developer Grant | EUR 1250 | 1 | EUR 1250 |
+
+<table class='clean_table'><tbody><tr>
+<td style='width:50%;'>
+
+whatever *123*
+
+</td><td style='width:50%;'>
+
+<table><tbody>
+<tr><td>SUBTOTAL<td/><td>EUR 1250</td></tr>
+<tr><td>TAX<td/><td>0%</td></tr>
+<tr><td class='bigger'>BALANCE DUE<td/><td>EUR 1250</td></tr>
+</tbody></table>
+
+</td>
+<tr/></tbody></table>
+
+[LAMDWIKIPOST 20220730105254; VER 20220730114053; ]
+
+## INVOICE
+
+{wide}{header INV0000}
+
+INV0000
+
+### Wu Yiming
+
+Apartment C11, Xi'an Jiaotong University iHarbor Campus, Qingdu District
+
+XianYang City, ShannXi Province, China
+
+710000
+
++86 15198063018
+
+xp8110@outlook.com
+
+### <span class='gray smaller'>BILL TO</span><br /> Blender Foundation
+
+Buikslotermeerplein 161
+
+1025 ET Amsterdam, the Netherlands
+
+foundation@blender.org
+
+<br />
+
+| DESCRIPTION | RATE | QTY | AMOUNT |
+|---|---|---|---|
+| August Developer Grant | EUR 1250 | 1 | EUR 1250 |
+
+<table class='clean_table'><tbody><tr>
+<td style='width:50%;'>
+
+whatever *123*
+
+</td><td style='width:50%;'>
+
+<table><tbody>
+<tr><td>SUBTOTAL<td/><td>EUR 1250</td></tr>
+<tr><td>TAX<td/><td>0%</td></tr>
+<tr class='bigger'><td>BALANCE DUE<td/><td>EUR 1250</td></tr>
+</tbody></table>
+
+</td>
+<tr/></tbody></table>
+
+[LAMDWIKIPOST 20220730105254; VER 20220730114104; ]
+
+## INVOICE
+
+{wide}{header INV0000}
+
+INV0000
+
+### Wu Yiming
+
+Apartment C11, Xi'an Jiaotong University iHarbor Campus, Qingdu District
+
+XianYang City, ShannXi Province, China
+
+710000
+
++86 15198063018
+
+xp8110@outlook.com
+
+### <span class='gray smaller'>BILL TO</span><br /> Blender Foundation
+
+Buikslotermeerplein 161
+
+1025 ET Amsterdam, the Netherlands
+
+foundation@blender.org
+
+<br />
+
+| DESCRIPTION | RATE | QTY | AMOUNT |
+|---|---|---|---|
+| August Developer Grant | EUR 1250 | 1 | EUR 1250 |
+
+<table class='clean_table'><tbody><tr>
+<td style='width:50%;'>
+
+whatever *123*
+
+</td><td style='width:50%;'>
+
+<table><tbody>
+<tr><td>SUBTOTAL<td/><td>EUR 1250</td></tr>
+<tr><td>TAX<td/><td>0%</td></tr>
+<tr class='bigger bold'><td>BALANCE DUE<td/><td>EUR 1250</td></tr>
+</tbody></table>
+
+</td>
+<tr/></tbody></table>
+
+[LAMDWIKIPOST 20220730105254; VER 20220730114201; ]
+
+## INVOICE
+
+{wide}{header INV0000}
+
+INV0000
+
+### Wu Yiming
+
+Apartment C11, Xi'an Jiaotong University iHarbor Campus, Qingdu District
+
+XianYang City, ShannXi Province, China
+
+710000
+
++86 15198063018
+
+xp8110@outlook.com
+
+### <span class='gray smaller'>BILL TO</span><br /> Blender Foundation
+
+Buikslotermeerplein 161
+
+1025 ET Amsterdam, the Netherlands
+
+foundation@blender.org
+
+<br />
+
+| DESCRIPTION | RATE | QTY | AMOUNT |
+|---|---|---|---|
+| August Developer Grant | EUR 1250 | 1 | EUR 1250 |
+
+<table class='clean_table'><tbody><tr>
+<td style='width:50%;'>
+
+Note 
+
+I don't care whatever things 
+
+</td><td style='width:50%;'>
+
+<table><tbody>
+<tr><td>SUBTOTAL<td/><td>EUR 1250</td></tr>
+<tr><td>TAX<td/><td>0%</td></tr>
+<tr class='bigger bold'><td>BALANCE DUE<td/><td>EUR 1250</td></tr>
+</tbody></table>
+
+</td>
+<tr/></tbody></table>
+
+[LAMDWIKIPOST 20220730105254; VER 20220730114353; ]
+
+## INVOICE
+
+{wide}{header INV0000}
+
+INV0000
+
+### Wu Yiming
+
+Apartment C11, Xi'an Jiaotong University iHarbor Campus, Qingdu District
+
+XianYang City, ShannXi Province, China
+
+710000
+
++86 15198063018
+
+xp8110@outlook.com
+
+### <span class='gray smaller'>BILL TO</span><br /> Blender Foundation
+
+Buikslotermeerplein 161
+
+1025 ET Amsterdam, the Netherlands
+
+foundation@blender.org
+
+<br />
+
+| DESCRIPTION | RATE | QTY | AMOUNT |
+|---|---|---|---|
+| August Developer Grant | EUR 1250 | 1 | EUR 1250 |
+
+<table class='clean_table'><tbody><tr>
+<td style='width:50%;' markdown="1">
+
+Note 
+
+I don't care whatever things 
+
+</td><td style='width:50%;'>
+
+<table><tbody>
+<tr><td>SUBTOTAL<td/><td>EUR 1250</td></tr>
+<tr><td>TAX<td/><td>0%</td></tr>
+<tr class='bigger bold'><td>BALANCE DUE<td/><td>EUR 1250</td></tr>
+</tbody></table>
+
+</td>
+<tr/></tbody></table>
+
+[LAMDWIKIPOST 20220730105254; VER 20220730114450; ]
+
+## INVOICE
+
+{wide}{header INV0000}
+
+INV0000
+
+### Wu Yiming
+
+Apartment C11, Xi'an Jiaotong University iHarbor Campus, Qingdu District
+
+XianYang City, ShannXi Province, China
+
+710000
+
++86 15198063018
+
+xp8110@outlook.com
+
+### <span class='gray smaller'>BILL TO</span><br /> Blender Foundation
+
+Buikslotermeerplein 161
+
+1025 ET Amsterdam, the Netherlands
+
+foundation@blender.org
+
+<br />
+
+| DESCRIPTION | RATE | QTY | AMOUNT |
+|---|---|---|---|
+| August Developer Grant | EUR 1250 | 1 | EUR 1250 |
+
+<table class='clean_table'><tbody><tr>
+<td style='width:50%;' markdown="1">
+
+Note 
+
+I don't care whatever things 
+
+</td><td style='width:50%;' markdown="1">
+
+| SUBTOTAL | EUR 1250 |
+| TAX | 0% |
+| BALANCE DUE | EUR 1250 |
+
+</td>
+<tr/></tbody></table>
+
+[LAMDWIKIPOST 20220730105254; VER 20220730114502; ]
+
+## INVOICE
+
+{wide}{header INV0000}
+
+INV0000
+
+### Wu Yiming
+
+Apartment C11, Xi'an Jiaotong University iHarbor Campus, Qingdu District
+
+XianYang City, ShannXi Province, China
+
+710000
+
++86 15198063018
+
+xp8110@outlook.com
+
+### <span class='gray smaller'>BILL TO</span><br /> Blender Foundation
+
+Buikslotermeerplein 161
+
+1025 ET Amsterdam, the Netherlands
+
+foundation@blender.org
+
+<br />
+
+| DESCRIPTION | RATE | QTY | AMOUNT |
+|---|---|---|---|
+| August Developer Grant | EUR 1250 | 1 | EUR 1250 |
+
+<table class='clean_table'><tbody><tr>
+<td style='width:50%;' markdown="1">
+
+Note 
+
+I don't care whatever things 
+
+</td><td style='width:50%;' markdown="1">
+
+|---|---|
+| SUBTOTAL | EUR 1250 |
+| TAX | 0% |
+| BALANCE DUE | EUR 1250 |
+
+</td>
+<tr/></tbody></table>
+
+[LAMDWIKIPOST 20220730105254; VER 20220730114521; ]
+
+## INVOICE
+
+{wide}{header INV0000}
+
+INV0000
+
+### Wu Yiming
+
+Apartment C11, Xi'an Jiaotong University iHarbor Campus, Qingdu District
+
+XianYang City, ShannXi Province, China
+
+710000
+
++86 15198063018
+
+xp8110@outlook.com
+
+### <span class='gray smaller'>BILL TO</span><br /> Blender Foundation
+
+Buikslotermeerplein 161
+
+1025 ET Amsterdam, the Netherlands
+
+foundation@blender.org
+
+<br />
+
+| DESCRIPTION | RATE | QTY | AMOUNT |
+|---|---|---|---|
+| August Developer Grant | EUR 1250 | 1 | EUR 1250 |
+
+<table class='clean_table'><tbody><tr>
+<td style='width:50%;' markdown="1">
+
+Note 
+
+I don't care whatever things 
+
+</td><td style='width:50%;' markdown="1">
+
+| 123 | 123 |
+|---|---|
+| SUBTOTAL | EUR 1250 |
+| TAX | 0% |
+| BALANCE DUE | EUR 1250 |
+
+</td>
+<tr/></tbody></table>
+
+[LAMDWIKIPOST 20220730105254; VER 20220730114639; ]
+
+## INVOICE
+
+{wide}{header INV0000}
+
+INV0000
+
+### Wu Yiming
+
+Apartment C11, Xi'an Jiaotong University iHarbor Campus, Qingdu District
+
+XianYang City, ShannXi Province, China
+
+710000
+
++86 15198063018
+
+xp8110@outlook.com
+
+### <span class='gray smaller'>BILL TO</span><br /> Blender Foundation
+
+Buikslotermeerplein 161
+
+1025 ET Amsterdam, the Netherlands
+
+foundation@blender.org
+
+<br />
+
+| DESCRIPTION | RATE | QTY | AMOUNT |
+|---|---|---|---|
+| August Developer Grant | EUR 1250 | 1 | EUR 1250 |
+
+<table class='clean_table'><tbody><tr>
+<td style='width:50%;' markdown="1">
+
+Note 
+
+I don't care whatever things 
+
+</td><td style='width:50%;' markdown="1">
+
+| SUBTOTAL | EUR 1250 |
+|---|---|
+| TAX | 0% |
+| <span class='bigger bold'>BALANCE DUE</span> | <span class='bigger bold'>EUR 1250</span> |
+
+</td>
+<tr/></tbody></table>
+
+[LAMDWIKIPOST 20220730105254; VER 20220730114656; ]
+
+## INVOICE
+
+{wide}{header INV0000}
+
+INV0000
+
+### Wu Yiming
+
+Apartment C11, Xi'an Jiaotong University iHarbor Campus, Qingdu District
+
+XianYang City, ShannXi Province, China
+
+710000
+
++86 15198063018
+
+xp8110@outlook.com
+
+### <span class='gray smaller'>BILL TO</span><br /> Blender Foundation
+
+Buikslotermeerplein 161
+
+1025 ET Amsterdam, the Netherlands
+
+foundation@blender.org
+
+<br />
+
+| DESCRIPTION | RATE | QTY | AMOUNT |
+|---|---|---|---|
+| August Developer Grant | EUR 1250 | 1 | EUR 1250 |
+
+<table class='clean_table'><tbody><tr>
+<td style='width:50%;' markdown="1">
+
+Note 
+
+I don't care whatever things 
+
+</td><td style='width:50%;' markdown="1">
+
+| SUBTOTAL | EUR 1250 |
+|---|---|
+| TAX | 0% |
+|  |  |
+| <span class='bigger bold'>BALANCE DUE</span> | <span class='bigger bold'>EUR 1250</span> |
+
+</td>
+<tr/></tbody></table>
+
+[LAMDWIKIPOST 20220730105254; VER 20220730114711; ]
+
+## INVOICE
+
+{wide}{header INV0000}
+
+INV0000
+
+### Wu Yiming
+
+Apartment C11, Xi'an Jiaotong University iHarbor Campus, Qingdu District
+
+XianYang City, ShannXi Province, China
+
+710000
+
++86 15198063018
+
+xp8110@outlook.com
+
+### <span class='gray smaller'>BILL TO</span><br /> Blender Foundation
+
+Buikslotermeerplein 161
+
+1025 ET Amsterdam, the Netherlands
+
+foundation@blender.org
+
+<br />
+
+| DESCRIPTION | RATE | QTY | AMOUNT |
+|---|---|---|---|
+| August Developer Grant | EUR 1250 | 1 | EUR 1250 |
+
+<table class='clean_table'><tbody><tr>
+<td style='width:50%;' markdown="1">
+
+Note 
+
+I don't care whatever things 
+
+</td><td style='width:50%;' markdown="1">
+
+| SUBTOTAL | EUR 1250 |
+|---|---|
+| TAX | 0% |
+| &nbsp; |  |
+| <span class='bigger bold'>BALANCE DUE</span> | <span class='bigger bold'>EUR 1250</span> |
+
+</td>
+<tr/></tbody></table>
+
+[LAMDWIKIPOST 20220730105254; VER 20220730152851; ]
+
+## INVOICE
+
+{wide}{header INV0000}
+
+INV0000
+
+### Wu Yiming
+
+Apartment C11, Xi'an Jiaotong University iHarbor Campus, Qingdu District
+
+XianYang City, ShannXi Province, China
+
+710000
+
++86 15198063018
+
+xp8110@outlook.com
+
+### <span class='gray smaller'>BILL TO</span><br /> Blender Foundation
+
+Buikslotermeerplein 161
+
+1025 ET Amsterdam, the Netherlands
+
+foundation@blender.org
+
+<br />
+
+| DESCRIPTION | RATE | QTY | AMOUNT |
+|---|---|---|---|
+| August Developer Grant | EUR 1250 | 1 | EUR 1250 |
+
+<table class='clean_table'><tbody><tr>
+<td style='width:50%;' markdown="1">
+
+#### NOTES
+
+Note 
+
+I don't care whatever things 
+
+</td><td style='width:50%;' markdown="1">
+
+| SUBTOTAL | EUR 1250 |
+|---|---|
+| TAX | 0% |
+| &nbsp; |  |
+| <span class='bigger bold'>BALANCE DUE</span> | <span class='bigger bold'>EUR 1250</span> |
+
+</td>
+<tr/></tbody></table>
+
+[LAMDWIKIPOST 20220730105254; VER 20220730154138; ]
+
+## INVOICE
+
+{wide}{header INV0000}
+
+INV0000
+
+### Wu Yiming
+
+Apartment C11, Xi'an Jiaotong University iHarbor Campus, Qingdu District
+
+XianYang City, ShannXi Province, China
+
+710000
+
++86 15198063018
+
+xp8110@outlook.com
+
+### <span class='gray smaller'>BILL TO</span><br /> Blender Foundation
+
+Buikslotermeerplein 161
+
+1025 ET Amsterdam, the Netherlands
+
+foundation@blender.org
+
+<br />
+
+| DESCRIPTION | RATE | QTY | AMOUNT |
+|---|---|---|---|
+| August Developer Grant | EUR 1250 | 1 | EUR 1250 |
+
+<table class='clean_table'><tbody><tr>
+<td style='width:50%;' markdown="1">
+
+#### <span class='gray'>NOTES</span>
+
+Note 
+
+I don't care whatever things 
+
+</td><td style='width:50%;' markdown="1">
+
+| SUBTOTAL | EUR 1250 |
+|---|---|
+| TAX | 0% |
+| &nbsp; |  |
+| <span class='bigger bold'>BALANCE DUE</span> | <span class='bigger bold'>EUR 1250</span> |
+
+</td>
+<tr/></tbody></table>
+
+[LAMDWIKIPOST 20220730105254; VER 20220730155440; ]
+
+## INVOICE
+
+{wide}{header INV0000}
+
+INV0000
+
+### Wu Yiming
+
+Apartment C11, Xi'an Jiaotong University iHarbor Campus, Qingdu District
+
+XianYang City, ShannXi Province, China
+
+710000
+
++86 15198063018
+
+xp8110@outlook.com
+
+### <span class='gray smaller'>BILL TO</span><br /> Blender Foundation
+
+Buikslotermeerplein 161
+
+1025 ET Amsterdam, the Netherlands
+
+foundation@blender.org
+
+<br />
+
+| DESCRIPTION | RATE | QTY | AMOUNT |
+|---|---|---|---|
+| August Developer Grant | EUR 1250 | 1 | EUR 1250 |
+
+<table class='clean_table'><tbody><tr>
+<td style='width:50%;' markdown="1">
+
+### <span class='gray smaller'>NOTES</span>
+
+Note 
+
+I don't care whatever things 
+
+</td><td style='width:50%;' markdown="1">
+
+| SUBTOTAL | EUR 1250 |
+|---|---|
+| TAX | 0% |
+| &nbsp; |  |
+| <span class='bigger bold'>BALANCE DUE</span> | <span class='bigger bold'>EUR 1250</span> |
+
+</td>
+<tr/></tbody></table>
+
+[LAMDWIKIPOST 20220730105254; VER 20220730155656; ]
+
+## INVOICE
+
+{wide}{header INV0000}
+
+INV0000
+
+### Wu Yiming
+
+Apartment C11, Xi'an Jiaotong University iHarbor Campus, Qingdu District
+
+XianYang City, ShannXi Province, China
+
+710000
+
++86 15198063018
+
+xp8110@outlook.com
+
+### <span class='gray smaller'>BILL TO</span><br /> Blender Foundation
+
+Buikslotermeerplein 161
+
+1025 ET Amsterdam, the Netherlands
+
+foundation@blender.org
+
+<br />
+
+| DESCRIPTION | RATE | QTY | AMOUNT |
+|---|---|---|---|
+| August Developer Grant | EUR 1250 | 1 | EUR 1250 |
+
+<table class='clean_table'><tbody><tr>
+<td style='width:50%;' markdown="1">
+
+### <span class='gray smaller'>NOTES</span>
+
+Note 
+
+I don't care whatever things with complexity, just a longer sentence to see how the layout works.
+
+</td><td style='width:50%;' markdown="1">
+
+| SUBTOTAL | EUR 1250 |
+|---|---|
+| TAX | 0% |
+| &nbsp; |  |
+| <span class='bigger bold'>BALANCE DUE</span> | <span class='bigger bold'>EUR 1250</span> |
+
+</td>
+<tr/></tbody></table>
+
+[LAMDWIKIPOST 20220730105254; VER 20220730155757; ]
+
+## INVOICE
+
+{wide}{header INV0000}
+
+2021/12/32 | On Recipient
+
+### Wu Yiming
+
+Apartment C11, Xi'an Jiaotong University iHarbor Campus, Qingdu District
+
+XianYang City, ShannXi Province, China
+
+710000
+
++86 15198063018
+
+xp8110@outlook.com
+
+### <span class='gray smaller'>BILL TO</span><br /> Blender Foundation
+
+Buikslotermeerplein 161
+
+1025 ET Amsterdam, the Netherlands
+
+foundation@blender.org
+
+<br />
+
+| DESCRIPTION | RATE | QTY | AMOUNT |
+|---|---|---|---|
+| August Developer Grant | EUR 1250 | 1 | EUR 1250 |
+
+<table class='clean_table'><tbody><tr>
+<td style='width:50%;' markdown="1">
+
+### <span class='gray smaller'>NOTES</span>
+
+Note 
+
+I don't care whatever things with complexity, just a longer sentence to see how the layout works.
+
+I don't care whatever things with complexity, just a longer sentence to see how the layout works.
+
+</td><td style='width:50%;' markdown="1">
+
+| SUBTOTAL | EUR 1250 |
+|---|---|
+| TAX | 0% |
+| &nbsp; |  |
+| <span class='bigger bold'>BALANCE DUE</span> | <span class='bigger bold'>EUR 1250</span> |
+
+</td>
+<tr/></tbody></table>
+
+[LAMDWIKIPOST 20220730105254; VER 20220730161927; ]
+
+## INVOICE
+
+{wide}{header INV0000} {no_time}
+
+2021/12/32 | On Recipient
+
+### Wu Yiming
+
+Apartment C11, Xi'an Jiaotong University iHarbor Campus, Qingdu District
+
+XianYang City, ShannXi Province, China
+
+710000
+
++86 15198063018
+
+xp8110@outlook.com
+
+### <span class='gray smaller'>BILL TO</span><br /> Blender Foundation
+
+Buikslotermeerplein 161
+
+1025 ET Amsterdam, the Netherlands
+
+foundation@blender.org
+
+<br />
+
+| DESCRIPTION | RATE | QTY | AMOUNT |
+|---|---|---|---|
+| August Developer Grant | EUR 1250 | 1 | EUR 1250 |
+
+<table class='clean_table'><tbody><tr>
+<td style='width:50%;' markdown="1">
+
+### <span class='gray smaller'>NOTES</span>
+
+Note 
+
+I don't care whatever things with complexity, just a longer sentence to see how the layout works.
+
+I don't care whatever things with complexity, just a longer sentence to see how the layout works.
+
+</td><td style='width:50%;' markdown="1">
+
+| SUBTOTAL | EUR 1250 |
+|---|---|
+| TAX | 0% |
+| &nbsp; |  |
+| <span class='bigger bold'>BALANCE DUE</span> | <span class='bigger bold'>EUR 1250</span> |
+
+</td>
+<tr/></tbody></table>
+
+[LAMDWIKIPOST 20220730105254; VER 20220730162656; ]
+
+## INVOICE
+
+{wide}{header INV0000}{no_time}
+
+@ invoice
+
+2021/12/32 | On Recipient
+
+### Wu Yiming
+
+Apartment C11, Xi'an Jiaotong University iHarbor Campus, Qingdu District
+
+XianYang City, ShannXi Province, China
+
+710000
+
++86 15198063018
+
+xp8110@outlook.com
+
+### <span class='gray smaller'>BILL TO</span><br /> Blender Foundation
+
+Buikslotermeerplein 161
+
+1025 ET Amsterdam, the Netherlands
+
+foundation@blender.org
+
+<br />
+
+| DESCRIPTION | RATE | QTY | AMOUNT |
+|---|---|---|---|
+| August Developer Grant | EUR 1250 | 1 | EUR 1250 |
+
+<table class='clean_table'><tbody><tr>
+<td style='width:50%;' markdown="1">
+
+### <span class='gray smaller'>NOTES</span>
+
+Note 
+
+I don't care whatever things with complexity, just a longer sentence to see how the layout works.
+
+I don't care whatever things with complexity, just a longer sentence to see how the layout works.
+
+</td><td style='width:50%;' markdown="1">
+
+| SUBTOTAL | EUR 1250 |
+|---|---|
+| TAX | 0% |
+| &nbsp; |  |
+| <span class='bigger bold'>BALANCE DUE</span> | <span class='bigger bold'>EUR 1250</span> |
+
+</td>
+<tr/></tbody></table>
+
+[LAMDWIKIPOST 20220730163509; VER 20220730163509; ]
+
+## 制图服务协议
+
+G320AC9P
+
+### <span class='gray smaller'>客户</span><br />示例客户
+
+[LAMDWIKIPOST 20220730163509; VER 20220730163553; ]
+
+## 制图服务协议
+
+G320AC9P
+
+{wide}{no_time}{header 制图服务协议 | G320AC9P}
+
+### <span class='gray smaller'>客户</span><br />示例客户
+
+[LAMDWIKIPOST 20220730163509; VER 20220730163716; ]
+
+## 制图服务协议
+
+G320AC9P
+
+{wide}{no_time}{header 制图服务协议 | G320AC9P}
+
+### <span class='gray smaller'>客户</span><br />示例客户
+
+Buikslotermeerplein 161
+
+1025 ET Amsterdam, the Netherlands
+
+foundation@blender.org
+
+### 工作内容
+
+- Whatever things we discussed.
+
+[LAMDWIKIPOST 20220730163509; VER 20220730163801; ]
+
+## 制图服务协议
+
+G320AC9P
+
+{wide}{no_time}{header 制图服务协议 | G320AC9P}
+
+### <span class='gray smaller'>客户</span><br />示例客户
+
+Buikslotermeerplein 161
+
+1025 ET Amsterdam, the Netherlands
+
+foundation@blender.org
+
+#### 交付内容
+
+- Whatever things we discussed.
+
+[LAMDWIKIPOST 20220730163509; VER 20220730163823; ]
+
+## 制图服务协议
+
+G320AC9P
+
+{wide}{no_time}{header 制图服务协议 | G320AC9P}
+
+### <span class='gray smaller'>客户</span><br />示例客户
+
+Buikslotermeerplein 161
+
+1025 ET Amsterdam, the Netherlands
+
+foundation@blender.org
+
+### 交付内容
+Whatever things we discussed.
+Whatever things we discussed. We
+
+[LAMDWIKIPOST 20220730163509; VER 20220730163906; ]
+
+## 制图服务协议
+
+G320AC9P
+
+{wide}{no_time}{header 制图服务协议 | G320AC9P}
+
+### <span class='gray smaller'>客户</span><br />示例客户
+
+Buikslotermeerplein 161
+
+1025 ET Amsterdam, the Netherlands
+
+foundation@blender.org
+
+### 交付内容
+Whatever things we discussed.
+
+Fusce tempus a tortor non facilisis. Vestibulum consectetur bibendum sagittis. Suspendisse nec urna sit amet massa pulvinar pretium. Maecenas suscipit accumsan mi eu dignissim.
+
+In dictum pellentesque gravida. Etiam posuere diam quis ultricies ultricies. Nunc tortor ligula, dapibus vitae tortor eget, consequat varius lorem. Sed hendrerit et lacus egestas malesuada.
+
+Nulla facilisi. Nullam quam ipsum.
+
+Molestie vitae nibh id, molestie blandit sapien. Etiam sed erat scelerisque libero vehicula porttitor a id arcu. Nullam tincidunt nisi sapien, vitae lobortis odio semper vel. Quisque malesuada imperdiet velit, et cursus tortor imperdiet ac.
+
+[LAMDWIKIPOST 20220730163509; VER 20220730164007; ]
+
+## 制图服务协议
+
+G320AC9P
+
+{wide}{no_time}{header 制图服务协议 | G320AC9P}
+
+### <span class='gray smaller'>客户</span><br />示例客户
+
+Buikslotermeerplein 161
+
+1025 ET Amsterdam, the Netherlands
+
+foundation@blender.org
+
+### 交付内容
+Whatever things we discussed.
+
+Fusce tempus a tortor non facilisis. Vestibulum consectetur bibendum sagittis. Suspendisse nec urna sit amet massa pulvinar pretium. Maecenas suscipit accumsan mi eu dignissim.
+
+In dictum pellentesque gravida. Etiam posuere diam quis ultricies ultricies. Nunc tortor ligula, dapibus vitae tortor eget, consequat varius lorem. Sed hendrerit et lacus egestas malesuada.
+
+Nulla facilisi. Nullam quam ipsum.
+
+Molestie vitae nibh id, molestie blandit sapien. Etiam sed erat scelerisque libero vehicula porttitor a id arcu. Nullam tincidunt nisi sapien, vitae lobortis odio semper vel. Quisque malesuada imperdiet velit, et cursus tortor imperdiet ac.
+
+### 用途限制
+
+Nulla facilisi. Nullam quam ipsum.
+
+Molestie vitae nibh id, molestie blandit sapien. Etiam sed erat scelerisque libero vehicula porttitor a id arcu. Nullam tincidunt nisi sapien, vitae lobortis odio semper vel. Quisque malesuada imperdiet velit, et cursus tortor imperdiet ac.
+
+[LAMDWIKIPOST 20220730163509; VER 20220731044119; ]
+
+## 制图服务协议
+
+G320AC9P
+
+{wide}{no_time}{header 制图服务协议 | G320AC9P}
+
+### <span class='gray smaller'>客户</span><br />示例客户
+
+Buikslotermeerplein 161
+
+1025 ET Amsterdam, the Netherlands
+
+foundation@blender.org
+
+### 工作内容
+Whatever things we discussed.
+
+Fusce tempus a tortor non facilisis. Vestibulum consectetur bibendum sagittis. Suspendisse nec urna sit amet massa pulvinar pretium. Maecenas suscipit accumsan mi eu dignissim.
+
+In dictum pellentesque gravida. Etiam posuere diam quis ultricies ultricies. Nunc tortor ligula, dapibus vitae tortor eget, consequat varius lorem. Sed hendrerit et lacus egestas malesuada.
+
+Nulla facilisi. Nullam quam ipsum.
+
+Molestie vitae nibh id, molestie blandit sapien. Etiam sed erat scelerisque libero vehicula porttitor a id arcu. Nullam tincidunt nisi sapien, vitae lobortis odio semper vel. Quisque malesuada imperdiet velit, et cursus tortor imperdiet ac.
+
+### 使用限制
+
+Nulla facilisi. Nullam quam ipsum.
+
+Molestie vitae nibh id, molestie blandit sapien. Etiam sed erat scelerisque libero vehicula porttitor a id arcu. Nullam tincidunt nisi sapien, vitae lobortis odio semper vel. Quisque malesuada imperdiet velit, et cursus tortor imperdiet ac.
+
+### 交付流程
+
+| 阶段 | 时间 |
+|---|---|
+| 元素和布局草图 | 7天 |
+| 草图修改 | 反馈之后7天 |
+| 完成整体工作 | 确认后30天 |
+
+[LAMDWIKIPOST 20220730163509; VER 20220731045217; ]
+
+## 制图服务协议
+
+G320AC9P
+
+{wide}{no_time}{header 制图服务协议 | G320AC9P}
+
+### <span class='gray smaller'>客户</span><br />示例客户
+
+Buikslotermeerplein 161
+
+1025 ET Amsterdam, the Netherlands
+
+foundation@blender.org
+
+### 工作内容
+Whatever things we discussed.
+
+Fusce tempus a tortor non facilisis. Vestibulum consectetur bibendum sagittis. Suspendisse nec urna sit amet massa pulvinar pretium. Maecenas suscipit accumsan mi eu dignissim.
+
+In dictum pellentesque gravida. Etiam posuere diam quis ultricies ultricies. Nunc tortor ligula, dapibus vitae tortor eget, consequat varius lorem. Sed hendrerit et lacus egestas malesuada.
+
+### 使用限制
+
+Nulla facilisi. Nullam quam ipsum.
+
+Molestie vitae nibh id, molestie blandit sapien. Etiam sed erat scelerisque libero vehicula porttitor a id arcu. Nullam tincidunt nisi sapien, vitae lobortis odio semper vel. Quisque malesuada imperdiet velit, et cursus tortor imperdiet ac.
+
+### 交付流程
+
+| 阶段 | 时间 |
+|---|---|
+| 元素和布局草图 | 首款支付之日起7天 |
+| 草图修改 | 草图反馈之后7天 |
+| 完成正式图像 | 修改确认后30天 |
+
+### 金额和付款流程
+
+| 项目 | 阶段 | 金额 |
+|---|---|---|
+| 制图测试 | 需确认制图风格 | CNY 1000 |
+| 首付(扣除了制图测试费用) | 签署服务协议后 | CNY 24000 |
+| 确认款 | 确认全部草图后,启动正式工作之前 | CNY15000 |
+| 交付款 | 交付全部完成作品之前 | CNY10000 |
+|---|---|---|
+
+[LAMDWIKIPOST 20220730163509; VER 20220731045251; ]
+
+## 制图服务协议
+
+G320AC9P
+
+{wide}{no_time}{header 制图服务协议 | G320AC9P}
+
+### <span class='gray smaller'>客户</span><br />示例客户
+
+Buikslotermeerplein 161
+
+1025 ET Amsterdam, the Netherlands
+
+foundation@blender.org
+
+### 工作内容
+Whatever things we discussed.
+
+Fusce tempus a tortor non facilisis. Vestibulum consectetur bibendum sagittis. Suspendisse nec urna sit amet massa pulvinar pretium. Maecenas suscipit accumsan mi eu dignissim.
+
+In dictum pellentesque gravida. Etiam posuere diam quis ultricies ultricies. Nunc tortor ligula, dapibus vitae tortor eget, consequat varius lorem. Sed hendrerit et lacus egestas malesuada.
+
+### 使用限制
+
+Nulla facilisi. Nullam quam ipsum.
+
+Molestie vitae nibh id, molestie blandit sapien. Etiam sed erat scelerisque libero vehicula porttitor a id arcu. Nullam tincidunt nisi sapien, vitae lobortis odio semper vel. Quisque malesuada imperdiet velit, et cursus tortor imperdiet ac.
+
+### 交付流程
+
+| 阶段 | 时间 |
+|---|---|
+| 元素和布局草图 | 首款支付之日起7天 |
+| 草图修改 | 草图反馈之后7天 |
+| 完成正式图像 | 修改确认后30天 |
+
+### 金额和付款流程
+
+| 项目 | 阶段 | 金额 |
+|---|---|---:|
+| 制图测试 | 需确认制图风格 | CNY 1000 |
+| 首付(扣除了制图测试费用) | 签署服务协议后 | CNY 24000 |
+| 确认款 | 确认全部草图后,启动正式工作之前 | CNY 15000 |
+| 交付款 | 交付全部完成作品之前 | CNY 10000 |
+|---|---|---|
+
+[LAMDWIKIPOST 20220730163509; VER 20220731050611; ]
+
+## 制图服务协议
+
+G320AC9P
+
+{wide}{no_time}{header 制图服务协议 | G320AC9P}
+
+### <span class='gray smaller'>客户</span><br />示例客户
+
+Buikslotermeerplein 161
+
+1025 ET Amsterdam, the Netherlands
+
+foundation@blender.org
+
+### 工作内容
+Whatever things we discussed.
+
+Fusce tempus a tortor non facilisis. Vestibulum consectetur bibendum sagittis. Suspendisse nec urna sit amet massa pulvinar pretium. Maecenas suscipit accumsan mi eu dignissim.
+
+In dictum pellentesque gravida. Etiam posuere diam quis ultricies ultricies. Nunc tortor ligula, dapibus vitae tortor eget, consequat varius lorem. Sed hendrerit et lacus egestas malesuada.
+
+### 使用限制
+
+Nulla facilisi. Nullam quam ipsum.
+
+Molestie vitae nibh id, molestie blandit sapien. Etiam sed erat scelerisque libero vehicula porttitor a id arcu. Nullam tincidunt nisi sapien, vitae lobortis odio semper vel. Quisque malesuada imperdiet velit, et cursus tortor imperdiet ac.
+
+### 交付流程
+
+| 阶段 | 时间 |
+|---|---|
+| 元素和布局草图 | 首款支付之日起7天 |
+| 草图修改 | 草图反馈之后7天 |
+| 完成正式图像 | 修改确认后30天 |
+
+### 金额和付款流程
+
+| 项目 | 阶段 | 金额 |
+|---|---|---:|
+| 制图测试 | 需确认制图风格 | CNY 1000 |
+| 首付(扣除了制图测试费用) | 签署服务协议后 | CNY 24000 |
+| 确认款 | 确认全部草图后,启动正式工作之前 | CNY 15000 |
+| 交付款 | 交付全部完成作品之前 | CNY 10000 |
+|---|---|---|
+
+<table class='clean_table'><tbody><tr>
+<td style='width:50%;' markdown="1">
+
+### <span class='gray smaller'>付款信息</span>
+
+Note 
+
+I don't care whatever things with complexity, just a longer sentence to see how the layout works.
+
+I don't care whatever things with complexity, just a longer sentence to see how the layout works.
+
+</td><td style='width:50%;' markdown="1">
+
+| 小计 | CNY 50000 |
+|---|---|
+| 增值税(适用) | 0% |
+| 文化行业个人所得税(适用) | 0.5% |
+| &nbsp; |  |
+| <span class='bigger bold'>总计</span> | <span class='bigger bold'>CNY 50251</span> |
+
+</td>
+<tr/></tbody></table>
+
+[LAMDWIKIPOST 20220730163509; VER 20220731050717; ]
+
+## 制图服务协议
+
+G320AC9P
+
+{wide}{no_time}{header 制图服务协议 | G320AC9P}
+
+### <span class='gray smaller'>客户</span><br />示例客户
+
+Buikslotermeerplein 161
+
+1025 ET Amsterdam, the Netherlands
+
+foundation@blender.org
+
+### 工作内容
+Whatever things we discussed.
+
+Fusce tempus a tortor non facilisis. Vestibulum consectetur bibendum sagittis. Suspendisse nec urna sit amet massa pulvinar pretium. Maecenas suscipit accumsan mi eu dignissim.
+
+In dictum pellentesque gravida. Etiam posuere diam quis ultricies ultricies. Nunc tortor ligula, dapibus vitae tortor eget, consequat varius lorem. Sed hendrerit et lacus egestas malesuada.
+
+### 使用限制
+
+Nulla facilisi. Nullam quam ipsum.
+
+Molestie vitae nibh id, molestie blandit sapien. Etiam sed erat scelerisque libero vehicula porttitor a id arcu. Nullam tincidunt nisi sapien, vitae lobortis odio semper vel. Quisque malesuada imperdiet velit, et cursus tortor imperdiet ac.
+
+### 媒介、交付材料和流程
+
+材料的交付时间照下面表格中所描述的进行:
+
+| 阶段 | 时间 |
+|---|---|
+| 元素和布局草图 | 首款支付之日起7天 |
+| 草图修改 | 草图反馈之后7天 |
+| 完成正式图像 | 修改确认后30天 |
+
+### 金额和付款阶段
+
+| 项目 | 阶段 | 金额 |
+|---|---|---:|
+| 制图测试 | 需确认制图风格 | CNY 1000 |
+| 首付(扣除了制图测试费用) | 签署服务协议后 | CNY 24000 |
+| 确认款 | 确认全部草图后,启动正式工作之前 | CNY 15000 |
+| 交付款 | 交付全部完成作品之前 | CNY 10000 |
+
+<table class='clean_table'><tbody><tr>
+<td style='width:50%;' markdown="1">
+
+### 付款信息
+
+Note 
+
+I don't care whatever things with complexity, just a longer sentence to see how the layout works.
+
+I don't care whatever things with complexity, just a longer sentence to see how the layout works.
+
+</td><td style='width:50%;' markdown="1">
+
+| 小计 | CNY 50000 |
+|---|---|
+| 增值税(适用) | 0% |
+| 文化行业个人所得税(适用) | 0.5% |
+| &nbsp; |  |
+| <span class='bigger bold'>总计</span> | <span class='bigger bold'>CNY 50251</span> |
+
+</td>
+<tr/></tbody></table>
+
+[LAMDWIKIPOST 20220730163509; VER 20220731084339; ]
+
+## 制图服务协议
+
+G320AC9P
+
+{wide}{no_time}{header 制图服务协议 | G320AC9P}
+
+### <span class='gray smaller'>客户</span><br />示例客户
+
+Buikslotermeerplein 161
+
+1025 ET Amsterdam, the Netherlands
+
+foundation@blender.org
+
+### 工作内容
+
+Whatever things we discussed.
+
+Fusce tempus a tortor non facilisis. Vestibulum consectetur bibendum sagittis. Suspendisse nec urna sit amet massa pulvinar pretium. Maecenas suscipit accumsan mi eu dignissim.
+
+In dictum pellentesque gravida. Etiam posuere diam quis ultricies ultricies. Nunc tortor ligula, dapibus vitae tortor eget, consequat varius lorem. Sed hendrerit et lacus egestas malesuada.
+
+### 权利和用途限制
+
+客户可将交付的最终作品用于:仅适用于宣传客户产品的海报、传单、折页等非出版物类的数字或传统印刷品。
+
+客户不拥有作品的版权。
+
+客户仅能以非演绎的方式在上述用途范围内使用最终作品。
+
+### 媒介、交付材料和流程
+
+材料的交付按以下表格中所描述的时间点进行:
+
+| 阶段 | 时间 |
+|---|---|
+| 元素和布局草图 | 首款支付之日起7天内 |
+| 草图修改 | 草图反馈之后7天内 |
+| 完成正式图像 | 修改确认后30天内 |
+
+### 金额和付款阶段
+
+| 项目 | 阶段 | 金额 |
+|---|---|---:|
+| 制图测试 | 需确认制图风格 | CNY 1000 |
+| 首付(扣除了制图测试费用) | 签署服务协议后 | CNY 24000 |
+| 确认款 | 确认全部草图后,启动正式工作之前 | CNY 15000 |
+| 交付款 | 交付全部完成作品之前 | CNY 10000 |
+
+<table class='clean_table'><tbody><tr>
+<td style='width:50%;' markdown="1">
+
+### 付款信息
+
+Note 
+
+I don't care whatever things with complexity, just a longer sentence to see how the layout works.
+
+I don't care whatever things with complexity, just a longer sentence to see how the layout works.
+
+</td><td style='width:50%;' markdown="1">
+
+| 小计 | CNY 50000 |
+|---|---|
+| 增值税(适用) | 0% |
+| 文化行业个人所得税(适用) | 0.5% |
+| &nbsp; |  |
+| <span class='bigger bold'>总计</span> | <span class='bigger bold'>CNY 50251</span> |
+
+</td>
+<tr/></tbody></table>
+
+[LAMDWIKIPOST 20220730163509; VER 20220731085549; ]
+
+## 制图服务协议
+
+G320AC9P
+
+{wide}{no_time}{header 制图服务协议 | G320AC9P}
+
+### <span class='gray smaller'>客户</span><br />示例客户
+
+Buikslotermeerplein 161
+
+1025 ET Amsterdam, the Netherlands
+
+foundation@blender.org
+
+### 工作内容
+
+为客户提供的“什么玩意”剧情制作插图。
+
+格式:24页,单页A4的印刷尺寸。
+
+风格:风格测试页确定的风格。
+
+### 权利和用途限制
+
+客户可将交付的最终作品用于:仅适用于宣传客户产品的海报、传单、折页等非出版物类的数字或传统印刷品。
+
+客户不拥有作品的版权。
+
+客户仅能以非演绎的方式在上述用途范围内使用最终作品。
+
+### 交付流程
+
+材料的交付按以下表格中所描述的时间点进行:
+
+| 阶段 | 时间 |
+|---|---|
+| 元素和布局草图 | 首款支付之日起7天内 |
+| 草图修改 | 草图反馈之后7天内 |
+| 完成正式图像 | 修改确认后30天内 |
+
+### 金额和付款阶段
+
+| 项目 | 阶段 | 金额 |
+|---|---|---:|
+| 制图测试 | 需确认制图风格 | CNY 1000 |
+| 首付(扣除了制图测试费用) | 签署服务协议后 | CNY 24000 |
+| 确认款 | 确认全部草图后,启动正式工作之前 | CNY 15000 |
+| 交付款 | 交付全部完成作品之前 | CNY 10000 |
+
+<table class='clean_table'><tbody><tr>
+<td style='width:50%;' markdown="1">
+
+### 付款信息
+
+Note 
+
+I don't care whatever things with complexity, just a longer sentence to see how the layout works.
+
+I don't care whatever things with complexity, just a longer sentence to see how the layout works.
+
+</td><td style='width:50%;' markdown="1">
+
+| 小计 | CNY 50000 |
+|---|---|
+| 增值税(参考) | 0% |
+| 文化行业个人所得税(参考) | 0.5% |
+| &nbsp; |  |
+| <span class='bigger bold'>总计(参考)</span> | <span class='bigger bold'>CNY 50251</span> |
+
+</td>
+<tr/></tbody></table>
+
+[LAMDWIKIPOST 20220730163509; VER 20220731090053; ]
+
+## 制图服务协议
+
+G320AC9P
+
+{wide}{no_time}{header 制图服务协议 | G320AC9P}
+
+### <span class='gray smaller'>客户</span><br />示例客户
+
+Buikslotermeerplein 161
+
+1025 ET Amsterdam, the Netherlands
+
+foundation@blender.org
+
+### 工作内容
+
+为客户提供的“什么玩意”剧情制作插图。
+
+格式:24页,单页A4的印刷尺寸。
+
+风格:风格测试页确定的风格。
+
+#### <span class='gray'>附加要求</span>
+
+首尾页印刷留白为10mm,其余页20mm,在30mm内不出现文字,该尺寸在A4外轮廓尺寸上缩减。
+
+首尾页需要背面摊开为一幅连续的图像。
+
+### 权利和用途限制
+
+客户可将交付的最终作品用于:仅适用于宣传客户产品的海报、传单、折页等非出版物类的数字或传统印刷品。
+
+客户不拥有作品的版权。
+
+客户仅能以非演绎的方式在上述用途范围内使用最终作品。
+
+### 交付流程
+
+材料的交付按以下表格中所描述的时间点进行:
+
+| 阶段 | 时间 |
+|---|---|
+| 元素和布局草图 | 首款支付之日起7天内 |
+| 草图修改 | 草图反馈之后7天内 |
+| 完成正式图像 | 修改确认后30天内 |
+
+### 金额和付款阶段
+
+| 项目 | 阶段 | 金额 |
+|---|---|---:|
+| 制图测试 | 需确认制图风格 | CNY 1000 |
+| 首付(扣除了制图测试费用) | 签署服务协议后 | CNY 24000 |
+| 确认款 | 确认全部草图后,启动正式工作之前 | CNY 15000 |
+| 交付款 | 交付全部完成作品之前 | CNY 10000 |
+
+<table class='clean_table'><tbody><tr>
+<td style='width:50%;' markdown="1">
+
+### 付款信息
+
+Note 
+
+I don't care whatever things with complexity, just a longer sentence to see how the layout works.
+
+I don't care whatever things with complexity, just a longer sentence to see how the layout works.
+
+</td><td style='width:50%;' markdown="1">
+
+| 小计 | CNY 50000 |
+|---|---|
+| 增值税(参考) | 0% |
+| 文化行业个人所得税(参考) | 0.5% |
+| &nbsp; |  |
+| <span class='bigger bold'>总计(参考)</span> | <span class='bigger bold'>CNY 50251</span> |
+
+</td>
+<tr/></tbody></table>
+
+[LAMDWIKIPOST 20220730163509; VER 20220731091543; ]
+
+## 制图服务协议
+
+G320AC9P
+
+{wide}{no_time}{header 制图服务协议 | G320AC9P}
+
+### <span class='gray smaller'>客户</span><br />示例客户
+
+Buikslotermeerplein 161
+
+1025 ET Amsterdam, the Netherlands
+
+foundation@blender.org
+
+
+### 工作内容
+
+<table class='clean_table'><tbody><tr>
+<td style='width:50%;' markdown="1">
+
+作者为客户的“什么玩意”剧情以风格测试页确定的风格制作插图。
+
+数字文件形式交付。
+
+### <span class='gray smaller'>其他需求</span>
+
+首尾页需要背面摊开为一幅连续的图像。
+
+</td><td style='width:50%;' markdown="1">
+
+| 特征 | 选择 |
+|---|---|
+| 图像尺寸 | A4 - 出血^1 |
+| 出血 | 20mm |
+| 首页 | 右页 |
+| 是否对开 | 是 |
+| 分辨率 | 600dpi |
+| 文件格式 | PNG |
+
+</td>
+<tr/></tbody></table>
+
+### 权利和用途限制
+
+客户可将交付的最终作品用于:仅适用于宣传客户产品的海报、传单、折页等非出版物类的数字或传统印刷品。
+
+客户不拥有作品的版权。
+
+客户仅能以非演绎的方式在上述用途范围内使用最终作品。
+
+客户需要在使用作品时为作者署名。
+
+作者可以通过互联网和印刷品展示本项目的中间和最终作品以宣传作者的制图服务。
+
+### 交付流程
+
+不提供作品纸质原件。材料的交付按以下表格中所描述的时间点进行:
+
+| 阶段 | 时间 |
+|---|---|
+| 元素和布局草图 | 首款支付之日起7天内 |
+| 草图修改 | 草图反馈之后7天内 |
+| 完成正式图像 | 修改确认后30天内 |
+
+### 金额和付款阶段
+
+| 项目 | 阶段 | 金额 |
+|---|---|---:|
+| 制图测试 | 需确认制图风格 | CNY 1000 |
+| 首付(扣除了制图测试费用) | 签署服务协议后 | CNY 24000 |
+| 确认款 | 确认全部草图后,启动正式工作之前 | CNY 15000 |
+| 交付款 | 交付全部完成作品之前 | CNY 10000 |
+
+<table class='clean_table'><tbody><tr>
+<td style='width:50%;' markdown="1">
+
+### 付款信息
+
+Note 
+
+I don't care whatever things with complexity, just a longer sentence to see how the layout works.
+
+I don't care whatever things with complexity, just a longer sentence to see how the layout works.
+
+</td><td style='width:50%;' markdown="1">
+
+| 小计 | CNY 50000 |
+|---|---|
+| 增值税(参考) | 0% |
+| 文化行业个人所得税(参考) | 0.5% |
+| &nbsp; |  |
+| <span class='bigger bold'>总计(参考)</span> | <span class='bigger bold'>CNY 50251</span> |
+
+</td>
+<tr/></tbody></table>
+
+### 一般条款
+
+1. 在支付交付款前,您可以通过小尺寸图像检查效果。交付款到帐的一个工作日内我将发送全部全尺寸图像。
+2.
+
+[LAMDWIKIPOST 20220730163509; VER 20220731101211; ]
+
+## 制图服务协议
+
+G320AC9P
+
+{wide}{no_time}{header 制图服务协议 | G320AC9P}
+
+### <span class='gray smaller'>客户</span><br />示例客户
+
+Buikslotermeerplein 161
+
+1025 ET Amsterdam, the Netherlands
+
+foundation@blender.org
+
+
+### 工作内容
+
+<table class='clean_table'><tbody><tr>
+<td style='width:50%;' markdown="1">
+
+艺术家(我)按风格测试页确定的外观为客户(您)的“什么玩意”剧情制作插图。
+
+以数字文件形式交付。
+
+### <span class='gray smaller'>其他需求</span>
+
+首尾页需要背面摊开为一幅连续的图像。
+
+</td><td style='width:50%;' markdown="1">
+
+| 特征 | 选择 |
+|---|---|
+| 图像尺寸 | A4 - 出血^1 |
+| 出血 | 20mm |
+| 首页 | 右页 |
+| 是否对开 | 是 |
+| 分辨率 | 600dpi |
+| 文件格式 | PNG |
+
+</td>
+<tr/></tbody></table>
+
+### 权利和用途限制
+
+客户(您)可将交付的最终作品用于:仅适用于宣传客户产品的海报、传单、折页等非出版物类的数字或传统印刷品。
+
+客户不拥有作品的版权。
+
+客户仅能以非演绎的方式在上述用途范围内使用最终作品。
+
+客户需要在使用作品时为作者署名。
+
+艺术家可以通过互联网和印刷品展示本项目的中间和最终作品以宣传自身的制图服务。
+
+### 交付流程
+
+不提供作品纸质原件。材料的交付按以下表格中所描述的时间点进行:
+
+| 阶段 | 时间 |
+|---|---|
+| 元素和布局草图 | 首款支付之日起7天内 |
+| 草图修改 | 草图反馈之后7天内 |
+| 完成正式图像 | 修改确认后30天内 |
+
+### 金额和付款阶段
+
+| 项目 | 阶段 | 金额 |
+|---|---|---:|
+| 制图测试 | 需确认制图风格 | CNY 1000 |
+| 首付(扣除了制图测试费用) | 签署服务协议后 | CNY 24000 |
+| 确认款 | 确认全部草图后,启动正式工作之前 | CNY 15000 |
+| 交付款 | 交付全部完成作品之前 | CNY 10000 |
+
+<table class='clean_table'><tbody><tr>
+<td style='width:50%;' markdown="1">
+
+### 付款信息
+
+Note 
+
+I don't care whatever things with complexity, just a longer sentence to see how the layout works.
+
+I don't care whatever things with complexity, just a longer sentence to see how the layout works.
+
+</td><td style='width:50%;' markdown="1">
+
+| 小计 | CNY 50000 |
+|---|---|
+| 增值税(参考) | 0% |
+| 文化行业个人所得税(参考) | 0.5% |
+| &nbsp; |  |
+| <span class='bigger bold'>总计(参考)</span> | <span class='bigger bold'>CNY 50251</span> |
+
+</td>
+<tr/></tbody></table>
+
+### 一般条款
+
+1. 在支付交付款前,您(客户)可以通过小尺寸图像检查效果。交付款到帐的一个工作日内您将通过电子邮件收到全部全尺寸图像。
+2. 您若需要将作品用于前述用途以外的领域,请重新协商附加版权协议。
+3. 在工作完成之前,您若遭遇财务或时间上的困难而无法继续履行协议,请告知我(艺术家)。自告知之日起3年内,您仍可以返回该项目,我将在您支付下一阶段款项后继续履行本协议。
+4.
+

+ 100 - 0
archive/202208.md

@@ -0,0 +1,100 @@
+[LAMDWIKIPOST 20220806102622; VER 20220806102622; HASI 20211028081604.jpg; ]
+
+[![图片 keep](images/20211028081604.jpg) 战术射精](@射精)
+
+[LAMDWIKIPOST 20220806102622; VER 20220806103206; HASI 20211028081604.jpg; ]
+
+[![图片 keep_inline](images/20211028081604.jpg) 战术射精](@射精)
+
+[LAMDWIKIPOST 20220806102622; VER 20220806103246; HASI 20211028081604.jpg; ]
+
+[![图片 keep_inline original](images/20211028081604.jpg) 战术射精](@射精)
+
+[LAMDWIKIPOST 20220806102622; VER 20220806103305; HASI 20211028081604.jpg; ]
+
+[![图片](images/20211028081604.jpg) 战术射精](@射精)
+
+[LAMDWIKIPOST 20220806102622; VER 20220806111238; HASI 20211028081604.jpg; ]
+
+- [![图片](images/20211028081604.jpg) 战术射精](@射精)
+- [![图片](images/20211028081604.jpg) 战术射精](@射精)
+
+[LAMDWIKIPOST 20220806122959; VER 20220806122959; HASP 20220730163509; HASI 20211224061904.jpg; ]
+
+[![图片](images/20211224061904.jpg) 额](20220730163509)
+
+[LAMDWIKIPOST 20220806122959; VER 20220806153110; HASP 20220730163509; HASI 20211224061904.jpg; ]
+
+[![图片 5em](images/20211224061904.jpg) 额](20220730163509)
+
+[LAMDWIKIPOST 20220806122959; VER 20220809105853; HASP 20220730163509; HASI 20211224061904.jpg; ]
+
+[![图片 5em](images/20211224061904.jpg) 额](20220730163509)
+
+33
+
+[LAMDWIKIPOST 20220806122959; VER 20220809110657; HASP 20220730163509; HASI 20211224061904.jpg; ]
+
+[![图片 5em](images/20211224061904.jpg) 额](20220730163509)
+
+33
+
+{wide}
+
+[LAMDWIKIPOST 20220809110324; VER 20220809110324; ]
+
+# 1231231
+
+asdasdas
+
+[LAMDWIKIPOST 20220809110324; VER 20220809113940; ]
+
+#
+asdasdas
+
+[LAMDWIKIPOST 20220809110336; VER 20220809110336; ]
+
+qwe123412312eww
+
+[LAMDWIKIPOST 20220810123940; VER 20220810123940; HASP 20220729043434; ]
+
+[超级射精](20220729043434) 哒哒哒哒哒!{reversed}
+
+[LAMDWIKIPOST 20220816081800; VER 20220816081800; ]
+
+123
+
+[LAMDWIKIPOST 20220816081800; VER 20220816081828; ]
+
+123
+
+{no_time} {wide}
+
+[LAMDWIKIPOST 20220816081800; VER 20220816081948; ]
+
+123
+
+{no_time} {wide} {no_title}
+
+[LAMDWIKIPOST 20220816081800; VER 20220819163541; ]
+
+123
+
+{no_time} {wide} {no_title}cccc
+
+[LAMDWIKIPOST 20220816081800; VER 20220819163630; MTHREAD 20220819163603 | 20220816081800 20220819163533 20220819163534;]
+
+
+
+[LAMDWIKIPOST 20220816081800; VER 20231007170226; ]
+
+123
+
+{no_time} {wide} {no_title}cccc
+
+{slides}
+
+[LAMDWIKIPOST 20220819163533; VER 20220819163533; ]
+
+qwe
+

+ 42 - 0
archive/202301.md

@@ -0,0 +1,42 @@
+[LAMDWIKIPOST 20230109041814; VER 20230109041814; ]
+
+其3132123
+
+adsasfsfd是sad
+
+
+afdsfa
+
+[LAMDWIKIPOST 20230109041814; VER 20230117153116; ]
+
+其3132123
+
+adsasfsfd是sad
+
+
+afdsfa
+
+{reversed d}
+
+[LAMDWIKIPOST 20230109041814; VER 20230117153307; ]
+
+其3132123
+
+adsasfsfd是sad
+
+
+afdsfa
+
+{reversed d}
+
+[LAMDWIKIPOST 20230109041814; VER 20230117153333; ]
+
+其3132123
+
+adsasfsfd是sad
+
+
+afdsfa
+
+{reversed d}
+

+ 6 - 0
archive/202302.md

@@ -0,0 +1,6 @@
+[LAMDWIKIPOST 20230207101741; VER 20230207101741; ]
+
+# 射精训练基地
+
+什么玩意
+

+ 91 - 0
archive/202310.md

@@ -0,0 +1,91 @@
+[LAMDWIKIPOST 20231007181935; VER 20231007181935; ]
+
+## unique challenges in the implementation of line art
+
+- cuts the line instead of keeping occlusion result in ambigious state, conforms better to shapes but needs a higher precision of floating point intermediate storage, and not easy have this part of data exposed reasonably to the outside for custom manipulation
+- doesn't do fully tiled processing means it's kinda slow sometimes.
+- intersection filtering is a headache since it's global.
+- the fact that we use GP to rasterize means currently chaining plays a very big role in line quality which posts a design optimization requirement for GP which will talk later.
+- Shadow project means doing visibility for 2 times which is slow.
+- Contour means doing flag cutting for yet another time, and also very hard to filter under current setup.
+
+[LAMDWIKIPOST 20231007181950; VER 20231007181950; ]
+
+## node based line filtering possible design choices
+
+- feed multiple attributes [mockup todo]
+- feed line styles [mockup todo]
+- possible integration with loop/simulation node, which can solve multiple line type issue [mockup todo] due to multi step projection
+- compatibility/versioning with previous modifier setup
+- stroke baking design?
+- z-reorder: multiple passes [mockup todo]
+    - need compositor involvement
+
+[LAMDWIKIPOST 20231007181950; VER 20231008052649; HASI 20211224061904.jpg; ]
+
+## node based line filtering possible design choices
+
+- feed multiple attributes [mockup todo]
+- feed line styles [mockup todo]
+- possible integration with loop/simulation node, which can solve multiple line type issue [mockup todo] due to multi step projection
+- compatibility/versioning with previous modifier setup
+- stroke baking design?
+- z-reorder: multiple passes [mockup todo]
+    - need compositor involvement
+
+![图片](images/20211224061904.jpg)
+
+[LAMDWIKIPOST 20231007181950; VER 20231008054401; HASI 20211224061904.jpg; ]
+
+## node based line filtering possible design choices
+
+- feed multiple attributes [mockup todo]
+- feed line styles [mockup todo]
+- possible integration with loop/simulation node, which can solve multiple line type issue [mockup todo] due to multi step projection
+- compatibility/versioning with previous modifier setup
+- stroke baking design?
+- z-reorder: multiple passes [mockup todo]
+    - need compositor involvement
+
+![图片](images/20211224061904.jpg)
+![图片](images/20211224061904.jpg)
+![图片](images/20211224061904.jpg)
+
+[LAMDWIKIPOST 20231007181950; VER 20231008054547; HASI 20211224061904.jpg; ]
+
+## node based line filtering possible design choices
+
+- feed multiple attributes [mockup todo]
+- feed line styles [mockup todo]
+- possible integration with loop/simulation node, which can solve multiple line type issue [mockup todo] due to multi step projection
+- compatibility/versioning with previous modifier setup
+- stroke baking design?
+- z-reorder: multiple passes [mockup todo]
+    - need compositor involvement
+
+![图片 keep_inline original](images/20211224061904.jpg)
+![图片 keep_inline original](images/20211224061904.jpg)
+
+[LAMDWIKIPOST 20231007181950; VER 20231008054604; HASI 20211224061904.jpg; ]
+
+## node based line filtering possible design choices
+
+![图片 keep_inline original](images/20211224061904.jpg)
+![图片 keep_inline original](images/20211224061904.jpg)
+
+- feed multiple attributes [mockup todo]
+- feed line styles [mockup todo]
+- possible integration with loop/simulation node, which can solve multiple line type issue [mockup todo] due to multi step projection
+- compatibility/versioning with previous modifier setup
+- stroke baking design?
+- z-reorder: multiple passes [mockup todo]
+    - need compositor involvement
+
+[LAMDWIKIPOST 20231007182013; VER 20231007182013; ]
+
+## benifits
+
+- doesn't have evaluation conflict in depsgraph
+- much better intersection filtering effect
+- line styles can be processed further with geometry generation downstream
+

+ 12 - 0
archive/202312.md

@@ -0,0 +1,12 @@
+[LAMDWIKIPOST 20231221074854; VER 20231221074854; ]
+
+123
+
+[LAMDWIKIPOST 20231221074858; VER 20231221074858; ]
+
+rthgrdfgsdfgdf
+
+[LAMDWIKIPOST 20231221074903; VER 20231221074903; INTO 20231221074858V20240106143751; ]
+
+234werfsfasdfa
+

+ 12 - 0
archive/202404.md

@@ -0,0 +1,12 @@
+[LAMDWIKIPOST 20240411140128; VER 20240411140128; HASP 20231228092214; HASI 20211109160323.jpg; ]
+
+[![图片](images/20211109160323.jpg)  sdfsad](20231228092214)
+
+[LAMDWIKIPOST 20240411140128; VER 20240411140134; HASP 20231228092214; HASI 20211109160323.jpg; ]
+
+[![10em](images/20211109160323.jpg)  sdfsad](20231228092214)
+
+[LAMDWIKIPOST 20240423145632; VER 20240423145632; ]
+
+qweq
+

+ 5 - 0
auth/config.php

@@ -0,0 +1,5 @@
+<?php
+define('APP_URL', 'http://localhost/auth/');
+define('APP_KEY', '8ddec22db026695260706eafe429f65caf90c34eab37d24588e212d1714b65b3');
+define('USER_HASH', 'e72e392d30878dcda4d63257d4065d14');
+define('USER_URL', 'https://www.wellobserve.com');

+ 400 - 0
auth/index.php

@@ -0,0 +1,400 @@
+<?php
+function error_page($header, $body, $http = '400 Bad Request')
+{
+    $protocol = isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0';
+    header($protocol . ' ' . $http);
+    $html = <<<HTML
+<!doctype html>
+<html>
+    <head>
+        <style>
+            .error{
+                width:100%;
+                text-align:center;
+                margin-top:10%;
+            }
+        </style>
+        <title>Error: $header</title>
+    </head>
+    <body>
+        <div class='error'>
+            <h1>Error: $header</h1>
+            <p>$body</p>
+        </div>
+    </body>
+</html>
+HTML;
+    die($html);
+}
+
+$configfile= __DIR__ . '/config.php';
+if (file_exists($configfile)) {
+    include_once $configfile;
+} else {
+    error_page(
+        'Configuration Error',
+        'Endpoint not yet configured, visit <a href="setup.php">setup.php</a> for instructions on how to set it up.'
+    );
+}
+
+// Enable string comparison in constant time.
+if (!function_exists('hash_equals')) {
+    function hash_equals($known_string, $user_string)
+    {
+        $known_length = strlen($known_string);
+        if ($known_length !== strlen($user_string)) {
+            return false;
+        }
+        $match = 0;
+        for ($i = 0; $i < $known_length; $i++) {
+            $match |= (ord($known_string[$i]) ^ ord($user_string[$i]));
+        }
+        return $match === 0;
+    }
+}
+
+// Signed codes always have an time-to-live, by default 1 year (31536000 seconds).
+function create_signed_code($key, $message, $ttl = 31536000, $appended_data = '')
+{
+    $expires = time() + $ttl;
+    $body = $message . $expires . $appended_data;
+    $signature = hash_hmac('sha256', $body, $key);
+    return dechex($expires) . ':' . $signature . ':' . base64_url_encode($appended_data);
+}
+
+function verify_signed_code($key, $message, $code)
+{
+    $code_parts = explode(':', $code, 3);
+    if (count($code_parts) !== 3) {
+        return false;
+    }
+    $expires = hexdec($code_parts[0]);
+    if (time() > $expires) {
+        return false;
+    }
+    $body = $message . $expires . base64_url_decode($code_parts[2]);
+    $signature = hash_hmac('sha256', $body, $key);
+    return hash_equals($signature, $code_parts[1]);
+}
+
+function verify_password($pass)
+{
+    $hash_user = trim(preg_replace('/^https?:\/\//', '', USER_URL), '/');
+    $hash = md5($hash_user . $pass . APP_KEY);
+
+    return hash_equals(USER_HASH, $hash);
+}
+
+function filter_input_regexp($type, $variable, $regexp, $flags = null)
+{
+    $options = array(
+        'options' => array('regexp' => $regexp)
+    );
+    if ($flags !== null) {
+        $options['flags'] = $flags;
+    }
+    return filter_input(
+        $type,
+        $variable,
+        FILTER_VALIDATE_REGEXP,
+        $options
+    );
+}
+
+function get_q_value($mime, $accept)
+{
+    $fulltype = preg_replace('@^([^/]+\/).+$@', '$1*', $mime);
+    $regex = implode(
+        '',
+        array(
+            '/(?<=^|,)\s*(\*\/\*|',
+            preg_quote($fulltype, '/'),
+            '|',
+            preg_quote($mime, '/'),
+            ')\s*(?:[^,]*?;\s*q\s*=\s*([0-9.]+))?\s*(?:,|$)/'
+        )
+    );
+    $out = preg_match_all($regex, $accept, $matches);
+    $types = array_combine($matches[1], $matches[2]);
+    if (array_key_exists($mime, $types)) {
+        $q = $types[$mime];
+    } elseif (array_key_exists($fulltype, $types)) {
+        $q = $types[$fulltype];
+    } elseif (array_key_exists('*/*', $types)) {
+        $q = $types['*/*'];
+    } else {
+        return 0;
+    }
+    return $q === '' ? 1 : floatval($q);
+}
+
+// URL Safe Base64 per https://tools.ietf.org/html/rfc7515#appendix-C
+
+function base64_url_encode($string)
+{
+    $string = base64_encode($string);
+    $string = rtrim($string, '=');
+    $string = strtr($string, '+/', '-_');
+    return $string;
+}
+
+function base64_url_decode($string)
+{
+    $string = strtr($string, '-_', '+/');
+    $padding = strlen($string) % 4;
+    if ($padding !== 0) {
+        $string .= str_repeat('=', 4 - $padding);
+    }
+    $string = base64_decode($string);
+    return $string;
+}
+
+if ((!defined('APP_URL') || APP_URL == '')
+    || (!defined('APP_KEY') || APP_KEY == '')
+    || (!defined('USER_HASH') || USER_HASH == '')
+    || (!defined('USER_URL') || USER_URL == '')
+) {
+    error_page(
+        'Configuration Error',
+        'Endpoint not configured correctly, visit <a href="setup.php">setup.php</a> for instructions on how to set it up.'
+    );
+}
+
+// First handle verification of codes.
+$code = filter_input_regexp(INPUT_POST, 'code', '@^[0-9a-f]+:[0-9a-f]{64}:@');
+
+if ($code !== null) {
+    $redirect_uri = filter_input(INPUT_POST, 'redirect_uri', FILTER_VALIDATE_URL);
+    $client_id = filter_input(INPUT_POST, 'client_id', FILTER_VALIDATE_URL);
+
+    // Exit if there are errors in the client supplied data.
+    if (!(is_string($code)
+        && is_string($redirect_uri)
+        && is_string($client_id)
+        && verify_signed_code(APP_KEY, USER_URL . $redirect_uri . $client_id, $code))
+    ) {
+        error_page('Verification Failed', 'Given Code Was Invalid');
+    }
+
+    $response = array('me' => USER_URL);
+
+    $code_parts = explode(':', $code, 3);
+
+    if ($code_parts[2] !== '') {
+        $response['scope'] = base64_url_decode($code_parts[2]);
+    }
+
+    // Accept header
+    $accept_header = '*/*';
+    if (isset($_SERVER['HTTP_ACCEPT']) && strlen($_SERVER['HTTP_ACCEPT']) > 0) {
+        $accept_header = $_SERVER['HTTP_ACCEPT'];
+    }
+
+    // Find the q value for application/json.
+    $json = get_q_value('application/json', $accept_header);
+
+    // Find the q value for application/x-www-form-urlencoded.
+    $form = get_q_value('application/x-www-form-urlencoded', $accept_header);
+
+    // Respond in the correct way.
+    if ($json === 0 && $form === 0) {
+        error_page(
+            'No Accepted Response Types',
+            'The client accepts neither JSON nor Form encoded responses.',
+            '406 Not Acceptable'
+        );
+    } elseif ($json >= $form) {
+        header('Content-Type: application/json');
+        exit(json_encode($response));
+    } else {
+        header('Content-Type: application/x-www-form-urlencoded');
+        exit(http_build_query($response));
+    }
+}
+
+// If this is not verification, collect all the client supplied data. Exit on errors.
+
+$me = filter_input(INPUT_GET, 'me', FILTER_VALIDATE_URL);
+$client_id = filter_input(INPUT_GET, 'client_id', FILTER_VALIDATE_URL);
+$redirect_uri = filter_input(INPUT_GET, 'redirect_uri', FILTER_VALIDATE_URL);
+$state = filter_input_regexp(INPUT_GET, 'state', '@^[\x20-\x7E]*$@');
+$response_type = filter_input_regexp(INPUT_GET, 'response_type', '@^(id|code)?$@');
+$scope = filter_input_regexp(INPUT_GET, 'scope', '@^([\x21\x23-\x5B\x5D-\x7E]+( [\x21\x23-\x5B\x5D-\x7E]+)*)?$@');
+
+if (!is_string($client_id)) { // client_id is either omitted or not a valid URL.
+    error_page(
+        'Faulty Request',
+        'There was an error with the request. The "client_id" field is invalid.'
+    );
+}
+if (!is_string($redirect_uri)) { // redirect_uri is either omitted or not a valid URL.
+    error_page(
+        'Faulty Request',
+        'There was an error with the request. The "redirect_uri" field is invalid.'
+    );
+}
+if ($state === false) { // state contains invalid characters.
+    error_page(
+        'Faulty Request',
+        'There was an error with the request. The "state" field contains invalid data.'
+    );
+}
+if ($response_type === false) { // response_type is given as something other than id or code.
+    error_page(
+        'Faulty Request',
+        'There was an error with the request. The "response_type" field must be "code".'
+    );
+}
+if ($scope === false) { // scope contains invalid characters.
+    error_page(
+        'Faulty Request',
+        'There was an error with the request. The "scope" field contains invalid data.'
+    );
+}
+if ($scope === '') { // scope is left empty.
+    // Treat empty parameters as if omitted.
+    $scope = null;
+}
+
+// If the user submitted a password, get ready to redirect back to the callback.
+
+$pass_input = filter_input(INPUT_POST, 'password', FILTER_UNSAFE_RAW);
+
+if ($pass_input !== null) {
+    $csrf_code = filter_input(INPUT_POST, '_csrf', FILTER_UNSAFE_RAW);
+
+    // Exit if the CSRF does not verify.
+    if ($csrf_code === null || !verify_signed_code(APP_KEY, $client_id . $redirect_uri . $state, $csrf_code)) {
+        error_page(
+            'Invalid CSF Code',
+            'Usually this means you took too long to log in. Please try again.'
+        );
+    }
+
+    // Exit if the password does not verify.
+    if (!verify_password($pass_input)) {
+        // Optional logging for failed logins.
+        //
+        // Enabling this on shared hosting may not be a good idea if syslog
+        // isn't private and accessible. Enable with caution.
+        if (function_exists('syslog') && defined('SYSLOG_FAILURE') && SYSLOG_FAILURE === 'I understand') {
+            syslog(LOG_CRIT, sprintf(
+                'IndieAuth: login failure from %s for %s',
+                $_SERVER['REMOTE_ADDR'],
+                $me
+            ));
+        }
+
+        error_page('Login Failed', 'Invalid password.');
+    }
+
+    $scope = filter_input_regexp(INPUT_POST, 'scopes', '@^[\x21\x23-\x5B\x5D-\x7E]+$@', FILTER_REQUIRE_ARRAY);
+
+    // Scopes are defined.
+    if ($scope !== null) {
+        // Exit if the scopes ended up with illegal characters or were not supplied as array.
+        if ($scope === false || in_array(false, $scope, true)) {
+            error_page('Invalid Scopes', 'The scopes provided contained illegal characters.');
+        }
+
+        // Turn scopes into a single string again.
+        $scope = implode(' ', $scope);
+    }
+
+    $code = create_signed_code(APP_KEY, USER_URL . $redirect_uri . $client_id, 5 * 60, $scope);
+
+    $final_redir = $redirect_uri;
+    if (strpos($redirect_uri, '?') === false) {
+        $final_redir .= '?';
+    } else {
+        $final_redir .= '&';
+    }
+    $parameters = array(
+        'code' => $code,
+        'me' => USER_URL
+    );
+    if ($state !== null) {
+        $parameters['state'] = $state;
+    }
+    $final_redir .= http_build_query($parameters);
+
+    // Optional logging for successful logins.
+    //
+    // Enabling this on shared hosting may not be a good idea if syslog
+    // isn't private and accessible. Enable with caution.
+    if (function_exists('syslog') && defined('SYSLOG_SUCCESS') && SYSLOG_SUCCESS === 'I understand') {
+        syslog(LOG_INFO, sprintf(
+            'IndieAuth: login from %s for %s',
+            $_SERVER['REMOTE_ADDR'],
+            $me
+        ));
+    }
+
+    // Redirect back.
+    header('Location: ' . $final_redir, true, 302);
+    exit();
+}
+
+// If neither password nor a code was submitted, we need to ask the user to authenticate.
+
+$csrf_code = create_signed_code(APP_KEY, $client_id . $redirect_uri . $state, 2 * 60);
+
+?><!doctype html>
+<html>
+    <head>
+        <title>Login</title>
+        <style>
+h1{text-align:center;margin-top:3%;}
+body {text-align:center;}
+fieldset, pre {width:400px; margin-left:auto; margin-right:auto;margin-bottom:50px; background-color:#FFC; min-height:1em;}
+fieldset {text-align:left;}
+
+.form-login{ 
+margin-left:auto;
+width:300px;
+margin-right:auto;
+text-align:center;
+margin-top:20px;
+border:solid 1px black;
+padding:20px;
+}
+.form-line{ margin:5px 0 0 0;}
+.submit{width:100%}
+.yellow{background-color:#FFC}
+
+        </style>
+    </head>
+    <body>
+        <form method="POST" action="">
+            <h1>Authenticate</h1>
+            <div>You are attempting to login with client <pre><?php echo htmlspecialchars($client_id); ?></pre></div>
+            <?php if (strlen($scope) > 0) : ?>
+            <div>It is requesting the following scopes, uncheck any you do not wish to grant:</div>
+            <fieldset>
+                <legend>Scopes</legend>
+                <?php foreach (explode(' ', $scope) as $n => $checkbox) : ?>
+                <div>
+                    <input id="scope_<?php echo $n; ?>" type="checkbox" name="scopes[]" value="<?php echo htmlspecialchars($checkbox); ?>" checked>
+                    <label for="scope_<?php echo $n; ?>"><?php echo $checkbox; ?></label>
+                </div>
+                <?php endforeach; ?>
+            </fieldset>
+            <?php endif; ?>
+            <div>After login you will be redirected to  <pre><?php echo htmlspecialchars($redirect_uri); ?></pre></div>
+            <div class="form-login">
+                <input type="hidden" name="_csrf" value="<?php echo $csrf_code; ?>" />
+                <p class="form-line">
+                    Logging in as:<br />
+                    <span class="yellow"><?php echo htmlspecialchars(USER_URL); ?></span>
+                </p>
+                <div class="form-line">
+                    <label for="password">Password:</label><br />
+                    <input type="password" name="password" id="password" />
+                </div>
+                <div class="form-line">
+                    <input class="submit" type="submit" name="submit" value="Submit" />
+                </div>
+            </div>
+        </form>
+    </body>
+</html>

+ 132 - 0
auth/setup.php

@@ -0,0 +1,132 @@
+<html>
+<head>
+<title>
+Setup Selfauth
+</title>
+<style>
+h1{text-align:center;margin-top:5%;}
+h2{text-align:center;}
+.instructions{text-align:center;}
+.message{margin-top:20px;text-align:center;font-size:1.2em;font-weight:bold;}
+pre {width:400px; margin-left:auto; margin-right:auto;margin-bottom:50px;}
+form{ 
+margin-left:auto;
+width:300px;
+margin-right:auto;
+text-align:center;
+margin-top:20px;
+border:solid 1px black;
+padding:20px;
+}
+.form-line{ margin-top:5px;}
+.submit{width:100%}
+</style>
+</head>
+<body>
+<h1>Setup Selfauth</h1>
+<div>
+<?php
+define('RANDOM_BYTE_COUNT', 32);
+
+$app_url = 'http' . (isset($_SERVER['HTTPS']) ? 's' : '') . '://' . $_SERVER['HTTP_HOST']
+  . str_replace('setup.php', '', $_SERVER['REQUEST_URI']);
+
+if (function_exists('random_bytes')) {
+    $bytes = random_bytes(RANDOM_BYTE_COUNT);
+    $strong_crypto = true;
+} elseif (function_exists('openssl_random_pseudo_bytes')) {
+    $bytes = openssl_random_pseudo_bytes(RANDOM_BYTE_COUNT, $strong_crypto);
+} else {
+    $bytes = '';
+    for ($i=0; $i < RANDOM_BYTE_COUNT; $i++) {
+        $bytes .= chr(mt_rand(0, 255));
+    }
+    $strong_crypto = false;
+}
+$app_key = bin2hex($bytes);
+
+$configfile= __DIR__ . '/config.php';
+
+$configured = true;
+
+if (file_exists($configfile)) {
+    include_once $configfile;
+
+    if ((!defined('APP_URL') || APP_URL == '')
+        || (!defined('APP_KEY') || APP_KEY == '')
+        || (!defined('USER_HASH') || USER_HASH == '')
+        || (!defined('USER_URL') || USER_URL == '')
+    ) {
+        $configured = false;
+    }
+} else {
+    $configured = false;
+}
+
+if ($configured) : ?>
+    <h2>System already configured</h2>
+    <div class="instructions">
+        If you with to reconfigure, please remove config.php and reload this page.
+    </div>
+
+<?php else : ?>
+    <?php if ($strong_crypto === false) : ?>
+        <h2>
+           WARNING: this version of PHP does not support functions 'random_bytes' or 'openssl_random_pseudo_bytes'. 
+           This means your application is not as secure as it could be.  You may continues, but it is strongly recommended you upgrade PHP.
+        </h2> 
+    <?php endif; ?>
+
+    <div class="instructions">In order to configure Selfauth, you need to fill in a few values, this page helps generate those options.</div>
+    <?php if (isset($_POST['username'])) : ?>
+    <div>
+    <?php
+    $app_url = 'http' . (isset($_SERVER['HTTPS']) ? 's' : '') . '://' . $_SERVER['HTTP_HOST'] . str_replace('setup.php', '', $_SERVER['REQUEST_URI']);
+
+    $user = $_POST['username'];
+
+    $user_tmp = trim(preg_replace('/^https?:\/\//', '', $_POST['username']), '/');
+    $pass = md5($user_tmp . $_POST['password'] . $app_key);
+
+    $config_file_contents = "<?php
+define('APP_URL', '$app_url');
+define('APP_KEY', '$app_key');
+define('USER_HASH', '$pass');
+define('USER_URL', '$user');";
+
+    $file_written = false;
+
+    if (is_writeable($configfile) && !$configured) {
+        $handle = fopen($configfile, 'w');
+
+        if ($handle) {
+            $result = fwrite($handle, $config_file_contents);
+            if ($result !== false) {
+                $file_written = true;
+            }
+        }
+
+        fclose($handle);
+    }else{
+    }
+
+
+    if ($file_written) {
+        echo '<div class="message">config.php was successfully written to disk</div>';
+    } else {
+        echo '<div class="message">Fill in the file config.php with the following content</div>';
+        echo '<pre>';
+        echo htmlentities($config_file_contents);
+        echo '</pre>';
+    }
+?>
+    </div>
+    <?php endif ?>
+    <form method="POST" action="">
+    <div class="form-line"><label>Login Url:</label> <input name='username' /></div>
+    <div class="form-line"><label>Password:</label> <input type='password' name='password' /></div>
+    <div class="form-line"><input class="submit" type="submit" name="submit" value="Generate Config"/></div>
+    </form>
+<?php endif; ?>
+</body>
+</html>

+ 18 - 0
blog.txt

@@ -0,0 +1,18 @@
+123123
+sadadfsdfasfas
+OMG it actually works!!!!
+OMG it actually works!!!!
+Really???
+Really???
+Wow it actually knows how to center the div
+or body... so to speak
+12/03/2022 10:46:26: so what next
+12/03/2022 10:46:36: Can it support new line?<br />
+<br />
+Let me see???
+12/03/2022 10:46:46: Not exactly tho lol
+12/03/2022 10:48:14: In the source code it does show it uses nl2br<br />
+so why does it creates a new line still
+12/03/2022 10:50:33: So this time let's see<br />
<br />
how's this thing doing
+12/03/2022 10:50:38: Awww flawless!!!!!
+12/03/2022 10:54:58: Awww flawless!!!!!

+ 45 - 0
chatgpt_test.php

@@ -0,0 +1,45 @@
+<?php
+
+// Set the filename for the blog data
+$filename = "blog.txt";
+
+// Check if the file exists and create it if it doesn't
+if (!file_exists($filename)) {
+  $file = fopen($filename, "w");
+  fclose($file);
+}
+
+// Read the current blog data from the file
+$data = file_get_contents($filename);
+
+// If a new post was submitted, append it to the current blog data
+if (isset($_POST['new_post'])) {
+  // Format the current time and date
+  $timestamp = date("m/d/Y H:i:s");
+
+  // Convert newline characters to <br> elements and remove the newlines
+  $post = str_replace("\n", "", nl2br($_POST['new_post']));
+
+  // Append the timestamp and the post content to the data
+  $data .= "$timestamp: $post\n";
+  file_put_contents($filename, $data);
+}
+
+// Set the CSS styles inline on the body element
+echo "<body style='margin: 0 auto; width: 80%; text-align: center;'>";
+
+// Display the blog posts in a list
+echo "<ul>";
+foreach (explode("\n", $data) as $post) {
+  echo "<li>$post</li>";
+}
+echo "</ul>";
+
+// Display a form for creating new posts
+echo '<form action="" method="post">';
+echo '<textarea name="new_post" rows="5" cols="50"></textarea><br>';
+echo '<input type="submit" value="Submit">';
+echo '</form>';
+
+// Close the body element
+echo "</body>";

+ 102 - 0
chatgpt_test_2.php

@@ -0,0 +1,102 @@
+<?php
+// Set the file path where the blog posts will be stored
+$file_path = 'posts.txt';
+
+// Set the page title
+$page_title = 'My PHP Blog';
+
+// Start the user session
+session_start();
+
+// Check if the user is logged in
+if (!isset($_SESSION['username'])) {
+  // If not, check if the login form was submitted
+  if (isset($_POST['login'])) {
+    // Get the username and password from the form
+    $username = trim($_POST['username']);
+    $password = trim($_POST['password']);
+
+    // Validate the input
+    if ($username == 'admin' && $password == 'password') {
+      // If the username and password are correct, set the session variable and redirect to the blog page
+      $_SESSION['username'] = $username;
+      header('Location: blog.php');
+      exit;
+    } else {
+      // If the username and password are incorrect, display an error message
+      $error = 'Incorrect username or password.';
+    }
+  }
+
+  // Display the login page
+  echo "<html><head><title>$page_title - Login</title></head><body>";
+  echo "<h1>$page_title</h1>";
+  if (isset($error)) { echo "<p>$error</p>"; }
+  echo '<form method="post">';
+  echo '<label>Username:</label><br>';
+  echo '<input type="text" name="username"><br>';
+  echo '<label>Password:</label><br>';
+  echo '<input type="password" name="password"><br>';
+  echo '<input type="submit" name="login" value="Login">';
+  echo '</form>';
+  echo '</body></html>';
+  exit;
+}
+
+// Display the blog page
+echo "<html><head><title>$page_title</title></head><body>";
+echo "<h1>$page_title</h1>";
+
+// Check if the form was submitted
+if (isset($_POST['submit'])) {
+  // Get the post title and content from the form
+  $title = trim($_POST['title']);
+  $content = trim($_POST['content']);
+
+  // Validate the input
+  if (strlen($title) == 0) {
+    $error = 'Please enter a post title.';
+  } else if (strlen($content) == 0) {
+    $error = 'Please enter post content.';
+  } else {
+    // Format the post data as a string to be saved to the file
+    $post = "=== $title ===\n$content\n";
+
+    // Open the file in append mode and write the post data
+    $fh = fopen($file_path, 'a');
+    fwrite($fh, $post);
+    fclose($fh);
+  }
+}
+
+// Open the file and read the posts
+$fh = fopen($file_path, 'r');
+$posts = '';
+while (!feof($fh)) {
+  // Read each line of the file and append it to the posts string
+  $line = fgets($fh);
+  $posts .= $line;
+}
+fclose($fh);
+
+// Display the blog posts on the page
+echo $posts;
+
+// Display the new post form
+echo '<h2>New Post</h2>';
+if (isset($error)) { echo "<p>$error</p>"; }
+echo '<form method="post">';
+echo '<label>Title:</label><br>';
+echo '<input type="text" name="title" value="';
+if (isset($title)) { echo htmlspecialchars($title); }
+echo '"><br>';
+echo '<label>Content:</label><br>';
+echo '<textarea name="content">';
+if (isset($content)) { echo htmlspecialchars($content); }
+echo '</textarea><br>';
+echo '<input type="submit" name="submit" value="Submit">';
+echo '</form>';
+
+// Display the HTML footer
+echo '</body></html>';
+?>

+ 2 - 0
custom_translations.md

@@ -0,0 +1,2 @@
+- 超精爆射 | Hyper Cum
+- 射精训练基地 | Cum training arena

+ 14 - 0
demo/.htaccess

@@ -0,0 +1,14 @@
+RewriteEngine on
+
+RewriteCond %{HTTPS} !=on
+RewriteCond %{HTTP_HOST} !=localhost
+RewriteCond %{REQUEST_URI}  !^.*(jpg|png|gif)$
+RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
+
+RewriteCond %{HTTP_HOST} !^www\.
+RewriteCond %{HTTP_HOST} !=localhost
+RewriteRule ^(.*)$ https://www.%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
+
+<Files ~ "\.md$">
+deny from all
+</Files>

+ 0 - 0
demo/.la_lock


+ 2032 - 0
demo/Parsedown.php

@@ -0,0 +1,2032 @@
+<?php
+
+#
+# Parsedown Modified version By ChengduLittleA-YimingWu
+# http://www.wellobserve.com
+# xp8110@outlook.com
+#
+# Original Parsedown
+# http://parsedown.org
+# (c) Emanuil Rusev
+# http://erusev.com
+#
+
+class Parsedown
+{
+    # ~
+
+    const version = '1.8.0-beta-5';
+
+    # ~
+    
+    protected $PATH;
+    
+    //==========================================================================
+    
+    function SetInterlinkPath($Path){
+        $this->PATH = $Path;
+        if(!is_dir($this->PATH)){
+            $OP = explode("/",$this->PATH);
+            $last=$OP[count($OP)-1];
+            if(strlen($last)>=3 && substr($last,strlen($last)-3)=='.md')
+                unset($OP[count($OP)-1]);
+            $this->PATH = implode('/',$OP);
+        }
+    }
+    
+    function GetInterlinkPath($Path){
+        $TP = str_replace("\\",'/',$Path);
+        $TP = str_replace("//",'/',$TP);
+        $P = explode("/",$TP);
+        $OP = explode("/",$this->PATH);
+        $i=0;
+        $OOP = [];
+        foreach($OP as $Part){ if ($Part == '') continue; $OOP[$i]=$Part; $i++; } 
+        foreach($P as $Part){
+            if(isset($Part) && $Part == '..'){
+                unset($OOP[count($OOP)-1]);
+            }else{
+                $OOP[count($OOP)] = $Part;
+            }
+        }
+        $Result = implode('/',$OOP);
+        return $Result;
+    }
+    
+    function InterlinkPath(){
+        return $this->PATH;
+    }
+    
+    //==============================================================================
+
+    function text($text)
+    {
+        $Elements = $this->textElements($text);
+
+        # convert to markup
+        $markup = $this->elements($Elements);
+
+        # trim line breaks
+        $markup = trim($markup, "\n");
+
+        return $markup;
+    }
+
+    protected function textElements($text)
+    {
+        # make sure no definitions are set
+        $this->DefinitionData = array();
+
+        # standardize line breaks
+        $text = str_replace(array("\r\n", "\r"), "\n", $text);
+
+        # remove surrounding line breaks
+        $text = trim($text, "\n");
+
+        # split text into lines
+        $lines = explode("\n", $text);
+
+        # iterate through lines to identify blocks
+        return $this->linesElements($lines);
+    }
+
+    #
+    # Setters
+    #
+
+    function setBreaksEnabled($breaksEnabled)
+    {
+        $this->breaksEnabled = $breaksEnabled;
+
+        return $this;
+    }
+
+    protected $breaksEnabled;
+
+    function setMarkupEscaped($markupEscaped)
+    {
+        $this->markupEscaped = $markupEscaped;
+
+        return $this;
+    }
+
+    protected $markupEscaped;
+
+    function setUrlsLinked($urlsLinked)
+    {
+        $this->urlsLinked = $urlsLinked;
+
+        return $this;
+    }
+
+    protected $urlsLinked = true;
+
+    function setSafeMode($safeMode)
+    {
+        $this->safeMode = (bool) $safeMode;
+
+        return $this;
+    }
+
+    protected $safeMode;
+
+    function setStrictMode($strictMode)
+    {
+        $this->strictMode = (bool) $strictMode;
+
+        return $this;
+    }
+
+    protected $strictMode;
+
+    protected $safeLinksWhitelist = array(
+        'http://',
+        'https://',
+        'ftp://',
+        'ftps://',
+        'mailto:',
+        'tel:',
+        'data:image/png;base64,',
+        'data:image/gif;base64,',
+        'data:image/jpeg;base64,',
+        'irc:',
+        'ircs:',
+        'git:',
+        'ssh:',
+        'news:',
+        'steam:',
+    );
+
+    #
+    # Lines
+    #
+
+    protected $BlockTypes = array(
+        '#' => array('Header'),
+        '*' => array('Rule', 'List'),
+        '+' => array('List'),
+        '-' => array('SetextHeader', 'Table', 'Rule', 'List'),
+        '0' => array('List'),
+        '1' => array('List'),
+        '2' => array('List'),
+        '3' => array('List'),
+        '4' => array('List'),
+        '5' => array('List'),
+        '6' => array('List'),
+        '7' => array('List'),
+        '8' => array('List'),
+        '9' => array('List'),
+        ':' => array('Table'),
+        '<' => array('Comment', 'Markup'),
+        '=' => array('SetextHeader'),
+        '>' => array('Quote'),
+        '[' => array('Reference'),
+        '_' => array('Rule'),
+        '`' => array('FencedCode'),
+        '|' => array('Table'),
+        '~' => array('FencedCode'),
+    );
+
+    # ~
+
+    protected $unmarkedBlockTypes = array(
+        'Code',
+    );
+
+    #
+    # Blocks
+    #
+
+    protected function lines(array $lines)
+    {
+        return $this->elements($this->linesElements($lines));
+    }
+
+    protected function linesElements(array $lines)
+    {
+        $Elements = array();
+        $CurrentBlock = null;
+
+        foreach ($lines as $line)
+        {
+            if (chop($line) === '')
+            {
+                if (isset($CurrentBlock))
+                {
+                    $CurrentBlock['interrupted'] = (isset($CurrentBlock['interrupted'])
+                        ? $CurrentBlock['interrupted'] + 1 : 1
+                    );
+                }
+
+                continue;
+            }
+
+            while (($beforeTab = strstr($line, "\t", true)) !== false)
+            {
+                $shortage = 4 - mb_strlen($beforeTab, 'utf-8') % 4;
+
+                $line = $beforeTab
+                    . str_repeat(' ', $shortage)
+                    . substr($line, strlen($beforeTab) + 1)
+                ;
+            }
+
+            $indent = strspn($line, ' ');
+
+            $text = $indent > 0 ? substr($line, $indent) : $line;
+
+            # ~
+
+            $Line = array('body' => $line, 'indent' => $indent, 'text' => $text);
+
+            # ~
+
+            if (isset($CurrentBlock['continuable']))
+            {
+                $methodName = 'block' . $CurrentBlock['type'] . 'Continue';
+                $Block = $this->$methodName($Line, $CurrentBlock);
+
+                if (isset($Block))
+                {
+                    $CurrentBlock = $Block;
+
+                    continue;
+                }
+                else
+                {
+                    if ($this->isBlockCompletable($CurrentBlock['type']))
+                    {
+                        $methodName = 'block' . $CurrentBlock['type'] . 'Complete';
+                        $CurrentBlock = $this->$methodName($CurrentBlock);
+                    }
+                }
+            }
+
+            # ~protected $breaksEnabled;
+
+            $marker = $text[0];
+
+            # ~
+
+            $blockTypes = $this->unmarkedBlockTypes;
+
+            if (isset($this->BlockTypes[$marker]))
+            {
+                foreach ($this->BlockTypes[$marker] as $blockType)
+                {
+                    $blockTypes []= $blockType;
+                }
+            }
+
+            #
+            # ~
+
+            foreach ($blockTypes as $blockType)
+            {
+                $Block = $this->{"block$blockType"}($Line, $CurrentBlock);
+
+                if (isset($Block))
+                {
+                    $Block['type'] = $blockType;
+
+                    if ( ! isset($Block['identified']))
+                    {
+                        if (isset($CurrentBlock))
+                        {
+                            $Elements[] = $this->extractElement($CurrentBlock);
+                        }
+
+                        $Block['identified'] = true;
+                    }
+
+                    if ($this->isBlockContinuable($blockType))
+                    {
+                        $Block['continuable'] = true;
+                    }
+
+                    $CurrentBlock = $Block;
+
+                    continue 2;
+                }
+            }
+
+            # ~
+
+            if (isset($CurrentBlock) and $CurrentBlock['type'] === 'Paragraph')
+            {
+                $Block = $this->paragraphContinue($Line, $CurrentBlock);
+            }
+
+            if (isset($Block))
+            {
+                $CurrentBlock = $Block;
+            }
+            else
+            {
+                if (isset($CurrentBlock))
+                {
+                    $Elements[] = $this->extractElement($CurrentBlock);
+                }
+
+                $CurrentBlock = $this->paragraph($Line);
+
+                $CurrentBlock['identified'] = true;
+            }
+        }
+
+        # ~
+
+        if (isset($CurrentBlock['continuable']) and $this->isBlockCompletable($CurrentBlock['type']))
+        {
+            $methodName = 'block' . $CurrentBlock['type'] . 'Complete';
+            $CurrentBlock = $this->$methodName($CurrentBlock);
+        }
+
+        # ~
+
+        if (isset($CurrentBlock))
+        {
+            $Elements[] = $this->extractElement($CurrentBlock);
+        }
+
+        # ~
+
+        return $Elements;
+    }
+
+    protected function extractElement(array $Component)
+    {
+        if ( ! isset($Component['element']))
+        {
+            if (isset($Component['markup']))
+            {
+                $Component['element'] = array('rawHtml' => $Component['markup']);
+            }
+            elseif (isset($Component['hidden']))
+            {
+                $Component['element'] = array();
+            }
+        }
+
+        return $Component['element'];
+    }
+
+    protected function isBlockContinuable($Type)
+    {
+        return method_exists($this, 'block' . $Type . 'Continue');
+    }
+
+    protected function isBlockCompletable($Type)
+    {
+        return method_exists($this, 'block' . $Type . 'Complete');
+    }
+
+    #
+    # Code
+
+    protected function blockCode($Line, $Block = null)
+    {
+        if (isset($Block) and $Block['type'] === 'Paragraph' and ! isset($Block['interrupted']))
+        {
+            return;
+        }
+
+        if ($Line['indent'] >= 4)
+        {
+            $text = substr($Line['body'], 4);
+
+            $Block = array(
+                'element' => array(
+                    'name' => 'pre',
+                    'element' => array(
+                        'name' => 'code',
+                        'text' => $text,
+                    ),
+                ),
+            );
+
+            return $Block;
+        }
+    }
+
+    protected function blockCodeContinue($Line, $Block)
+    {
+        if ($Line['indent'] >= 4)
+        {
+            if (isset($Block['interrupted']))
+            {
+                $Block['element']['element']['text'] .= str_repeat("\n", $Block['interrupted']);
+
+                unset($Block['interrupted']);
+            }
+
+            $Block['element']['element']['text'] .= "\n";
+
+            $text = substr($Line['body'], 4);
+
+            $Block['element']['element']['text'] .= $text;
+
+            return $Block;
+        }
+    }
+
+    protected function blockCodeComplete($Block)
+    {
+        return $Block;
+    }
+
+    #
+    # Comment
+
+    protected function blockComment($Line)
+    {
+        if ($this->markupEscaped or $this->safeMode)
+        {
+            return;
+        }
+
+        if (strpos($Line['text'], '<!--') === 0)
+        {
+            $Block = array(
+                'element' => array(
+                    'rawHtml' => $Line['body'],
+                    'autobreak' => true,
+                ),
+            );
+
+            if (strpos($Line['text'], '-->') !== false)
+            {
+                $Block['closed'] = true;
+            }
+
+            return $Block;
+        }
+    }
+
+    protected function blockCommentContinue($Line, array $Block)
+    {
+        if (isset($Block['closed']))
+        {
+            return;
+        }
+
+        $Block['element']['rawHtml'] .= "\n" . $Line['body'];
+
+        if (strpos($Line['text'], '-->') !== false)
+        {
+            $Block['closed'] = true;
+        }
+
+        return $Block;
+    }
+
+    #
+    # Fenced Code
+
+    protected function blockFencedCode($Line)
+    {
+        $marker = $Line['text'][0];
+
+        $openerLength = strspn($Line['text'], $marker);
+
+        if ($openerLength < 3)
+        {
+            return;
+        }
+
+        $infostring = trim(substr($Line['text'], $openerLength), "\t ");
+
+        if (strpos($infostring, '`') !== false)
+        {
+            return;
+        }
+
+        $Element = array(
+            'name' => 'code',
+            'text' => '',
+        );
+
+        if ($infostring !== '')
+        {
+            $Element['attributes'] = array('class' => "language-$infostring");
+        }
+
+        $Block = array(
+            'char' => $marker,
+            'openerLength' => $openerLength,
+            'element' => array(
+                'name' => 'pre',
+                'element' => $Element,
+            ),
+        );
+
+        return $Block;
+    }
+
+    protected function blockFencedCodeContinue($Line, $Block)
+    {
+        if (isset($Block['complete']))
+        {
+            return;
+        }
+
+        if (isset($Block['interrupted']))
+        {
+            $Block['element']['element']['text'] .= str_repeat("\n", $Block['interrupted']);
+
+            unset($Block['interrupted']);
+        }
+
+        if (($len = strspn($Line['text'], $Block['char'])) >= $Block['openerLength']
+            and chop(substr($Line['text'], $len), ' ') === ''
+        ) {
+            $Block['element']['element']['text'] = substr($Block['element']['element']['text'], 1);
+
+            $Block['complete'] = true;
+
+            return $Block;
+        }
+
+        $Block['element']['element']['text'] .= "\n" . $Line['body'];
+
+        return $Block;
+    }
+
+    protected function blockFencedCodeComplete($Block)
+    {
+        return $Block;
+    }
+
+    #
+    # Header
+
+    protected function blockHeader($Line)
+    {
+        $level = strspn($Line['text'], '#');
+
+        if ($level > 6)
+        {
+            return;
+        }
+
+        $text = trim($Line['text'], '#');
+
+        if ($this->strictMode and isset($text[0]) and $text[0] !== ' ')
+        {
+            return;
+        }
+
+        $text = trim($text, ' ');
+
+        $Block = array(
+            'element' => array(
+                'name' => 'h' . $level,
+                'handler' => array(
+                    'function' => 'lineElements',
+                    'argument' => $text,
+                    'destination' => 'elements',
+                )
+            ),
+        );
+
+        return $Block;
+    }
+
+    #
+    # List
+
+    protected function blockList($Line, array $CurrentBlock = null)
+    {
+        list($name, $pattern) = $Line['text'][0] <= '-' ? array('ul', '[*+-]') : array('ol', '[0-9]{1,9}+[.\)]');
+
+        if (preg_match('/^('.$pattern.'([ ]++|$))(.*+)/', $Line['text'], $matches))
+        {
+            $contentIndent = strlen($matches[2]);
+
+            if ($contentIndent >= 5)
+            {
+                $contentIndent -= 1;
+                $matches[1] = substr($matches[1], 0, -$contentIndent);
+                $matches[3] = str_repeat(' ', $contentIndent) . $matches[3];
+            }
+            elseif ($contentIndent === 0)
+            {
+                $matches[1] .= ' ';
+            }
+
+            $markerWithoutWhitespace = strstr($matches[1], ' ', true);
+
+            $Block = array(
+                'indent' => $Line['indent'],
+                'pattern' => $pattern,
+                'data' => array(
+                    'type' => $name,
+                    'marker' => $matches[1],
+                    'markerType' => ($name === 'ul' ? $markerWithoutWhitespace : substr($markerWithoutWhitespace, -1)),
+                ),
+                'element' => array(
+                    'name' => $name,
+                    'elements' => array(),
+                ),
+            );
+            $Block['data']['markerTypeRegex'] = preg_quote($Block['data']['markerType'], '/');
+
+            if ($name === 'ol')
+            {
+                $listStart = ltrim(strstr($matches[1], $Block['data']['markerType'], true), '0') ?: '0';
+
+                if ($listStart !== '1')
+                {
+                    if (
+                        isset($CurrentBlock)
+                        and $CurrentBlock['type'] === 'Paragraph'
+                        and ! isset($CurrentBlock['interrupted'])
+                    ) {
+                        return;
+                    }
+
+                    $Block['element']['attributes'] = array('start' => $listStart);
+                }
+            }
+
+            $Block['li'] = array(
+                'name' => 'li',
+                'handler' => array(
+                    'function' => 'li',
+                    'argument' => !empty($matches[3]) ? array($matches[3]) : array(),
+                    'destination' => 'elements'
+                )
+            );
+
+            $Block['element']['elements'] []= & $Block['li'];
+
+            return $Block;
+        }
+    }
+
+    protected function blockListContinue($Line, array $Block)
+    {
+        if (isset($Block['interrupted']) and empty($Block['li']['handler']['argument']))
+        {
+            return null;
+        }
+
+        $requiredIndent = ($Block['indent'] + strlen($Block['data']['marker']));
+
+        if ($Line['indent'] < $requiredIndent
+            and (
+                (
+                    $Block['data']['type'] === 'ol'
+                    and preg_match('/^[0-9]++'.$Block['data']['markerTypeRegex'].'(?:[ ]++(.*)|$)/', $Line['text'], $matches)
+                ) or (
+                    $Block['data']['type'] === 'ul'
+                    and preg_match('/^'.$Block['data']['markerTypeRegex'].'(?:[ ]++(.*)|$)/', $Line['text'], $matches)
+                )
+            )
+        ) {
+            if (isset($Block['interrupted']))
+            {
+                $Block['li']['handler']['argument'] []= '';
+
+                $Block['loose'] = true;
+
+                unset($Block['interrupted']);
+            }
+
+            unset($Block['li']);
+
+            $text = isset($matches[1]) ? $matches[1] : '';
+
+            $Block['indent'] = $Line['indent'];
+
+            $Block['li'] = array(
+                'name' => 'li',
+                'handler' => array(
+                    'function' => 'li',
+                    'argument' => array($text),
+                    'destination' => 'elements'
+                )
+            );
+
+            $Block['element']['elements'] []= & $Block['li'];
+
+            return $Block;
+        }
+        elseif ($Line['indent'] < $requiredIndent and $this->blockList($Line))
+        {
+            return null;
+        }
+
+        if ($Line['text'][0] === '[' and $this->blockReference($Line))
+        {
+            return $Block;
+        }
+
+        if ($Line['indent'] >= $requiredIndent)
+        {
+            if (isset($Block['interrupted']))
+            {
+                $Block['li']['handler']['argument'] []= '';
+
+                $Block['loose'] = true;
+
+                unset($Block['interrupted']);
+            }
+
+            $text = substr($Line['body'], $requiredIndent);
+
+            $Block['li']['handler']['argument'] []= $text;
+
+            return $Block;
+        }
+
+        if ( ! isset($Block['interrupted']))
+        {
+            $text = preg_replace('/^[ ]{0,'.$requiredIndent.'}+/', '', $Line['body']);
+
+            $Block['li']['handler']['argument'] []= $text;
+
+            return $Block;
+        }
+    }
+
+    protected function blockListComplete(array $Block)
+    {
+        if (isset($Block['loose']))
+        {
+            foreach ($Block['element']['elements'] as &$li)
+            {
+                if (end($li['handler']['argument']) !== '')
+                {
+                    $li['handler']['argument'] []= '';
+                }
+            }
+        }
+
+        return $Block;
+    }
+
+    #
+    # Quote
+
+    protected function blockQuote($Line)
+    {
+        if (preg_match('/^>[ ]?+(.*+)/', $Line['text'], $matches))
+        {
+            $Block = array(
+                'element' => array(
+                    'name' => 'blockquote',
+                    'handler' => array(
+                        'function' => 'linesElements',
+                        'argument' => (array) $matches[1],
+                        'destination' => 'elements',
+                    )
+                ),
+            );
+
+            return $Block;
+        }
+    }
+
+    protected function blockQuoteContinue($Line, array $Block)
+    {
+        if (isset($Block['interrupted']))
+        {
+            return;
+        }
+
+        if ($Line['text'][0] === '>' and preg_match('/^>[ ]?+(.*+)/', $Line['text'], $matches))
+        {
+            $Block['element']['handler']['argument'] []= $matches[1];
+
+            return $Block;
+        }
+
+        if ( ! isset($Block['interrupted']))
+        {
+            $Block['element']['handler']['argument'] []= $Line['text'];
+
+            return $Block;
+        }
+    }
+
+    #
+    # Rule
+
+    protected function blockRule($Line)
+    {
+        $marker = $Line['text'][0];
+
+        if (substr_count($Line['text'], $marker) >= 3 and chop($Line['text'], " $marker") === '')
+        {
+            $Block = array(
+                'element' => array(
+                    'name' => 'hr',
+                ),
+            );
+
+            return $Block;
+        }
+    }
+
+    #
+    # Setext
+
+    protected function blockSetextHeader($Line, array $Block = null)
+    {
+        if ( ! isset($Block) or $Block['type'] !== 'Paragraph' or isset($Block['interrupted']))
+        {
+            return;
+        }
+
+        if ($Line['indent'] < 4 and chop(chop($Line['text'], ' '), $Line['text'][0]) === '')
+        {
+            $Block['element']['name'] = $Line['text'][0] === '=' ? 'h1' : 'h2';
+
+            return $Block;
+        }
+    }
+
+    #
+    # Markup
+
+    protected function blockMarkup($Line)
+    {
+        if ($this->markupEscaped or $this->safeMode)
+        {
+            return;
+        }
+
+        if (preg_match('/^<[\/]?+(\w*)(?:[ ]*+'.$this->regexHtmlAttribute.')*+[ ]*+(\/)?>/', $Line['text'], $matches))
+        {
+            $element = strtolower($matches[1]);
+
+            if (in_array($element, $this->textLevelElements))
+            {
+                return;
+            }
+
+            $Block = array(
+                'name' => $matches[1],
+                'element' => array(
+                    'rawHtml' => $Line['text'],
+                    'autobreak' => true,
+                ),
+            );
+
+            return $Block;
+        }
+    }
+
+    protected function blockMarkupContinue($Line, array $Block)
+    {
+        if (isset($Block['closed']) or isset($Block['interrupted']))
+        {
+            return;
+        }
+
+        $Block['element']['rawHtml'] .= "\n" . $Line['body'];
+
+        return $Block;
+    }
+
+    #
+    # Reference
+
+    protected function blockReference($Line)
+    {
+        if (strpos($Line['text'], ']') !== false
+            and preg_match('/^\[(.+?)\]:[ ]*+<?(\S+?)>?(?:[ ]+["\'(](.+)["\')])?[ ]*+$/', $Line['text'], $matches)
+        ) {
+            $id = strtolower($matches[1]);
+
+            $Data = array(
+                'url' => $matches[2],
+                'title' => isset($matches[3]) ? $matches[3] : null,
+            );
+
+            $this->DefinitionData['Reference'][$id] = $Data;
+
+            $Block = array(
+                'element' => array(),
+            );
+
+            return $Block;
+        }
+    }
+
+    #
+    # Table
+
+    protected function blockTable($Line, array $Block = null)
+    {
+        if ( ! isset($Block) or $Block['type'] !== 'Paragraph' or isset($Block['interrupted']))
+        {
+            return;
+        }
+
+        if (
+            strpos($Block['element']['handler']['argument'], '|') === false
+            and strpos($Line['text'], '|') === false
+            and strpos($Line['text'], ':') === false
+            or strpos($Block['element']['handler']['argument'], "\n") !== false
+        ) {
+            return;
+        }
+
+        if (chop($Line['text'], ' -:|') !== '')
+        {
+            return;
+        }
+
+        $alignments = array();
+
+        $divider = $Line['text'];
+
+        $divider = trim($divider);
+        $divider = trim($divider, '|');
+
+        $dividerCells = explode('|', $divider);
+
+        foreach ($dividerCells as $dividerCell)
+        {
+            $dividerCell = trim($dividerCell);
+
+            if ($dividerCell === '')
+            {
+                return;
+            }
+
+            $alignment = null;
+
+            if ($dividerCell[0] === ':')
+            {
+                $alignment = 'left';
+            }
+
+            if (substr($dividerCell, - 1) === ':')
+            {
+                $alignment = $alignment === 'left' ? 'center' : 'right';
+            }
+
+            $alignments []= $alignment;
+        }
+
+        # ~
+
+        $HeaderElements = array();
+
+        $header = $Block['element']['handler']['argument'];
+
+        $header = trim($header);
+        $header = trim($header, '|');
+
+        $headerCells = explode('|', $header);
+
+        if (count($headerCells) !== count($alignments))
+        {
+            return;
+        }
+
+        foreach ($headerCells as $index => $headerCell)
+        {
+            $headerCell = trim($headerCell);
+
+            $HeaderElement = array(
+                'name' => 'th',
+                'handler' => array(
+                    'function' => 'lineElements',
+                    'argument' => $headerCell,
+                    'destination' => 'elements',
+                )
+            );
+
+            if (isset($alignments[$index]))
+            {
+                $alignment = $alignments[$index];
+
+                $HeaderElement['attributes'] = array(
+                    'style' => "text-align: $alignment;",
+                );
+            }
+
+            $HeaderElements []= $HeaderElement;
+        }
+
+        # ~
+
+        $Block = array(
+            'alignments' => $alignments,
+            'identified' => true,
+            'element' => array(
+                'name' => 'table',
+                'elements' => array(),
+            ),
+        );
+
+        $Block['element']['elements'] []= array(
+            'name' => 'thead',
+        );
+
+        $Block['element']['elements'] []= array(
+            'name' => 'tbody',
+            'elements' => array(),
+        );
+
+        $Block['element']['elements'][0]['elements'] []= array(
+            'name' => 'tr',
+            'elements' => $HeaderElements,
+        );
+
+        return $Block;
+    }
+
+    protected function blockTableContinue($Line, array $Block)
+    {
+        if (isset($Block['interrupted']))
+        {
+            return;
+        }
+
+        if (count($Block['alignments']) === 1 or $Line['text'][0] === '|' or strpos($Line['text'], '|'))
+        {
+            $Elements = array();
+
+            $row = $Line['text'];
+
+            $row = trim($row);
+            $row = trim($row, '|');
+
+            preg_match_all('/(?:(\\\\[|])|[^|`]|`[^`]++`|`)++/', $row, $matches);
+
+            $cells = array_slice($matches[0], 0, count($Block['alignments']));
+
+            foreach ($cells as $index => $cell)
+            {
+                $cell = trim($cell);
+
+                $Element = array(
+                    'name' => 'td',
+                    'handler' => array(
+                        'function' => 'lineElements',
+                        'argument' => $cell,
+                        'destination' => 'elements',
+                    )
+                );
+
+                if (isset($Block['alignments'][$index]))
+                {
+                    $Element['attributes'] = array(
+                        'style' => 'text-align: ' . $Block['alignments'][$index] . ';',
+                    );
+                }
+
+                $Elements []= $Element;
+            }
+
+            $Element = array(
+                'name' => 'tr',
+                'elements' => $Elements,
+            );
+
+            $Block['element']['elements'][1]['elements'] []= $Element;
+
+            return $Block;
+        }
+    }
+
+    #
+    # ~
+    #
+
+    protected function paragraph($Line)
+    {
+        return array(
+            'type' => 'Paragraph',
+            'element' => array(
+                'name' => 'p',
+                'handler' => array(
+                    'function' => 'lineElements',
+                    'argument' => $Line['text'],
+                    'destination' => 'elements',
+                ),
+            ),
+        );
+    }
+
+    protected function paragraphContinue($Line, array $Block)
+    {
+        if (isset($Block['interrupted']))
+        {
+            return;
+        }
+
+        $Block['element']['handler']['argument'] .= "\n".$Line['text'];
+
+        return $Block;
+    }
+
+    #
+    # Inline Elements
+    #
+
+    protected $InlineTypes = array(
+        '!' => array('Image'),
+        '&' => array('SpecialCharacter'),
+        '*' => array('Emphasis'),
+        ':' => array('Url'),
+        '<' => array('UrlTag', 'EmailTag', 'Markup'),
+        '[' => array('Link'),
+        '_' => array('Emphasis'),
+        '`' => array('Code'),
+        '~' => array('Strikethrough'),
+        '\\' => array('EscapeSequence'),
+    );
+
+    # ~
+
+    protected $inlineMarkerList = '!*_&[:<`~\\';
+
+    #
+    # ~
+    #
+
+    public function line($text, $nonNestables = array())
+    {
+        return $this->elements($this->lineElements($text, $nonNestables));
+    }
+
+    protected function lineElements($text, $nonNestables = array())
+    {
+        # standardize line breaks
+        $text = str_replace(array("\r\n", "\r"), "\n", $text);
+
+        $Elements = array();
+
+        $nonNestables = (empty($nonNestables)
+            ? array()
+            : array_combine($nonNestables, $nonNestables)
+        );
+
+        # $excerpt is based on the first occurrence of a marker
+
+        while ($excerpt = strpbrk($text, $this->inlineMarkerList))
+        {
+            $marker = $excerpt[0];
+
+            $markerPosition = strlen($text) - strlen($excerpt);
+
+            $Excerpt = array('text' => $excerpt, 'context' => $text);
+
+            foreach ($this->InlineTypes[$marker] as $inlineType)
+            {
+                # check to see if the current inline type is nestable in the current context
+
+                if (isset($nonNestables[$inlineType]))
+                {
+                    continue;
+                }
+
+                $Inline = $this->{"inline$inlineType"}($Excerpt);
+
+                if ( ! isset($Inline))
+                {
+                    continue;
+                }
+
+                # makes sure that the inline belongs to "our" marker
+
+                if (isset($Inline['position']) and $Inline['position'] > $markerPosition)
+                {
+                    continue;
+                }
+
+                # sets a default inline position
+
+                if ( ! isset($Inline['position']))
+                {
+                    $Inline['position'] = $markerPosition;
+                }
+
+                # cause the new element to 'inherit' our non nestables
+
+
+                $Inline['element']['nonNestables'] = isset($Inline['element']['nonNestables'])
+                    ? array_merge($Inline['element']['nonNestables'], $nonNestables)
+                    : $nonNestables
+                ;
+
+                # the text that comes before the inline
+                $unmarkedText = substr($text, 0, $Inline['position']);
+
+                # compile the unmarked text
+                $InlineText = $this->inlineText($unmarkedText);
+                $Elements[] = $InlineText['element'];
+
+                # compile the inline
+                $Elements[] = $this->extractElement($Inline);
+
+                # remove the examined text
+                $text = substr($text, $Inline['position'] + $Inline['extent']);
+
+                continue 2;
+            }
+
+            # the marker does not belong to an inline
+
+            $unmarkedText = substr($text, 0, $markerPosition + 1);
+
+            $InlineText = $this->inlineText($unmarkedText);
+            $Elements[] = $InlineText['element'];
+
+            $text = substr($text, $markerPosition + 1);
+        }
+
+        $InlineText = $this->inlineText($text);
+        $Elements[] = $InlineText['element'];
+
+        foreach ($Elements as &$Element)
+        {
+            if ( ! isset($Element['autobreak']))
+            {
+                $Element['autobreak'] = false;
+            }
+        }
+
+        return $Elements;
+    }
+
+    #
+    # ~
+    #
+
+    protected function inlineText($text)
+    {
+        $Inline = array(
+            'extent' => strlen($text),
+            'element' => array(),
+        );
+
+        $Inline['element']['elements'] = self::pregReplaceElements(
+            $this->breaksEnabled ? '/[ ]*+\n/' : '/(?:[ ]*+\\\\|[ ]{2,}+)\n/',
+            array(
+                array('name' => 'br'),
+                array('text' => "\n"),
+            ),
+            $text
+        );
+
+        return $Inline;
+    }
+
+    protected function inlineCode($Excerpt)
+    {
+        $marker = $Excerpt['text'][0];
+
+        if (preg_match('/^(['.$marker.']++)[ ]*+(.+?)[ ]*+(?<!['.$marker.'])\1(?!'.$marker.')/s', $Excerpt['text'], $matches))
+        {
+            $text = $matches[2];
+            $text = preg_replace('/[ ]*+\n/', ' ', $text);
+
+            return array(
+                'extent' => strlen($matches[0]),
+                'element' => array(
+                    'name' => 'code',
+                    'text' => $text,
+                ),
+            );
+        }
+    }
+
+    protected function inlineEmailTag($Excerpt)
+    {
+        $hostnameLabel = '[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?';
+
+        $commonMarkEmail = '[a-zA-Z0-9.!#$%&\'*+\/=?^_`{|}~-]++@'
+            . $hostnameLabel . '(?:\.' . $hostnameLabel . ')*';
+
+        if (strpos($Excerpt['text'], '>') !== false
+            and preg_match("/^<((mailto:)?$commonMarkEmail)>/i", $Excerpt['text'], $matches)
+        ){
+            $url = $matches[1];
+
+            if ( ! isset($matches[2]))
+            {
+                $url = "mailto:$url";
+            }
+
+            return array(
+                'extent' => strlen($matches[0]),
+                'element' => array(
+                    'name' => 'a',
+                    'text' => $matches[1],
+                    'attributes' => array(
+                        'href' => $url,
+                    ),
+                ),
+            );
+        }
+    }
+
+    protected function inlineEmphasis($Excerpt)
+    {
+        if ( ! isset($Excerpt['text'][1]))
+        {
+            return;
+        }
+
+        $marker = $Excerpt['text'][0];
+
+        if ($Excerpt['text'][1] === $marker and preg_match($this->StrongRegex[$marker], $Excerpt['text'], $matches))
+        {
+            $emphasis = 'strong';
+        }
+        elseif (preg_match($this->EmRegex[$marker], $Excerpt['text'], $matches))
+        {
+            $emphasis = 'em';
+        }
+        else
+        {
+            return;
+        }
+
+        return array(
+            'extent' => strlen($matches[0]),
+            'element' => array(
+                'name' => $emphasis,
+                'handler' => array(
+                    'function' => 'lineElements',
+                    'argument' => $matches[1],
+                    'destination' => 'elements',
+                )
+            ),
+        );
+    }
+
+    protected function inlineEscapeSequence($Excerpt)
+    {
+        if (isset($Excerpt['text'][1]) and in_array($Excerpt['text'][1], $this->specialCharacters))
+        {
+            return array(
+                'element' => array('rawHtml' => $Excerpt['text'][1]),
+                'extent' => 2,
+            );
+        }
+    }
+    
+    protected $imageMode = False;
+
+    protected function inlineImage($Excerpt)
+    {
+        if ( ! isset($Excerpt['text'][1]) or $Excerpt['text'][1] !== '[')
+        {
+            return;
+        }
+
+        $Excerpt['text']= substr($Excerpt['text'], 1);
+        
+        $this->imageMode = True;
+        $Link = $this->inlineLink($Excerpt);
+        $this->imageMode = False;
+
+        if ($Link === null)
+        {
+            return;
+        }
+
+        $Inline = array(
+            'extent' => $Link['extent'] + 1,
+            'element' => array(
+                'name' => 'img',
+                'attributes' => array(
+                    'src' => $Link['element']['attributes']['href'],
+                    'alt' => $Link['element']['handler']['argument'],
+                ),
+                'autobreak' => true,
+            ),
+        );
+
+        $Inline['element']['attributes'] += $Link['element']['attributes'];
+
+        unset($Inline['element']['attributes']['href']);
+
+        return $Inline;
+    }
+
+    protected function inlineLink($Excerpt)
+    {
+        $Element = array(
+            'name' => 'a',
+            'handler' => array(
+                'function' => 'lineElements',
+                'argument' => null,
+                'destination' => 'elements',
+            ),
+            'nonNestables' => array('Url', 'Link'),
+            'attributes' => array(
+                'href' => null,
+                'title' => null,
+            ),
+        );
+
+        $extent = 0;
+
+        $remainder = $Excerpt['text'];
+
+        if (preg_match('/\[((?:[^][]++|(?R))*+)\]/', $remainder, $matches))
+        {
+            $Element['handler']['argument'] = $matches[1];
+
+            $extent += strlen($matches[0]);
+
+            $remainder = substr($remainder, $extent);
+        }
+        else
+        {
+            return;
+        }
+
+        if (preg_match('/^[(]\s*+((?:[^ ()]++|[(][^ )]+[)])++)(?:[ ]+("[^"]*+"|\'[^\']*+\'))?\s*+[)]/', $remainder, $matches))
+        {
+            $Element['attributes']['href'] = $matches[1];
+
+            if (isset($matches[2]))
+            {
+                $Element['attributes']['title'] = substr($matches[2], 1, - 1);
+            }
+
+            $extent += strlen($matches[0]);
+        }
+        else
+        {
+            if (preg_match('/^\s*\[(.*?)\]/', $remainder, $matches))
+            {
+                $definition = strlen($matches[1]) ? $matches[1] : $Element['handler']['argument'];
+                $definition = strtolower($definition);
+
+                $extent += strlen($matches[0]);
+            }
+            else
+            {
+                $definition = strtolower($Element['handler']['argument']);
+            }
+
+            if ( ! isset($this->DefinitionData['Reference'][$definition]))
+            {
+                return;
+            }
+
+            $Definition = $this->DefinitionData['Reference'][$definition];
+
+            $Element['attributes']['href'] = $Definition['url'];
+            $Element['attributes']['title'] = $Definition['title'];
+        }
+        if(!$this->imageMode){
+            if(strpos($Element['attributes']['href'], 'http') === False &&
+               strpos($Element['attributes']['href'], 'https') === False){
+          		$Element['attributes']['href']=$this->GetInterlinkPath($Element['attributes']['href']);
+            }
+        }else{
+            if(strpos($Element['attributes']['href'], 'http') === False &&
+               strpos($Element['attributes']['href'], 'https') === False){
+                $Element['attributes']['href']=$this->GetInterlinkPath($Element['attributes']['href']);
+            }
+        }
+        return array(
+            'extent' => $extent,
+            'element' => $Element,
+        );
+    }
+
+    protected function inlineMarkup($Excerpt)
+    {
+        if ($this->markupEscaped or $this->safeMode or strpos($Excerpt['text'], '>') === false)
+        {
+            return;
+        }
+
+        if ($Excerpt['text'][1] === '/' and preg_match('/^<\/\w[\w-]*+[ ]*+>/s', $Excerpt['text'], $matches))
+        {
+            return array(
+                'element' => array('rawHtml' => $matches[0]),
+                'extent' => strlen($matches[0]),
+            );
+        }
+
+        if ($Excerpt['text'][1] === '!' and preg_match('/^<!---?[^>-](?:-?+[^-])*-->/s', $Excerpt['text'], $matches))
+        {
+            return array(
+                'element' => array('rawHtml' => $matches[0]),
+                'extent' => strlen($matches[0]),
+            );
+        }
+
+        if ($Excerpt['text'][1] !== ' ' and preg_match('/^<\w[\w-]*+(?:[ ]*+'.$this->regexHtmlAttribute.')*+[ ]*+\/?>/s', $Excerpt['text'], $matches))
+        {
+            return array(
+                'element' => array('rawHtml' => $matches[0]),
+                'extent' => strlen($matches[0]),
+            );
+        }
+    }
+
+    protected function inlineSpecialCharacter($Excerpt)
+    {
+        if (substr($Excerpt['text'], 1, 1) !== ' ' and strpos($Excerpt['text'], ';') !== false
+            and preg_match('/^&(#?+[0-9a-zA-Z]++);/', $Excerpt['text'], $matches)
+        ) {
+            return array(
+                'element' => array('rawHtml' => '&' . $matches[1] . ';'),
+                'extent' => strlen($matches[0]),
+            );
+        }
+
+        return;
+    }
+
+    protected function inlineStrikethrough($Excerpt)
+    {
+        if ( ! isset($Excerpt['text'][1]))
+        {
+            return;
+        }
+
+        if ($Excerpt['text'][1] === '~' and preg_match('/^~~(?=\S)(.+?)(?<=\S)~~/', $Excerpt['text'], $matches))
+        {
+            return array(
+                'extent' => strlen($matches[0]),
+                'element' => array(
+                    'name' => 'del',
+                    'handler' => array(
+                        'function' => 'lineElements',
+                        'argument' => $matches[1],
+                        'destination' => 'elements',
+                    )
+                ),
+            );
+        }
+    }
+
+    protected function inlineUrl($Excerpt)
+    {
+        if ($this->urlsLinked !== true or ! isset($Excerpt['text'][2]) or $Excerpt['text'][2] !== '/')
+        {
+            return;
+        }
+
+        if (strpos($Excerpt['context'], 'http') !== false
+            and preg_match('/\bhttps?+:[\/]{2}[^\s<]+\b\/*+/ui', $Excerpt['context'], $matches, PREG_OFFSET_CAPTURE)
+        ) {
+            $url = $matches[0][0];
+
+            $Inline = array(
+                'extent' => strlen($matches[0][0]),
+                'position' => $matches[0][1],
+                'element' => array(
+                    'name' => 'a',
+                    'text' => $url,
+                    'attributes' => array(
+                        'href' => $url,
+                    ),
+                ),
+            );
+
+            return $Inline;
+        }
+    }
+
+    protected function inlineUrlTag($Excerpt)
+    {
+        if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<(\w++:\/{2}[^ >]++)>/i', $Excerpt['text'], $matches))
+        {
+            $url = $matches[1];
+
+            return array(
+                'extent' => strlen($matches[0]),
+                'element' => array(
+                    'name' => 'a',
+                    'text' => $url,
+                    'attributes' => array(
+                        'href' => $url,
+                    ),
+                ),
+            );
+        }
+    }
+
+    # ~
+
+    protected function unmarkedText($text)
+    {
+        $Inline = $this->inlineText($text);
+        return $this->element($Inline['element']);
+    }
+
+    #
+    # Handlers
+    #
+
+    protected function handle(array $Element)
+    {
+        if (isset($Element['handler']))
+        {
+            if (!isset($Element['nonNestables']))
+            {
+                $Element['nonNestables'] = array();
+            }
+
+            if (is_string($Element['handler']))
+            {
+                $function = $Element['handler'];
+                $argument = $Element['text'];
+                unset($Element['text']);
+                $destination = 'rawHtml';
+            }
+            else
+            {
+                $function = $Element['handler']['function'];
+                $argument = $Element['handler']['argument'];
+                $destination = $Element['handler']['destination'];
+            }
+
+            $Element[$destination] = $this->{$function}($argument, $Element['nonNestables']);
+
+            if ($destination === 'handler')
+            {
+                $Element = $this->handle($Element);
+            }
+
+            unset($Element['handler']);
+        }
+
+        return $Element;
+    }
+
+    protected function handleElementRecursive(array $Element)
+    {
+        return $this->elementApplyRecursive(array($this, 'handle'), $Element);
+    }
+
+    protected function handleElementsRecursive(array $Elements)
+    {
+        return $this->elementsApplyRecursive(array($this, 'handle'), $Elements);
+    }
+
+    protected function elementApplyRecursive($closure, array $Element)
+    {
+        $Element = call_user_func($closure, $Element);
+
+        if (isset($Element['elements']))
+        {
+            $Element['elements'] = $this->elementsApplyRecursive($closure, $Element['elements']);
+        }
+        elseif (isset($Element['element']))
+        {
+            $Element['element'] = $this->elementApplyRecursive($closure, $Element['element']);
+        }
+
+        return $Element;
+    }
+
+    protected function elementApplyRecursiveDepthFirst($closure, array $Element)
+    {
+        if (isset($Element['elements']))
+        {
+            $Element['elements'] = $this->elementsApplyRecursiveDepthFirst($closure, $Element['elements']);
+        }
+        elseif (isset($Element['element']))
+        {
+            $Element['element'] = $this->elementsApplyRecursiveDepthFirst($closure, $Element['element']);
+        }
+
+        $Element = call_user_func($closure, $Element);
+
+        return $Element;
+    }
+
+    protected function elementsApplyRecursive($closure, array $Elements)
+    {
+        foreach ($Elements as &$Element)
+        {
+            $Element = $this->elementApplyRecursive($closure, $Element);
+        }
+
+        return $Elements;
+    }
+
+    protected function elementsApplyRecursiveDepthFirst($closure, array $Elements)
+    {
+        foreach ($Elements as &$Element)
+        {
+            $Element = $this->elementApplyRecursiveDepthFirst($closure, $Element);
+        }
+
+        return $Elements;
+    }
+
+    protected function element(array $Element)
+    {
+        if ($this->safeMode)
+        {
+            $Element = $this->sanitiseElement($Element);
+        }
+
+        # identity map if element has no handler
+        $Element = $this->handle($Element);
+
+        $hasName = isset($Element['name']);
+
+        $markup = '';
+
+        if ($hasName)
+        {
+            $markup .= '<' . $Element['name'];
+
+            if (isset($Element['attributes']))
+            {
+                foreach ($Element['attributes'] as $name => $value)
+                {
+                    if ($value === null)
+                    {
+                        continue;
+                    }
+
+                    $markup .= " $name=\"".self::escape($value).'"';
+                }
+            }
+        }
+
+        $permitRawHtml = false;
+
+        if (isset($Element['text']))
+        {
+            $text = $Element['text'];
+        }
+        // very strongly consider an alternative if you're writing an
+        // extension
+        elseif (isset($Element['rawHtml']))
+        {
+            $text = $Element['rawHtml'];
+
+            $allowRawHtmlInSafeMode = isset($Element['allowRawHtmlInSafeMode']) && $Element['allowRawHtmlInSafeMode'];
+            $permitRawHtml = !$this->safeMode || $allowRawHtmlInSafeMode;
+        }
+
+        $hasContent = isset($text) || isset($Element['element']) || isset($Element['elements']);
+
+        if ($hasContent)
+        {
+            $markup .= $hasName ? '>' : '';
+
+            if (isset($Element['elements']))
+            {
+                $markup .= $this->elements($Element['elements']);
+            }
+            elseif (isset($Element['element']))
+            {
+                $markup .= $this->element($Element['element']);
+            }
+            else
+            {
+                if (!$permitRawHtml)
+                {
+                    $markup .= self::escape($text, true);
+                }
+                else
+                {
+                    $markup .= $text;
+                }
+            }
+
+            $markup .= $hasName ? '</' . $Element['name'] . '>' : '';
+        }
+        elseif ($hasName)
+        {
+            $markup .= ' />';
+        }
+
+        return $markup;
+    }
+
+    protected function elements(array $Elements)
+    {
+        $markup = '';
+
+        $autoBreak = true;
+
+        foreach ($Elements as $Element)
+        {
+            if (empty($Element))
+            {
+                continue;
+            }
+
+            $autoBreakNext = (isset($Element['autobreak'])
+                ? $Element['autobreak'] : isset($Element['name'])
+            );
+            // (autobreak === false) covers both sides of an element
+            $autoBreak = !$autoBreak ? $autoBreak : $autoBreakNext;
+
+            $markup .= ($autoBreak ? "\n" : '') . $this->element($Element);
+            $autoBreak = $autoBreakNext;
+        }
+
+        $markup .= $autoBreak ? "\n" : '';
+
+        return $markup;
+    }
+
+    # ~
+
+    protected function li($lines)
+    {
+        $Elements = $this->linesElements($lines);
+
+        if ( ! in_array('', $lines)
+            and isset($Elements[0]) and isset($Elements[0]['name'])
+            and $Elements[0]['name'] === 'p'
+        ) {
+            unset($Elements[0]['name']);
+        }
+
+        return $Elements;
+    }
+
+    #
+    # AST Convenience
+    #
+
+    /**
+     * Replace occurrences $regexp with $Elements in $text. Return an array of
+     * elements representing the replacement.
+     */
+    protected static function pregReplaceElements($regexp, $Elements, $text)
+    {
+        $newElements = array();
+
+        while (preg_match($regexp, $text, $matches, PREG_OFFSET_CAPTURE))
+        {
+            $offset = $matches[0][1];
+            $before = substr($text, 0, $offset);
+            $after = substr($text, $offset + strlen($matches[0][0]));
+
+            $newElements[] = array('text' => $before);
+
+            foreach ($Elements as $Element)
+            {
+                $newElements[] = $Element;
+            }
+
+            $text = $after;
+        }
+
+        $newElements[] = array('text' => $text);
+
+        return $newElements;
+    }
+
+    #
+    # Deprecated Methods
+    #
+
+    function parse($text)
+    {
+        $markup = $this->text($text);
+
+        return $markup;
+    }
+
+    protected function sanitiseElement(array $Element)
+    {
+        static $goodAttribute = '/^[a-zA-Z0-9][a-zA-Z0-9-_]*+$/';
+        static $safeUrlNameToAtt  = array(
+            'a'   => 'href',
+            'img' => 'src',
+        );
+
+        if ( ! isset($Element['name']))
+        {
+            unset($Element['attributes']);
+            return $Element;
+        }
+
+        if (isset($safeUrlNameToAtt[$Element['name']]))
+        {
+            $Element = $this->filterUnsafeUrlInAttribute($Element, $safeUrlNameToAtt[$Element['name']]);
+        }
+
+        if ( ! empty($Element['attributes']))
+        {
+            foreach ($Element['attributes'] as $att => $val)
+            {
+                # filter out badly parsed attribute
+                if ( ! preg_match($goodAttribute, $att))
+                {
+                    unset($Element['attributes'][$att]);
+                }
+                # dump onevent attribute
+                elseif (self::striAtStart($att, 'on'))
+                {
+                    unset($Element['attributes'][$att]);
+                }
+            }
+        }
+
+        return $Element;
+    }
+
+    protected function filterUnsafeUrlInAttribute(array $Element, $attribute)
+    {
+        foreach ($this->safeLinksWhitelist as $scheme)
+        {
+            if (self::striAtStart($Element['attributes'][$attribute], $scheme))
+            {
+                return $Element;
+            }
+        }
+
+        $Element['attributes'][$attribute] = str_replace(':', '%3A', $Element['attributes'][$attribute]);
+
+        return $Element;
+    }
+
+    #
+    # Static Methods
+    #
+
+    protected static function escape($text, $allowQuotes = false)
+    {
+        return htmlspecialchars($text, $allowQuotes ? ENT_NOQUOTES : ENT_QUOTES, 'UTF-8');
+    }
+
+    protected static function striAtStart($string, $needle)
+    {
+        $len = strlen($needle);
+
+        if ($len > strlen($string))
+        {
+            return false;
+        }
+        else
+        {
+            return strtolower(substr($string, 0, $len)) === strtolower($needle);
+        }
+    }
+
+    static function instance($name = 'default')
+    {
+        if (isset(self::$instances[$name]))
+        {
+            return self::$instances[$name];
+        }
+
+        $instance = new static();
+
+        self::$instances[$name] = $instance;
+
+        return $instance;
+    }
+
+    private static $instances = array();
+
+    #
+    # Fields
+    #
+
+    protected $DefinitionData;
+
+    #
+    # Read-Only
+
+    protected $specialCharacters = array(
+        '\\', '`', '*', '_', '{', '}', '[', ']', '(', ')', '>', '#', '+', '-', '.', '!', '|', '~'
+    );
+
+    protected $StrongRegex = array(
+        '*' => '/^[*]{2}((?:\\\\\*|[^*]|[*][^*]*+[*])+?)[*]{2}(?![*])/s',
+        '_' => '/^__((?:\\\\_|[^_]|_[^_]*+_)+?)__(?!_)/us',
+    );
+
+    protected $EmRegex = array(
+        '*' => '/^[*]((?:\\\\\*|[^*]|[*][*][^*]+?[*][*])+?)[*](?![*])/s',
+        '_' => '/^_((?:\\\\_|[^_]|__[^_]*__)+?)_(?!_)\b/us',
+    );
+
+    protected $regexHtmlAttribute = '[a-zA-Z_:][\w:.-]*+(?:\s*+=\s*+(?:[^"\'=<>`\s]+|"[^"]*+"|\'[^\']*+\'))?+';
+
+    protected $voidElements = array(
+        'area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source',
+    );
+
+    protected $textLevelElements = array(
+        'a', 'br', 'bdo', 'abbr', 'blink', 'nextid', 'acronym', 'basefont',
+        'b', 'em', 'big', 'cite', 'small', 'spacer', 'listing',
+        'i', 'rp', 'del', 'code',          'strike', 'marquee',
+        'q', 'rt', 'ins', 'font',          'strong',
+        's', 'tt', 'kbd', 'mark',
+        'u', 'xm', 'sub', 'nobr',
+                   'sup', 'ruby',
+                   'var', 'span',
+                   'wbr', 'time',
+    );
+}

+ 689 - 0
demo/ParsedownExtra.php

@@ -0,0 +1,689 @@
+<?php
+
+#
+#
+# Parsedown Extra
+# https://github.com/erusev/parsedown-extra
+#
+# (c) Emanuil Rusev
+# http://erusev.com
+#
+# For the full license information, view the LICENSE file that was distributed
+# with this source code.
+#
+#
+
+class ParsedownExtra extends Parsedown
+{
+    # ~
+
+    const version = '0.8.0-beta-1';
+
+    # ~
+
+    function __construct()
+    {
+        if (version_compare(parent::version, '1.7.1') < 0)
+        {
+            throw new Exception('ParsedownExtra requires a later version of Parsedown');
+        }
+
+        $this->BlockTypes[':'] []= 'DefinitionList';
+        $this->BlockTypes['*'] []= 'Abbreviation';
+
+        # identify footnote definitions before reference definitions
+        array_unshift($this->BlockTypes['['], 'Footnote');
+
+        # identify footnote markers before before links
+        array_unshift($this->InlineTypes['['], 'FootnoteMarker');
+    }
+
+    #
+    # ~
+
+    function text($text)
+    {
+        $Elements = $this->textElements($text);
+
+        # convert to markup
+        $markup = $this->elements($Elements);
+
+        # trim line breaks
+        $markup = trim($markup, "\n");
+
+        # merge consecutive dl elements
+
+        $markup = preg_replace('/<\/dl>\s+<dl>\s+/', '', $markup);
+
+        # add footnotes
+
+        if (isset($this->DefinitionData['Footnote']))
+        {
+            $Element = $this->buildFootnoteElement();
+
+            $markup .= "\n" . $this->element($Element);
+        }
+
+        return $markup;
+    }
+
+    #
+    # Blocks
+    #
+
+    #
+    # Abbreviation
+
+    protected function blockAbbreviation($Line)
+    {
+        if (preg_match('/^\*\[(.+?)\]:[ ]*(.+?)[ ]*$/', $Line['text'], $matches))
+        {
+            $this->DefinitionData['Abbreviation'][$matches[1]] = $matches[2];
+
+            $Block = array(
+                'hidden' => true,
+            );
+
+            return $Block;
+        }
+    }
+
+    #
+    # Footnote
+
+    protected function blockFootnote($Line)
+    {
+        if (preg_match('/^\[\^(.+?)\]:[ ]?(.*)$/', $Line['text'], $matches))
+        {
+            $Block = array(
+                'label' => $matches[1],
+                'text' => $matches[2],
+                'hidden' => true,
+            );
+
+            return $Block;
+        }
+    }
+
+    protected function blockFootnoteContinue($Line, $Block)
+    {
+        if ($Line['text'][0] === '[' and preg_match('/^\[\^(.+?)\]:/', $Line['text']))
+        {
+            return;
+        }
+
+        if (isset($Block['interrupted']))
+        {
+            if ($Line['indent'] >= 4)
+            {
+                $Block['text'] .= "\n\n" . $Line['text'];
+
+                return $Block;
+            }
+        }
+        else
+        {
+            $Block['text'] .= "\n" . $Line['text'];
+
+            return $Block;
+        }
+    }
+
+    protected function blockFootnoteComplete($Block)
+    {
+        $this->DefinitionData['Footnote'][$Block['label']] = array(
+            'text' => $Block['text'],
+            'count' => null,
+            'number' => null,
+        );
+
+        return $Block;
+    }
+
+    #
+    # Definition List
+
+    protected function blockDefinitionList($Line, $Block)
+    {
+        if ( ! isset($Block) or $Block['type'] !== 'Paragraph')
+        {
+            return;
+        }
+
+        $Element = array(
+            'name' => 'dl',
+            'elements' => array(),
+        );
+
+        $terms = explode("\n", $Block['element']['handler']['argument']);
+
+        foreach ($terms as $term)
+        {
+            $Element['elements'] []= array(
+                'name' => 'dt',
+                'handler' => array(
+                    'function' => 'lineElements',
+                    'argument' => $term,
+                    'destination' => 'elements'
+                ),
+            );
+        }
+
+        $Block['element'] = $Element;
+
+        $Block = $this->addDdElement($Line, $Block);
+
+        return $Block;
+    }
+
+    protected function blockDefinitionListContinue($Line, array $Block)
+    {
+        if ($Line['text'][0] === ':')
+        {
+            $Block = $this->addDdElement($Line, $Block);
+
+            return $Block;
+        }
+        else
+        {
+            if (isset($Block['interrupted']) and $Line['indent'] === 0)
+            {
+                return;
+            }
+
+            if (isset($Block['interrupted']))
+            {
+                $Block['dd']['handler']['function'] = 'textElements';
+                $Block['dd']['handler']['argument'] .= "\n\n";
+
+                $Block['dd']['handler']['destination'] = 'elements';
+
+                unset($Block['interrupted']);
+            }
+
+            $text = substr($Line['body'], min($Line['indent'], 4));
+
+            $Block['dd']['handler']['argument'] .= "\n" . $text;
+
+            return $Block;
+        }
+    }
+
+    #
+    # Header
+
+    protected function blockHeader($Line)
+    {
+        $Block = parent::blockHeader($Line);
+
+        if (preg_match('/[ #]*{('.$this->regexAttribute.'+)}[ ]*$/', $Block['element']['handler']['argument'], $matches, PREG_OFFSET_CAPTURE))
+        {
+            $attributeString = $matches[1][0];
+
+            $Block['element']['attributes'] = $this->parseAttributeData($attributeString);
+
+            $Block['element']['handler']['argument'] = substr($Block['element']['handler']['argument'], 0, $matches[0][1]);
+        }
+
+        return $Block;
+    }
+
+    #
+    # Markup
+
+    protected function blockMarkup($Line)
+    {
+        if ($this->markupEscaped or $this->safeMode)
+        {
+            return;
+        }
+
+        if (preg_match('/^<(\w[\w-]*)(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*(\/)?>/', $Line['text'], $matches))
+        {
+            $element = strtolower($matches[1]);
+
+            if (in_array($element, $this->textLevelElements))
+            {
+                return;
+            }
+
+            $Block = array(
+                'name' => $matches[1],
+                'depth' => 0,
+                'element' => array(
+                    'rawHtml' => $Line['text'],
+                    'autobreak' => true,
+                ),
+            );
+
+            $length = strlen($matches[0]);
+            $remainder = substr($Line['text'], $length);
+
+            if (trim($remainder) === '')
+            {
+                if (isset($matches[2]) or in_array($matches[1], $this->voidElements))
+                {
+                    $Block['closed'] = true;
+                    $Block['void'] = true;
+                }
+            }
+            else
+            {
+                if (isset($matches[2]) or in_array($matches[1], $this->voidElements))
+                {
+                    return;
+                }
+                if (preg_match('/<\/'.$matches[1].'>[ ]*$/i', $remainder))
+                {
+                    $Block['closed'] = true;
+                }
+            }
+
+            return $Block;
+        }
+    }
+
+    protected function blockMarkupContinue($Line, array $Block)
+    {
+        if (isset($Block['closed']))
+        {
+            return;
+        }
+
+        if (preg_match('/^<'.$Block['name'].'(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*>/i', $Line['text'])) # open
+        {
+            $Block['depth'] ++;
+        }
+
+        if (preg_match('/(.*?)<\/'.$Block['name'].'>[ ]*$/i', $Line['text'], $matches)) # close
+        {
+            if ($Block['depth'] > 0)
+            {
+                $Block['depth'] --;
+            }
+            else
+            {
+                $Block['closed'] = true;
+            }
+        }
+
+        if (isset($Block['interrupted']))
+        {
+            $Block['element']['rawHtml'] .= "\n";
+            unset($Block['interrupted']);
+        }
+
+        $Block['element']['rawHtml'] .= "\n".$Line['body'];
+
+        return $Block;
+    }
+
+    protected function blockMarkupComplete($Block)
+    {
+        if ( ! isset($Block['void']))
+        {
+            $Block['element']['rawHtml'] = $this->processTag($Block['element']['rawHtml']);
+        }
+
+        return $Block;
+    }
+
+    #
+    # Setext
+
+    protected function blockSetextHeader($Line, array $Block = null)
+    {
+        $Block = parent::blockSetextHeader($Line, $Block);
+        
+        //Yiming: prevent error
+        if(!$Block) return NULL;
+        if (preg_match('/[ ]*{('.$this->regexAttribute.'+)}[ ]*$/', $Block['element']['handler']['argument'], $matches, PREG_OFFSET_CAPTURE))
+        {
+            $attributeString = $matches[1][0];
+
+            $Block['element']['attributes'] = $this->parseAttributeData($attributeString);
+
+            $Block['element']['handler']['argument'] = substr($Block['element']['handler']['argument'], 0, $matches[0][1]);
+        }
+
+        return $Block;
+    }
+
+    #
+    # Inline Elements
+    #
+
+    #
+    # Footnote Marker
+
+    protected function inlineFootnoteMarker($Excerpt)
+    {
+        if (preg_match('/^\[\^(.+?)\]/', $Excerpt['text'], $matches))
+        {
+            $name = $matches[1];
+
+            if ( ! isset($this->DefinitionData['Footnote'][$name]))
+            {
+                return;
+            }
+
+            $this->DefinitionData['Footnote'][$name]['count'] ++;
+
+            if ( ! isset($this->DefinitionData['Footnote'][$name]['number']))
+            {
+                $this->DefinitionData['Footnote'][$name]['number'] = ++ $this->footnoteCount; # » &
+            }
+
+            $Element = array(
+                'name' => 'sup',
+                'attributes' => array('id' => 'fnref'.$this->DefinitionData['Footnote'][$name]['count'].':'.$name),
+                'element' => array(
+                    'name' => 'a',
+                    'attributes' => array('href' => '#fn:'.$name, 'class' => 'footnote-ref'),
+                    'text' => $this->DefinitionData['Footnote'][$name]['number'],
+                ),
+            );
+
+            return array(
+                'extent' => strlen($matches[0]),
+                'element' => $Element,
+            );
+        }
+    }
+
+    private $footnoteCount = 0;
+
+    #
+    # Link
+
+    protected function inlineLink($Excerpt)
+    {
+        $Link = parent::inlineLink($Excerpt);
+        
+        if(!isset($Link['extent'])) return $Link;
+        $remainder = substr($Excerpt['text'], $Link['extent']);
+
+        if (preg_match('/^[ ]*{('.$this->regexAttribute.'+)}/', $remainder, $matches))
+        {
+            $Link['element']['attributes'] += $this->parseAttributeData($matches[1]);
+
+            $Link['extent'] += strlen($matches[0]);
+        }
+
+        return $Link;
+    }
+
+    #
+    # ~
+    #
+
+    private $currentAbreviation;
+    private $currentMeaning;
+
+    protected function insertAbreviation(array $Element)
+    {
+        if (isset($Element['text']))
+        {
+            $Element['elements'] = self::pregReplaceElements(
+                '/\b'.preg_quote($this->currentAbreviation, '/').'\b/',
+                array(
+                    array(
+                        'name' => 'abbr',
+                        'attributes' => array(
+                            'title' => $this->currentMeaning,
+                        ),
+                        'text' => $this->currentAbreviation,
+                    )
+                ),
+                $Element['text']
+            );
+
+            unset($Element['text']);
+        }
+
+        return $Element;
+    }
+
+    protected function inlineText($text)
+    {
+        $Inline = parent::inlineText($text);
+
+        if (isset($this->DefinitionData['Abbreviation']))
+        {
+            foreach ($this->DefinitionData['Abbreviation'] as $abbreviation => $meaning)
+            {
+                $this->currentAbreviation = $abbreviation;
+                $this->currentMeaning = $meaning;
+
+                $Inline['element'] = $this->elementApplyRecursiveDepthFirst(
+                    array($this, 'insertAbreviation'),
+                    $Inline['element']
+                );
+            }
+        }
+
+        return $Inline;
+    }
+
+    #
+    # Util Methods
+    #
+
+    protected function addDdElement(array $Line, array $Block)
+    {
+        $text = substr($Line['text'], 1);
+        $text = trim($text);
+
+        unset($Block['dd']);
+
+        $Block['dd'] = array(
+            'name' => 'dd',
+            'handler' => array(
+                'function' => 'lineElements',
+                'argument' => $text,
+                'destination' => 'elements'
+            ),
+        );
+
+        if (isset($Block['interrupted']))
+        {
+            $Block['dd']['handler']['function'] = 'textElements';
+
+            unset($Block['interrupted']);
+        }
+
+        $Block['element']['elements'] []= & $Block['dd'];
+
+        return $Block;
+    }
+
+    protected function buildFootnoteElement()
+    {
+        $Element = array(
+            'name' => 'div',
+            'attributes' => array('class' => 'footnotes'),
+            'elements' => array(
+                array('name' => 'hr'),
+                array(
+                    'name' => 'ol',
+                    'elements' => array(),
+                ),
+            ),
+        );
+
+        uasort($this->DefinitionData['Footnote'], 'self::sortFootnotes');
+
+        foreach ($this->DefinitionData['Footnote'] as $definitionId => $DefinitionData)
+        {
+            if ( ! isset($DefinitionData['number']))
+            {
+                continue;
+            }
+
+            $text = $DefinitionData['text'];
+
+            $textElements = parent::textElements($text);
+
+            $numbers = range(1, $DefinitionData['count']);
+
+            $backLinkElements = array();
+
+            foreach ($numbers as $number)
+            {
+                $backLinkElements[] = array('text' => ' ');
+                $backLinkElements[] = array(
+                    'name' => 'a',
+                    'attributes' => array(
+                        'href' => "#fnref$number:$definitionId",
+                        'rev' => 'footnote',
+                        'class' => 'footnote-backref',
+                    ),
+                    'rawHtml' => '&#8617;',
+                    'allowRawHtmlInSafeMode' => true,
+                    'autobreak' => false,
+                );
+            }
+
+            unset($backLinkElements[0]);
+
+            $n = count($textElements) -1;
+
+            if ($textElements[$n]['name'] === 'p')
+            {
+                $backLinkElements = array_merge(
+                    array(
+                        array(
+                            'rawHtml' => '&#160;',
+                            'allowRawHtmlInSafeMode' => true,
+                        ),
+                    ),
+                    $backLinkElements
+                );
+
+                unset($textElements[$n]['name']);
+
+                $textElements[$n] = array(
+                    'name' => 'p',
+                    'elements' => array_merge(
+                        array($textElements[$n]),
+                        $backLinkElements
+                    ),
+                );
+            }
+            else
+            {
+                $textElements[] = array(
+                    'name' => 'p',
+                    'elements' => $backLinkElements
+                );
+            }
+
+            $Element['elements'][1]['elements'] []= array(
+                'name' => 'li',
+                'attributes' => array('id' => 'fn:'.$definitionId),
+                'elements' => array_merge(
+                    $textElements
+                ),
+            );
+        }
+
+        return $Element;
+    }
+
+    # ~
+
+    protected function parseAttributeData($attributeString)
+    {
+        $Data = array();
+
+        $attributes = preg_split('/[ ]+/', $attributeString, - 1, PREG_SPLIT_NO_EMPTY);
+
+        foreach ($attributes as $attribute)
+        {
+            if ($attribute[0] === '#')
+            {
+                $Data['id'] = substr($attribute, 1);
+            }
+            else # "."
+            {
+                $classes []= substr($attribute, 1);
+            }
+        }
+
+        if (isset($classes))
+        {
+            $Data['class'] = implode(' ', $classes);
+        }
+
+        return $Data;
+    }
+
+    # ~
+
+    protected function processTag($elementMarkup) # recursive
+    {
+        # http://stackoverflow.com/q/1148928/200145
+        libxml_use_internal_errors(true);
+
+        $DOMDocument = new DOMDocument;
+
+        # http://stackoverflow.com/q/11309194/200145
+        $elementMarkup = mb_convert_encoding($elementMarkup, 'HTML-ENTITIES', 'UTF-8');
+
+        # http://stackoverflow.com/q/4879946/200145
+        $DOMDocument->loadHTML($elementMarkup);
+        $DOMDocument->removeChild($DOMDocument->doctype);
+        $DOMDocument->replaceChild($DOMDocument->firstChild->firstChild->firstChild, $DOMDocument->firstChild);
+
+        $elementText = '';
+
+        if ($DOMDocument->documentElement->getAttribute('markdown') === '1')
+        {
+            foreach ($DOMDocument->documentElement->childNodes as $Node)
+            {
+                $elementText .= $DOMDocument->saveHTML($Node);
+            }
+
+            $DOMDocument->documentElement->removeAttribute('markdown');
+
+            $elementText = "\n".$this->text($elementText)."\n";
+        }
+        else
+        {
+            foreach ($DOMDocument->documentElement->childNodes as $Node)
+            {
+                $nodeMarkup = $DOMDocument->saveHTML($Node);
+
+                if ($Node instanceof DOMElement and ! in_array($Node->nodeName, $this->textLevelElements))
+                {
+                    $elementText .= $this->processTag($nodeMarkup);
+                }
+                else
+                {
+                    $elementText .= $nodeMarkup;
+                }
+            }
+        }
+
+        # because we don't want for markup to get encoded
+        $DOMDocument->documentElement->nodeValue = 'placeholder\x1A';
+
+        $markup = $DOMDocument->saveHTML($DOMDocument->documentElement);
+        $markup = str_replace('placeholder\x1A', $elementText, $markup);
+
+        return $markup;
+    }
+
+    # ~
+
+    protected function sortFootnotes($A, $B) # callback
+    {
+        return $A['number'] - $B['number'];
+    }
+
+    #
+    # Fields
+    #
+
+    protected $regexAttribute = '(?:[#.][-\w]+[ ]*)';
+}

二进制
demo/images/20220118083330.jpg


二进制
demo/images/20220118091056.jpg


+ 2 - 0
demo/images/list.md

@@ -0,0 +1,2 @@
+- 20220118091056.jpg; 
+- 20220118083330.jpg; REFS 20220118083133; 

二进制
demo/images/thumb/20220118083330.jpg


二进制
demo/images/thumb/20220118091056.jpg


+ 3687 - 0
demo/index.php

@@ -0,0 +1,3687 @@
+<?php
+
+include 'Parsedown.php';    
+include 'ParsedownExtra.php';
+
+ini_set('display_errors', '1');
+ini_set('display_startup_errors', '1');
+error_reporting(E_ALL);
+
+class LA{
+    
+    protected $PDE;
+
+    protected $style;
+
+    /* config */
+    protected $Title;
+    protected $ShortTitle;
+    protected $Admin;
+    protected $Password;
+    protected $DisplayName;
+    protected $EMail;
+    protected $SpecialNavigation;
+    protected $SpecialFooter;
+    protected $SpecialFooter2;
+    protected $SpecialPinned;
+    protected $DefaultGallery; 
+    protected $ExpHost;
+    protected $ExpTitle;
+    protected $ExpShortTitle;
+    protected $ExpCaution;
+    protected $ExpIndex;
+    protected $ExpNavigation;
+    protected $ExpFooter;
+    protected $CommentEnabled;
+    
+    protected $Redirect;
+    protected $Translations;
+    protected $CustomTranslationContent;
+    
+    protected $CurrentOffset;
+    protected $PostsPerPage;
+    protected $HotPostCount;
+    
+    protected $LoggedIn;
+    protected $LoginTokens;
+    protected $InExperimentalMode;
+    protected $LanguageAppendix;
+    
+    protected $Posts;
+    protected $Threads; // [ keys: first last displayed count]
+    protected $Images;
+    protected $Galleries;
+    protected $Anchors;
+    
+    protected $Markers;
+    
+    protected $ExtraScripts;
+    
+    protected $NULL_POST;
+    protected $NULL_IMAGE;
+    protected $NULL_Gallery;
+    
+    public $PageType;
+    public $CurrentPostID;
+    
+    function FullURL(){
+        return (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ?
+                "https" : "http") . "://" . $_SERVER['HTTP_HOST'] . 
+                $_SERVER['REQUEST_URI'];
+    }
+    
+    function T($str){
+        if(!$this->LanguageAppendix) return $str;
+        foreach($this->Translations as $entry){
+            if($entry['zh']==$str)
+                return $entry[$this->LanguageAppendix];
+        }
+        return $str;
+    }
+    function SwitchLanguage(){        
+        if(isset($_COOKIE['la_language'])){
+            $this->LanguageAppendix = $_COOKIE['la_language'];
+        }else{
+            if(isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])){
+                $lang = $_SERVER['HTTP_ACCEPT_LANGUAGE'];
+                $lang = substr($lang,0,5);
+                if(preg_match("/zh/i",$lang))$this->LanguageAppendix = 'zh';
+                else $this->LanguageAppendix = 'en';
+            }
+        }
+    }
+    
+    function DisplayRedirectConfig(){
+        $s = "";
+        if(isset($this->Redirect) && isset($this->Redirect[0])) foreach($this->Redirect as $r){
+            if($r['for']=='P'){
+                $s.=("P ".$r['format'].":".$r['target'].";".PHP_EOL);
+            }else if($r['for']=='S'){
+                $s.=("S ".$r['format'].":".$r['domain'].":".$r['target'].";".PHP_EOL);
+            }
+        }
+        return $s;
+    }
+    
+    function DoSiteRedirect(){
+        if(isset($this->Redirect) && isset($this->Redirect[0])) foreach($this->Redirect as $r){
+            if($r['for']=='S'){
+                if(preg_match('/'.$r['format'].'/ui', $_SERVER['HTTP_HOST'])){
+                    if($_SERVER['REQUEST_URI']=='/'||$_SERVER['REQUEST_URI']==''){
+                        header('Location:https://'.$r['domain'].'/index.php?post='.$r['target']); exit;
+                    }else{
+                        header('Location:https://'.$r['domain'].$_SERVER['REQUEST_URI']); exit;
+                    }
+                }
+            }
+        }
+    }
+    
+    function WriteHTACCESS(){
+        $conf = fopen('.htaccess','w');
+        fwrite($conf,"RewriteEngine on".PHP_EOL.PHP_EOL);
+        if(isset($this->Redirect) && isset($this->Redirect[0])) foreach($this->Redirect as $r){
+            if($r['for']=='P'){
+                fwrite($conf,"RewriteRule ^".$r['format'].'$ /index.php?post='.$r['target'].' [R=302,L]'.PHP_EOL.PHP_EOL);
+            }// do site redirect in php.
+        }
+        fwrite($conf, 'RewriteCond %{HTTPS} !=on'.PHP_EOL.
+                      'RewriteCond %{HTTP_HOST} !=localhost'.PHP_EOL.
+                      'RewriteCond %{REQUEST_URI}  !^.*(jpg|png|gif)$'.PHP_EOL.
+                      'RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]'.PHP_EOL.PHP_EOL);
+        fwrite($conf, 'RewriteCond %{HTTP_HOST} !^www\.'.PHP_EOL.
+                      'RewriteCond %{HTTP_HOST} !=localhost'.PHP_EOL.
+                      'RewriteRule ^(.*)$ https://www.%{HTTP_HOST}%{REQUEST_URI} [L,R=301]'.PHP_EOL.PHP_EOL);
+        fwrite($conf,'<Files ~ "\.md$">'.PHP_EOL.'deny from all'.PHP_EOL.'</Files>'.PHP_EOL);
+        fflush($conf);fclose($conf);
+    }
+    
+    function BuildRedirectConfig($conf){
+        $this->Redirect=[];
+        if(preg_match_all('/P\s+(.*)\:\s*([0-9]{14})\s*;/u',$conf,$ma,PREG_SET_ORDER)){
+            foreach($ma as $m){
+                $redirect=[]; $redirect['for'] = 'P'; $redirect['format'] = $m[1]; $redirect['target'] = $m[2];
+                $this->Redirect[]=$redirect;
+            }
+        }
+        if(preg_match_all('/S\s+(\S+)\s*\:\s*(\S+)\s*\:\s*([0-9]{14})\s*;/u',$conf,$ma,PREG_SET_ORDER)){
+            foreach($ma as $m){
+                $redirect=[]; $redirect['for'] = 'S'; $redirect['format'] = $m[1]; $redirect['domain'] = $m[2]; $redirect['target'] = $m[3];
+                $this->Redirect[]=$redirect;
+            }
+        }
+    }
+    
+    function WriteTokens(){
+        $tf = fopen('la_tokens.md','w');
+        if(isset($this->LoginTokens) && sizeof($this->LoginTokens)) {
+             foreach($this->LoginTokens as $t){
+                fwrite($tf,'- '.$t.PHP_EOL);
+            }
+        }
+        fflush($tf);fclose($tf);
+    }
+    
+    function WriteConfig(){
+        if(!isset($this->Title)) $this->Title = $this->T('那么的维基');
+        if(!isset($this->ShortTitle)) $this->ShortTitle = $this->T('基');
+        if(!isset($this->Admin)) $this->Admin = 'admin';
+        if(!isset($this->DisplayName)) $this->DisplayName = $this->T('管理员');
+        if(!isset($this->Password)) $this->Password = password_hash('Admin', PASSWORD_DEFAULT).PHP_EOL;
+        $conf = fopen('la_config.md','w');
+        fwrite($conf,'- Title = '.$this->Title.PHP_EOL);
+        fwrite($conf,'- ShortTitle = '.$this->ShortTitle.PHP_EOL);
+        fwrite($conf,'- Admin = '.$this->Admin.PHP_EOL);
+        fwrite($conf,'- DisplayName = '.$this->DisplayName.PHP_EOL);
+        fwrite($conf,'- Password = '.$this->Password.PHP_EOL);
+        fwrite($conf,'- EMail = '.$this->EMail.PHP_EOL);
+        fwrite($conf,'- SpecialNavigation = '.$this->SpecialNavigation.PHP_EOL);
+        fwrite($conf,'- SpecialFooter = '.$this->SpecialFooter.PHP_EOL);
+        fwrite($conf,'- SpecialFooter2 = '.$this->SpecialFooter2.PHP_EOL);
+        fwrite($conf,'- SpecialPinned = '.$this->SpecialPinned.PHP_EOL);
+        fwrite($conf,'- DefaultGallery = '.$this->DefaultGallery.PHP_EOL);
+        fwrite($conf,'- CommentEnabled = '.($this->CommentEnabled?"True":"False").PHP_EOL);
+        fwrite($conf,'- ExpHost = '.$this->ExpHost.PHP_EOL);
+        fwrite($conf,'- ExpTitle = '.$this->ExpTitle.PHP_EOL);
+        fwrite($conf,'- ExpShortTitle = '.$this->ExpShortTitle.PHP_EOL);
+        fwrite($conf,'- ExpCaution = '.$this->ExpCaution.PHP_EOL);
+        fwrite($conf,'- ExpIndex = '.$this->ExpIndex.PHP_EOL);
+        fwrite($conf,'- ExpNavigation = '.$this->ExpNavigation.PHP_EOL);
+        fwrite($conf,'- ExpFooter = '.$this->ExpFooter.PHP_EOL);
+        fflush($conf);fclose($conf);
+        $conf = fopen('la_redirect.md','w');
+        fwrite($conf,$this->DisplayRedirectConfig());fflush($conf);fclose($conf);
+        $this->WriteHTACCESS();
+        $this->WriteTokens();
+    }
+    
+    function Install(){
+        if(!file_exists('la_config.md')){
+            $this->WriteConfig();
+        }
+        if(!is_dir('posts')) mkdir('posts');
+        if(!is_dir('images')) mkdir('images');
+        if(!is_dir('images/thumb')) mkdir('images/thumb');
+        if(!is_dir('styles')) mkdir('styles');
+        
+        $this->WriteStyles();
+        $this->WriteHTACCESS();
+    }
+    
+    function ReadConfig(){
+        if(!file_exists('la_config.md')){
+            $this->Install();
+        }
+        $c = file_get_contents('la_config.md');
+        if(preg_match('/-\s*Title\s*=\s*(\S+)\s*$/um', $c, $m)) $this->Title = $m[1]; else $this->Title=$this->T("那么的维基");
+        if(preg_match('/-\s*ShortTitle\s*=\s*(\S+)\s*$/um', $c, $m)) $this->ShortTitle = $m[1]; else $this->ShortTitle=$this->T("基");
+        if(preg_match('/-\s*Admin\s*=\s*(\S+)\s*$/um', $c, $m)) $this->Admin = $m[1];
+        if(preg_match('/-\s*Password\s*=\s*(\S+)\s*$/um', $c, $m)) $this->Password = $m[1];
+        if(preg_match('/-\s*DisplayName\s*=\s*(\S+)\s*$/um', $c, $m)) $this->DisplayName = $m[1];
+        if(preg_match('/-\s*EMail\s*=\s*(\S+)\s*$/um', $c, $m)) $this->EMail = $m[1];
+        if(preg_match('/-\s*SpecialNavigation\s*=\s*(\S+)\s*$/um', $c, $m)) $this->SpecialNavigation = $m[1];
+        if(preg_match('/-\s*SpecialFooter\s*=\s*(\S+)\s*$/um', $c, $m)) $this->SpecialFooter = $m[1];
+        if(preg_match('/-\s*SpecialFooter2\s*=\s*(\S+)\s*$/um', $c, $m)) $this->SpecialFooter2 = $m[1];
+        if(preg_match('/-\s*SpecialPinned\s*=\s*(\S+)\s*$/um', $c, $m)) $this->SpecialPinned = $m[1];
+        if(preg_match('/-\s*DefaultGallery\s*=\s*(\S+)\s*$/um', $c, $m)) $this->DefaultGallery = $m[1];
+        if(preg_match('/-\s*CommentEnabled\s*=\s*(\S+)\s*$/um', $c, $m)) $this->CommentEnabled = ($m[1]=="True");
+        if(preg_match('/-\s*ExpHost\s*=\s*(\S+)\s*$/um', $c, $m)) $this->ExpHost = $m[1];
+        if(preg_match('/-\s*ExpTitle\s*=\s*(\S+)\s*$/um', $c, $m)) $this->ExpTitle = $m[1]; else $this->ExpTitle=$this->T("实验访问");
+        if(preg_match('/-\s*ExpShortTitle\s*=\s*(\S+)\s*$/um', $c, $m)) $this->ExpShortTitle = $m[1]; else 
+                                                                        $this->ExpShortTitle = $this->ExpTitle;
+        if(preg_match('/-\s*ExpCaution\s*=\s*(\S+)\s*$/um', $c, $m)) $this->ExpCaution = $m[1]; 
+        if(preg_match('/-\s*ExpIndex\s*=\s*(\S+)\s*$/um', $c, $m)) $this->ExpIndex = $m[1]; 
+        if(preg_match('/-\s*ExpNavigation\s*=\s*(\S+)\s*$/um', $c, $m)) $this->ExpNavigation = $m[1];
+        if(preg_match('/-\s*ExpFooter\s*=\s*(\S+)\s*$/um', $c, $m)) $this->ExpFooter = $m[1];
+        if(file_exists('la_redirect.md')){
+            $c = file_get_contents('la_redirect.md');
+            $this->BuildRedirectConfig($c);
+        }
+        $this->Translations=[];
+        if(file_exists("translations.md")){
+            $c = file_get_contents('translations.md');
+            if(preg_match_all('/-\s+(\S.*)\s*\|\s*(\S.*)$/um',$c, $ma, PREG_SET_ORDER)) foreach($ma as $m){
+                $entry = []; $entry['zh'] = trim($m[1]); $entry['en'] = trim($m[2]);
+                $this->Translations[] = $entry;
+            }
+        }
+        if(file_exists("custom_translations.md")){
+            $this->CustomTranslationContent = file_get_contents('custom_translations.md');
+            if(preg_match_all('/-\s+(\S.*)\s*\|\s*(\S.*)$/um',$this->CustomTranslationContent, $ma, PREG_SET_ORDER)) foreach($ma as $m){
+                $entry = []; $entry['zh'] = trim($m[1]); $entry['en'] = trim($m[2]);
+                $this->Translations[] = $entry;
+            }
+        }
+        $this->LoginTokens=[];
+        if(file_exists('la_tokens.md')){
+            $c = file_get_contents('la_tokens.md');
+            if(preg_match_all('/-\s+(\S.*)\s*$/um',$c, $ma, PREG_SET_ORDER)) foreach($ma as $m){
+                $this->LoginTokens[] = $m[1];
+            }
+        }
+    }
+    
+    function __construct() {
+        $this->ReadConfig();
+        $this->PDE = new ParsedownExtra();
+        $this->PDE->SetInterlinkPath('/');
+        $this->Posts = [];
+        $this->Threads = [];
+        
+        $this->Markers=['●', '○', '✓', '×', '!', 'P', 'E', 'S'];
+        
+        $this->PostsPerPage = 40;
+        $this->CommentsPerPage = 100;
+        $this->HotPostCount = 15;
+    }
+    
+    function DoLogout(){
+        $this->LoggedIn = false;
+        unset($_SESSION['user_id']);
+        $this->RecordToken(true);
+    }
+    
+    function RecordToken($unset_current=false){
+        if(isset($unset_current) && isset($_COOKIE['la_token'])){
+            $t = $_COOKIE['la_token'];
+            setcookie('la_token', null, -1, '/'); unset($_COOKIE['la_token']);
+            if (($key = array_search($t,$this->LoginTokens)) !== false) { unset($this->LoginTokens[$key]); }
+            $this->WriteTokens();
+            return null;
+        }else{
+            $t = uniqid('la_',true);
+            setcookie('la_token',$t,time()+3600*24*7); $_COOKIE['la_token'] = $t;
+            $this->LoginTokens[] = $t;
+            $this->WriteTokens();
+            return $t;
+        }
+    }
+    
+    function LoginThroughToken(){
+        if(!isset($_COOKIE['la_token'])) return false;
+        $t = $_COOKIE['la_token'];
+        if (($key = array_search($t,$this->LoginTokens)) !== false) {
+            $_SESSION['user_id']=$this->Admin;
+            $this->LoggedIn = true;
+            setcookie('la_token',$t,time()+3600*24*7);
+            return true;
+        }
+        return false;
+    }
+    
+    function DoLogin(){
+        session_start();
+        $redirect=false;
+        if(isset($_GET['logout'])){ $this->DoLogout(); header('Location:index.php'); }
+        else if(!isset($_SESSION['user_id'])){
+            if(isset($_POST['login_button'])){
+                $id = trim($_POST['login_id']);
+                $pwd = trim($_POST['login_password']);
+                if(strtolower($this->Admin)==strtolower($id)&&password_verify($pwd, $this->Password)){
+                    $_SESSION['user_id']=$id;
+                    $this->RecordToken(false);
+                }
+                $redirect = true;
+            }else if($this->LoginThroughToken()){
+                // nothing;
+            }
+        }else{
+            if(strtolower($_SESSION['user_id']) == strtolower($this->Admin)){ $this->LoggedIn = true; }
+            else{ $this->DoLogout();}
+        }
+        if($redirect){
+            header('Location:index.php'.(isset($_GET['post'])?("?post=".$_GET['post']):"")
+                                       .(isset($_GET['settings'])?"?settings=true":""));
+        }
+    }
+    
+    function WriteStyles(){
+        $this->style="
+html{font-size:18px;font-family:'Noto Serif CJK SC','Times New Roman','SimSun', Georgia, serif;}
+body{background-color:%white%;color:%black%;}
+sup,sub{line-height:0;}
+blockquote{border-left:2px solid %black%;padding-left:0.3em;}
+*{box-sizing:border-box;padding:0;margin:0;}
+.page,.page_gallery{padding:1em;padding-top:0;}
+.hidden_on_desktop,.hidden_on_wide{display:none;}
+.hidden_on_desktop_force{display:none !important;}
+::file-selector-button{background:none;border:none;}
+a,button,::file-selector-button{text-decoration:underline;color:%black%;}
+a:hover,.button:hover,::file-selector-button:hover{text-decoration:none;color:%gray%;}
+.button:disabled{background-color:%gray%;pointer-events:none;}
+header{position:sticky;top:0;background-color:%white%;z-index:10;padding-top:0.5em;max-height:100vh;overflow:auto;z-index:30;}
+.header_nav{display:inline;}
+header a,.left a,.footer a,.clean_a,.clean_a a{text-decoration:none;}
+header a:hover,.button:hover{color:%gray% !important;}
+.footer{background-color:%white%;z-index:10;position:relative;}
+.invert_a,.invert_a a{color:%gray%;text-decoration:none;}
+.invert_a:hover,.invert_a a:hover{color:%black% !important;}
+.gray,.gray a{color:%gray%;}
+hr{border:1px solid %gray%;}
+header ul{display:inline-block;}
+header li{display:inline-block;}
+header li::before{content:' - '}
+header h1,header h2,header h3,header h4,header h5,header p{display:inline;font-size:1rem;}
+.main{position:relative;word-spacing:-1em;}
+.main *{word-spacing:initial;}
+pre{overflow:auto;max-width:100%;display:block;font-size:0.75em;}
+ul{display:block;}
+li{display:block;}
+table{width:100%;border-collapse:collapse;border-bottom:2px solid %black%;border-top:3px solid %black%;}
+table input{border:none!important;}
+table img{max-width:10rem !important;}
+td{padding-left:0.1em;padding-right:0.1em;}
+td:first-child{padding-left:0;}
+td:last-child{padding-right:0;}
+tbody tr:hover{box-shadow:inset 0 -2px 0 0px %black%;}
+thead{box-shadow:inset 0 -1px 0 0px %black%;position:sticky;top:2rem;background-color:%white%;z-index:5;}
+.post table{font-size:0.85em;}
+.interesting_tbody{background:linear-gradient(90deg, %white%ff, %white%88 20em);}
+.interesting_tbody td{display:contents;}
+.interesting_tbody tr{position:relative;scroll-margin:3.5em}
+.interesting_tbody td>*{display:table-cell;}
+.interesting_tbody .post_access{padding-top:0;}
+.interesting_tbody .post_menu_button{top:0;opacity:0;}
+.interesting_tbody td>img{position:absolute;left:1.4em;z-index:-1;height:1em;width:20em;
+display:block;top:0.2em;object-fit:cover;max-width:calc(100% - 1.4em) !important;}
+.interesting_tbody .p_row{display:flex;position:absolute;left:1.4em;top:0;z-index:-1;flex-wrap:nowrap;max-width:calc(100% - 1.4em);}
+.interesting_tbody .p_thumb{height:1em;}
+.interesting_tbody .p_thumb img{max-height:10rem !important;max-width:20rem !important;}
+tr:hover .post_menu_button{opacity:1;}
+.post_current_row{background-color:%lightopbkg%;}
+.align_right{text-align:right;}
+.left{display:inline-block;vertical-align:top;width:25%;height:calc(100vh - 5.2em);top:2em;
+position:sticky;overflow:auto;padding-right:0.2em;padding-bottom:4rem;}
+.center{display:inline-block;vertical-align:top;width:50%;padding-left:0.3em;overflow:visible;padding-bottom:4rem;}
+.center_wide{display:inline-block;vertical-align:top;width:75%;padding-left:0.3em;overflow:visible;padding-bottom:4rem;}
+.center_full{display:inline-block;vertical-align:top;width:100%;overflow:visible;padding-bottom:4rem;}
+.center_wide .p_thumb{height:10rem;}
+.sticky_title{position:sticky;top:1.35em;z-index:1;box-shadow:6em 3.5em 0.75em -3em inset %white%;pointer-events:none;}
+.center_exp{display:block;width:80%;margin:0 auto;overflow:visible;padding-bottom:1em;}
+.table_top{position:relative;left:calc(-50% - 0.45em);width:calc(200% + 0.6em);background:%white%;z-index:1;
+box-shadow:0px 0px 2em 1em %white%;margin-top:2em;margin-bottom:2em;}
+.right{display:inline-block;vertical-align:top;width:25%;position:sticky;top:2em;
+padding-left:0.5em;height:calc(100vh - 2.6em);overflow:auto;padding-bottom:4rem;}
+textarea,input[type=text],input[type=password]{width:100%;display:block;font-family:inherit;max-height:60vh;font-size:inherit;}
+select,textarea,input[type=text],input[type=password]{background:none;border:none;border-bottom:1px solid %black%;color:%black%;}
+.button{background:none;border:none;font-family:inherit;color:%black%;font-size:inherit;font-weight:bold;}
+.post{position:relative;scroll-margin:2.5em;border-radius:0.3em;
+padding-right:0rem;padding-left:0rem;padding-top:0.3rem;padding-bottom:0.3rem;margin-top:0.2em;margin-bottom:0.2em;}
+.center_exp .post{padding-left:0;padding-right:0;padding-top:0;padding-bottom:0;border-radius:0;}
+.post_width li,.post_width_big li,.footer_additional li,.footer_additional li,.post_dummy li
+{display:list-item;margin-left:1em;list-style:disc;}
+.post_width li li,.post_width_big li li,.footer_additional li li,.footer_additional li li,.post_dummy li li{list-style:circle;}
+.post_width > *,.post_width_big > *,.post_dummy > *,.post_ref > *{margin:0;margin-bottom:0.5em}
+.post_width > *:last-child,.post_width_big > *:last-child,.post_dummy > *:last-child,.post_ref > *:last-child{margin-bottom:0em;}
+.post_dummy > *{width:60%;margin:0 auto;margin-bottom:0.5em}
+.post_dummy > p img{display:block;width:100%;margin:0 auto;}
+.post h1,.post h2,.post h3,.post h4{margin-bottom:0.5rem;}
+.gallery_left li{display:list-item;margin-left:1em;list-style:none;}
+.gallery_left .selected{list-style:'→';}
+.focused_post{font-size:1.2em;margin-top:0.1em;margin-bottom:0.1em;padding:0.5rem !important;border:2px dashed #ac7843;}
+.post_width{position:relative;left:1.4rem;width:calc(100% - 1.7rem);padding-left:0.2em;overflow:visible;}
+.post_width_big{position:relative;left:0;width:100%;overflow:visible;}
+.post .post{padding:0;padding-top:0.3rem;}
+.post_menu_button{position:absolute;display:none;right:0rem;width:1.5rem;
+text-align:center;border-radius:0.3em;user-select:none;cursor:pointer;z-index:10;}
+.pointer{cursor:pointer;}
+.post:hover .post_menu_button{display:block;}
+.pop_menu{position:absolute;top:0.3rem;z-index:95;background-color:%lighterbkg%;
+padding:0.3em;right:0.3rem;text-align:right;border-radius:0.3em;font-size:1rem;
+box-shadow:0px 0px 10px rgb(0, 0, 0);}
+.pop_menu li{list-style:none;margin-left:0;}
+.pop_menu hr{border:2px solid rgba(0,0,0,0.1);}
+.toc{left:60%;width:40%;top:0;position:absolute;}
+.post_access{width:1.4rem;top:0;position:absolute;height:100%;text-align:center;
+font-weight:bold;border-right:2px solid transparent;padding-top:0.3rem;}
+.post_access:hover{background-color:%lightopbkg%;border-top-left-radius:0.3em;border-bottom-left-radius:0.3em;
+border-right:2px solid %black% !important;}
+.paa{width:1.4rem;min-width:1.4rem;}
+.opt_compact .post_access,.ref_compact .post_access{padding-top:0.2rem;border-right:2px solid %gray%;}
+.post_box{border:1px solid %gray%;border-radius:0.3em;padding:0.3em;}
+.post_box:hover,.post_menu_button:hover{background-color:%lightopbkg%}
+#big_image_info .post_box:hover{background-color:%graybkg%;}
+.post_preview{font-size:0.9rem;overflow:hidden;}
+.post .post_ref{margin:0;padding-left:1.7rem;}
+.post_ref_main{display:inline-block;vertical-align:top;}
+.post_preview .post_ref_main{max-height:6rem;overflow:hidden;}
+.post_ref_images{overflow:hidden;}
+.page_selector{padding-top:2rem;text-align:center;}
+.smaller{font-size:0.85em;}
+.bigger{font-size:1.3em;}
+.block{display:block;}
+.opt_compact,.ref_compact{margin-top:0;}
+.opt_compact{margin-left:1.6rem;}
+.opt_compact .post_width {margin-left:0.3em;width: calc(100% - 1.8rem);}
+.post_box_top{padding-bottom:0.3em;padding-top:0.3em;}
+.post_box_fixed_bottom{position:sticky;bottom:0em;background-color:%white%;z-index:5;}
+.spacer{height:0.5em;}
+.pop_right,.pop_right_big{position:fixed;top:0;right:0;bottom:0;width:30%;z-index:100;background-color:%graybkg%;display:none;
+transition-timing-function:ease-out;padding:1rem;overflow:auto;}
+@keyframes pop_slide_in{0%{right:-30%;}100%{right:0%;}}
+@keyframes pop_slide_out{0%{right:0%;}100%{right:-30%;}}
+@keyframes pop_slide_in_big{0%{right:-30%;}100%{right:0%;}}
+@keyframes pop_slide_out_big{0%{right:0%;}100%{right:-30%;}}
+.backdrop{position:fixed;top:0;right:0;bottom:0;left:0;background-color:rgba(0,0,0,0.5);transition-timing-function:ease-out;z-index:90;}
+@keyframes backdrop_fade_in{0%{opacity:0%;}100%{opacity:100%;}}
+@keyframes backdrop_fade_out{0%{opacity:100%;}100%{opacity:0%;}}
+.toc_entry_1{font-size:1.1em;}
+.toc_entry_2{font-size:1.0em;padding-left:0.5rem;}
+.toc_entry_3{font-size:0.9em;padding-left:1rem;}
+.toc_entry_4{font-size:0.85em;padding-left:1.5rem;}
+.toc_entry_5{font-size:0.8em;padding-left:2rem;}
+h1,h2,h3,h4,h5{scroll-margin:1.5em;}
+{display:inline}
+.left ul h1,.left ul h2,.left ul h3,.left ul h4,.left ul h5,.left ul p
+{font-size:1em;}
+.deleted_post{color:%gray%;text-decoration:line-through;}
+#file_list{margin-top:0.5em;}
+.file_thumb img{max-height:100%;max-width:100%;object-fit:cover;min-width:100%;min-height:100%;}
+#file_list li{margin-bottom:0.3em;}
+.ref_thumb{white-space:nowrap;overflow:hidden;}
+.ref_thumb .file_thumb{width:3em;height:3em;}
+.side_thumb li{margin:0.4em;display:inline-block;}
+.file_thumb{width:4em;height:4em;display:inline-block;line-height:0;vertical-align:middle;overflow:hidden;}
+.p_row{display:flex;flex-wrap:wrap;}
+.p_thumb{display:flex;flex-grow:1;height:6rem;margin-right:0.25rem;margin-bottom:0.25rem;overflow:hidden;position:relative;}
+.p_thumb img{object-fit:cover;max-height:100%;min-width:100%;}
+.ref_count,.p_thumb .post_menu_button{text-shadow: 0px 0px 10px rgb(0, 0, 0);}
+.p_thumb:hover .post_menu_button{display:block;}
+.p_thumb_selected{color:%black% !important;}
+.p_thumb_selected{display:block;}
+.post .p_thumb img{max-height:6rem;}
+.big_image_box{position:fixed;top:0;bottom:0;left:0;width:75%;z-index:95;text-align:center;pointer-events:none;}
+.big_image_box *{pointer-events:auto;}
+.big_image_box img{position:absolute;margin:auto;top:0;left:0;right:0;bottom:0;cursor:unset;}
+.big_side_box{position:fixed;top:0;bottom:0;right:0;width:25%;overflow:auto;z-index:98;color:%black%;padding:1rem;
+background:linear-gradient(to right, rgba(0,0,0,0), rgb(1, 1, 1));transition:background-size .2s linear;background-size: 300% 100%;}
+.big_side_box:hover{background-size: 100% 100%;}
+.big_side_box a,.big_side_box hr,#dropping_background{color:%black%;}
+.big_side_box a:hover{color:%gray%;}
+#dropping_background{background-color:rgba(0,0,0,0.4);position:fixed;top:0;right:0;bottom:0;left:0;z-index:100;text-align:center;
+box-shadow:0px 0px 500px black inset;display:flex;align-items:center;}
+img{cursor:pointer;max-height:100%;max-width:100%;}
+.post img{max-height:min(70vh, 20rem);max-width:min(100%, 20rem);}
+.post > a > img{display:block;margin:0.3em auto;}
+.post .original_img{max-width:100%;display:block;margin-left:auto;margin-right:auto;max-width:100%;max-height:90vh;}
+.original_img img{max-height:90vh;max-width:100%;}
+.p_row .original_img{margin-bottom:0;}
+.post_ref .original_img{margin:unset;max-width:unset;max-height:min(70vh, 20rem);max-width:min(100%, 20rem);}
+.b ul{font-size:1.4em;}
+no_pop{cursor:unset;}
+p{min-height:0.8em;}
+.bold{font-weight:bold;}
+.footer_additional{display:inline-block;width:50%;vertical-align:text-top;white-space:normal;}
+.small_footer{position:sticky;bottom:0em;background-color:%white%;padding-bottom:0.5em;z-index:10;overflow:auto;white-space:nowrap;}
+.top_post_hint{margin-left:1.5em;font-weight:bold;}
+.white{color:%white%;}
+.full_box{border:1px solid %black% !important;padding:0.3rem;overflow:auto;}
+.image_nav_prev,.image_nav_next{z-index:100;position:absolute;line-height:0;height:100%;width:20%;display:flex;align-items:center;
+transition:background-size .2s ease;padding:0.5em;text-shadow:0px 0px 5px black;user-select:none;pointer-events:auto;}
+.image_nav_prev{left:0;justify-content:left;background:linear-gradient(to left, rgba(0,0,0,0), rgba(0,0,0,0.2));
+background-repeat:no-repeat;background-size:0% 100%;}
+.image_nav_prev:hover,.image_nav_next:hover{background-size:100% 100%;}
+.image_nav_next{right:0;justify-content:right;background:linear-gradient(to right, rgba(0,0,0,0), rgba(0,0,0,0.2));
+background-repeat:no-repeat;background-size:0% 100%;transition:background-size .2s ease;background-position-x:100%;}
+.inquiry_buttons{position:fixed;left:0;right:25%;text-align:center;bottom:1em;margin:0 auto;width:max-content;
+background-color:rgba(0,0,0,0.5);z-index:110;padding:0.2em;padding-left:1em;padding-right:1em;
+border-radius:1em;box-shadow:0px 0px 5px;text-shadow:0px 0px 5px black;opacity:1;user-select:none;}
+.lr_buttons{background-color:rgba(0,0,0,0.5);padding:0.5em;padding-top:1em;padding-bottom:1em;
+border-radius:1em;box-shadow:0px 0px 5px;font-size:1.2rem;text-shadow:0px 0px 5px black;}
+.img_btn_hidden{opacity:0;transition:opacity 0.2s;}
+.special_alipay{background-color:#027aff;color:white;white-space:nowrap;
+font-family:sans-serif;font-weight:bold;border-radius:0.7em;font-size:0.75em;padding:0.25em;}
+.special_paypal{background-color:white;color:#253b80;white-space:nowrap;
+font-family:sans-serif;font-weight:bold;border-radius:2em;font-size:0.75em;
+padding:0.25em;padding-left:0.5em;padding-right:0.65em;font-style: italic;}
+.special_paypal_inner{color:#169bd7;}
+#waiting_bar{position:fixed;z-index:200;top:0;left:0;right:0;height:0.2em;background-color:%black%;transform:translate(-100%,0);
+animation:anim_loading 1s linear infinite;}
+@keyframes anim_loading{0%{transform:translate(-100%,0);} 100%{transform:translate(100%,0);}}
+.product_ref{width:32%;padding:0.2em!important;display:inline-block;text-align:center;vertical-align:top;margin-bottom:0.8em;}
+.product_thumb{max-height:11em;max-width:11em;display:inline-flex;margin-bottom:0.2em;background-color:%graybkg%;}
+.product_thumb img{box-shadow:none;object-fit:contain;max-height:unset;max-width:unset;width:100%;margin:0 auto !important;}
+.product_ref p{margin-bottom:0.2em;text-align:left;}
+.post_preview .product_thumb{max-height:4em;max-width:6em;}
+.purchase_button{background-color:%black%;color:%white%;padding-left:0.5em;padding-right:0.5em;text-decoration:none;font-weight:bold;}
+.page_break{page-break-after:always;}
+.text_highlight,.text_highlight a{background-color:%black%;color:%white%;}
+.gray.text_highlight,.gray.text_highlight a{background-color:%gray%;color:%white%;}
+.print_title{display:none;}
+.show_on_print{display:none;}
+.comment{font-size:0.9em;font-family:sans-serif;overflow:auto !important;width:100%;}
+.comment tbody tr:hover{box-shadow:none;}
+.comment table{border:none;}
+.comment li{display:list-item;list-style:'→';padding-left:0.3em;}
+.comment ul{padding-left:1em;}
+.comment ul li *{margin-bottom:0.5em;}
+
+@media screen and (max-width:1000px){
+.left{width:35%;}
+.center,.center_wide{width:65%;}
+.center_wide .p_thumb{height:8rem;}
+.right{display:none;}
+.post_width{width:calc(100% - 1.5rem);padding-left:0.2em;}
+.post_width_big{left:0;width:100%;}
+.hidden_on_wide{display:unset;}
+.hidden_on_narrow{display:none;}
+.pop_right{width:30%;}
+.pop_right_big{width:40%;}
+@keyframes pop_slide_in{0%{right:-30%;}100%{right:0%;}}
+@keyframes pop_slide_out{0%{right:0%;}100%{right:-30%;}}
+@keyframes pop_slide_in_big{0%{right:-40%;}100%{right:0%;}}
+@keyframes pop_slide_out_big{0%{right:0%;}100%{right:-40%;}}
+.big_side_box{width:35%;}
+.big_image_box{width:65%;}
+.inquiry_buttons{right:35%;}
+.table_top{left:calc(-50% - 1.7em);width: calc(154% + 0.5em);}
+.center_exp{display:block;width:100%;margin:0 auto;overflow:none;padding-bottom:1em;}
+.center_exp .post{overflow:auto;}
+}
+
+@media screen and (max-width:666px){
+html{font-size:16px;}
+.hidden_on_mobile{display:none !important;}
+.block_on_mobile{display:block !important;}
+.hidden_on_desktop{display:unset;}
+header ul{display:block;}
+header li{display:block;}
+header li::before{content:''}
+.left{position:relative;width:100%;position:relative;top:unset;height:unset;min-height:80vh;padding-right:0;display:block;}
+.center,.center_wide,.center_full{position:relative;left:0;top:0;width:100%;padding-left:0;display:block;}
+.center_wide .p_thumb{height:6rem;}
+.pop_right,.pop_right_big{top:unset;right:0;bottom:0;left:0;width:100%;}
+.pop_right{height:30%;}
+.pop_right_big{height:70%;}
+@keyframes pop_slide_in{0%{bottom:-30%;}100%{bottom:0%;}}
+@keyframes pop_slide_out{0%{bottom:0%;}100%{bottom:-30%;}}
+@keyframes pop_slide_in_big{0%{bottom:-70%;}100%{bottom:0%;}}
+@keyframes pop_slide_out_big{0%{bottom:0%;}100%{bottom:-70%;}}
+.big_image_box{position:fixed;top:0;bottom:8.5rem;left:0;right:0;width:100%;}
+.side_box_mobile_inner{background:linear-gradient(to bottom, rgba(0,0,0,0), rgba(1,1,1,0.9) 20%);
+transition:none;background-size:100% 100%;padding:0.5rem;padding-bottom: 5em;}
+.side_box_mobile_inner:hover{background-size:100% 100%;}
+.big_side_box{position:fixed;top:0;bottom:0;right:0;left:0;width:100%;
+height:unset;padding:0;padding-top:calc(100vh - 8.5rem);background:none;}
+.p_thumb{height:3rem;}
+.center .post{padding-right:0rem;padding-left:0rem;}
+.post{padding-right:0.3rem;padding-left:0.3rem;}
+.post .p_thumb img{max-height:3rem;}
+.page,.page_gallery{padding:0.2em;padding-top:0;}
+header{padding-top:0.3em;}
+.small_footer{padding-bottom:0.3em;}
+.footer_additional{display:block;width:100%;}
+.album_hint{display:block;font-size:1rem;}
+.image_nav{position:absolute !important;}
+.image_nav_prev,.image_nav_next{width:25%;}
+.image_nav_prev:hover,.image_nav_next:hover{background-size:0% 100%;color:%black% !important;}
+.inquiry_buttons{position:relative;left:unset;right:unset;text-align:left;bottom:unset;margin:unset;width:unset;
+background-color:unset;z-index:unset;padding:unset;padding-left:unset;padding-right:unset;
+border-radius:unset;box-shadow:unset;text-shadow:unset;}.img_btn_hidden{opacity:1;}
+.lr_buttons{background-color:unset;padding:unset;padding-top:unset;padding-bottom:unset;
+border-radius:unset;box-shadow:unset;font-size:1.3rem;text-shadow:unset;}
+.opt_compact,.ref_compact{line-break:anywhere;}
+.post_width,.post_width_big{overflow:auto;}
+.table_top{left:unset;width:100%;overflow:auto;}
+table img{max-width:30vw !important;}
+.product_ref{width:100%;display:block;}
+.post_dummy > *{width:100%;max-width:25rem;}
+.sticky_title{top:1.2em;}
+#upload_selector{width:100%;}
+.focused_post{padding:0.3rem !important;}
+.interesting_tbody{background:linear-gradient(90deg, %white%ff, %white%88 10em);}
+}
+
+@media print{
+body,footer,header,.small_footer,a,.clean_a,.invert_a,.clean_a a,.invert_a a{background:none;color:black;}
+table{border-bottom:2px solid black;border-top:2px solid black;}
+table img{max-width:5em;max-width:8em !important;max-height:8em !important;}
+thead{box-shadow:inset 0 -1px 0 0px black;background:none;}
+.post,.focused_post{padding:0 !important;margin-top:0.3em;margin-bottom:0.5em;}
+.post_width,.post_width_big{overflow:hidden;left:0;width:100%;padding-left:0em;}
+.post h1,.post h2,.post h3,.post h4{margin-top:0.5rem;}
+.gray,.gray a,.deleted_post{color:rgba(0,0,0,0.5);}
+.left,.right{display:none;}
+.center, .center_wide, .center_full{width:100%;padding:0;display:block;font-size:16px;line-height:1.3}
+hr{border:1px solid black;}
+.post_box_top{display:none;}
+.opt_compact .post_access,.ref_compact .post_access{border-right:none;display:inline;}
+.text_highlight,.text_highlight a,.gray.text_highlight,.gray.text_highlight a,.purchase_button{background-color:lightgray;color:black;}
+.focused_post{border:none;font-size:1em;}
+.hidden_on_print{display:none;}
+.print_column{column-count:2;margin-top:0.5rem;margin-bottom:0.5rem;}
+.post_access{display:none;}
+.opt_compact{margin-left:0;}
+.opt_compact .post_width{left:1.4rem;width:calc(100% - 1.7rem);padding-left:0.2em;}
+.print_title{column-span:all;display:block;margin-top:2em;margin-bottom:0.5rem;font-size:1.2em;}
+.print_title:first-of-type{margin-top:1em;}
+.print_title+.post h1:first-of-type{display:none;}
+.opt_compact h1:first-of-type,.ref_compact h1:first-of-type{display:unset;}
+.table_top{position:relative;left:0;width:100%;background:none;z-index:1;box-shadow:none;margin-top:0.2em;margin-bottom:0.2em;}
+.header_nav{display:none;}
+.show_on_print{display:block;}
+blockquote{border-left:2px solid black;}
+.footer_additional{display:none;}
+.small_footer{margin-top:1rem;}
+.page_selector{display:none;}
+.p_thumb{height:4rem;}
+.post .p_thumb img{max-height:4rem;}
+.sticky_title{box-shadow:none;}
+.center_wide .p_thumb{display:inline-flex;height:5.8rem;width:5.8rem;margin-right:0;}
+.center_wide .p_row{display:block;}
+}
+";
+        $this->style=preg_replace('/%white%/','#231a0d',$this->style);
+        $this->style=preg_replace('/%black%/','#f8ca9b',$this->style);
+        $this->style=preg_replace('/%gray%/','#ac7843',$this->style);
+        $this->style=preg_replace('/%graybkg%/','#39270e',$this->style);
+        $this->style=preg_replace('/%lightopbkg%/','#daae8010',$this->style);
+        $this->style=preg_replace('/%lighterbkg%/','#675340',$this->style);
+        $this->style=preg_replace('/%focusedbkg%/','#482f0c',$this->style);
+        $f = fopen('styles/main.css','w');
+        fwrite($f,$this->style);
+        fclose($f);
+    }
+    
+    function GiveSafeEMail(){
+        return preg_replace('/\./u','[dot]',preg_replace('/\@/u','[at]',$this->EMail));
+    }
+    
+    function &FindImage($name){
+        if(isset($this->Images[0]))foreach($this->Images as &$im){
+            if($im['name']==$name) return $im;
+        }
+        return $this->NULL_IMAGE;
+    }
+    
+    function ReadImages($clear_non_exist = false){
+        $path = 'images/list.md';
+        if(!file_exists($path)){ $f = fopen($path,'w'); fflush($f); fclose($f); }
+        $c = file_get_contents($path);
+        if(preg_match_all('/GALLERY\s+(\S+)(.*)$/mu', $c, $ma, PREG_SET_ORDER)) foreach($ma as $m){
+            $g=[]; $g['name']=$m[1];//$g['count']=0;
+            if(preg_match('/FEATURED([^;]*?);/u', $m[2], $arg)){ $g['featured']=true; }
+            if(preg_match('/EXPERIMENTAL([^;]*?);/u', $m[2], $arg)){ $g['experimental']=true; }
+            $this->Galleries[] = $g;
+        }
+        if(preg_match_all('/^-\s*([^;]+)\s*?;\s*?(.*)$/mu', $c, $ma, PREG_SET_ORDER)) foreach($ma as $m){
+            $name = trim($m[1]);
+            $item = []; $item['file'] = 'images/'.$name; $item['name'] = $name;
+            if(file_exists('images/thumb/'.$name)){$item['thumb']='images/thumb/'.$name;}else{$item['thumb']='images/'.$name;}
+            if(preg_match('/REFS\s+([^;]*);/u',$m[2],$refs) && preg_match_all('/[0-9]{14}/u',$refs[1],$rs, PREG_SET_ORDER)){
+                $item['refs']=[];
+                foreach($rs as $r){ if(!in_array($r[0], $item['refs'])) $item['refs'][] = $r[0]; }
+            }
+            if(preg_match('/GAL\s+([^;]*);/u',$m[2],$gals) && preg_match_all('/(\S+)/u',$gals[1],$ga, PREG_SET_ORDER)){
+                $item['galleries']=[];
+                foreach($ga as $g){ if(!in_array($g[0], $item['galleries'])) $item['galleries'][] = $g[0]; }
+            }
+            if(preg_match('/PRODUCT\s+([^;]*);/u',$m[2],$product)){
+                $item['product']=$product[1];
+            }
+            $this->Images[] = $item;
+        }
+        
+        $files = array_merge([],glob('images/*.jpg'));
+        $files = array_merge($files,glob('images/*.jpeg'));
+        $files = array_merge($files,glob('images/*.png'));
+        $files = array_merge($files,glob('images/*.gif'));
+        if(isset($files[0]))foreach($files as $file) {
+            if(preg_match('/[0-9]{14,}\.(jpg|jpeg|gif|png)/u', $file, $m)) {
+                $name = trim($m[0]);
+                if(!$this->FindImage($name)){
+                    $item = []; $item['name']=$name; $item['file'] = 'images/'.$name;
+                    if(file_exists('images/thumb/'.$name)){$item['thumb']='images/thumb/'.$name;}else{$item['thumb']='images/'.$name;}
+                    $this->Images[] = $item;
+                }
+            }
+        }
+        if($clear_non_exist){
+            if(isset($this->Images[0]) && isset($files[0])){
+                foreach($this->Images as &$im){
+                    if(!in_array($im['file'],$files)){
+                        $im['deleted'] = 1;
+                    }
+                }
+            }
+        }
+        function cmpf($a, $b){
+            if ($a['name'] == $b['name']) return 0;
+            return (($a['name'] > $b['name']) ? 1 : -1);
+        }
+        function cmpaf($a, $b){
+            if ($a['name'] == $b['name']) return 0;
+            return ($a['name'] > $b['name']) ? -1 : 1;
+        }
+        if(isset($this->Galleries[0]))usort($this->Galleries,"cmpf");
+        if(isset($this->Images[0]))usort($this->Images,"cmpaf");
+    }
+    
+    function WriteImages(){
+        $path = 'images/list.md';
+        $f = fopen($path,'w');
+        if(isset($this->Galleries[0]))foreach($this->Galleries as &$g){
+            if(isset($g['deleted'])) continue;
+            fwrite($f,'GALLERY '.$g['name']);
+            if(isset($g['featured']) && $g['featured']!=false) { fwrite($f,' FEATURED;'); }
+            if(isset($g['experimental']) && $g['experimental']!=false) { fwrite($f,' EXPERIMENTAL;'); }
+            fwrite($f, PHP_EOL);
+        }
+        if(isset($this->Images[0]))foreach($this->Images as &$im){
+            if(isset($im['deleted'])) continue;
+            fwrite($f, "- ".$im['name'].'; ');
+            if(isset($im['refs']) && isset($im['refs'][0])){ fwrite($f, 'REFS '.implode(" ",$im['refs'])."; "); }
+            if(isset($im['galleries']) && isset($im['galleries'][0])){ fwrite($f, 'GAL '.implode(" ",$im['galleries'])."; "); }
+            if(isset($im['product']) && $im['product']!=''){ fwrite($f, 'PRODUCT '.$im['product']."; "); }
+            fwrite($f, PHP_EOL);
+        }
+        fflush($f);
+        fclose($f);
+    }
+    
+    function EditImage($name, $link_gallery, $do_remove = false, $product_link=NULL, $rename=NULL){
+        if(!($im = &$this->FindImage($name))) return;
+        if(isset($link_gallery)){
+            if($do_remove){
+                if(!isset($im['galleries']) || !isset($im['galleries'][0])) return;
+                foreach($im['galleries'] as $key => $g){ if ($g==$link_gallery) unset($im['galleries'][$key]); }
+                $im['galleries'] = array_merge($im['galleries']);
+            }else{
+                if(!isset($im['galleries'])) $im['galleries']=[];
+                foreach($im['galleries'] as &$g){ if ($g==$link_gallery) return; }
+                $im['galleries'][]=$link_gallery;
+            }
+        }
+        if(isset($product_link)){
+            if($product_link!=''){$im['product']=$product_link;}
+            else{unset($im['product']);}
+        }
+        if(isset($rename)){
+            $ext=pathinfo($im['file'],PATHINFO_EXTENSION);
+            rename($im['file'], 'images/'.$rename.'.'.$ext);
+            if(isset($im['thumb'])) rename($im['thumb'], 'images/thumb/'.$rename.'.'.$ext);
+            $im['name'] = $rename.'.'.$ext; 
+        }
+    }
+    
+    function RegenerateThumbnails(){
+        $glob = glob('images/*.jpg');
+        if(!is_dir('images/thumb')) mkdir('images/thumb');
+        foreach($glob as $file) {
+            $thumb_destination = 'images/thumb/'.basename($file);   
+            $img = new Imagick($file); $geo=$img->getImageGeometry();
+            $width=$geo['width']; $height=$geo['height'];
+            $lim=400;
+            $scale = $lim / min($width,$height);
+            if($scale<1){
+                $img->resizeImage($width*$scale,$height*$scale,imagick::FILTER_GAUSSIAN,0.7);
+            }
+            $img->setImageFormat('jpeg');
+            $img->setImageCompressionQuality(90);
+            $img->writeImage($thumb_destination);
+        }
+    }
+    
+    function CompressImage($source, $destination, $thumb_destination, $quality, $sizelim, $abs_max) {    
+        $img = new Imagick($source); $geo=$img->getImageGeometry(); $img2 = clone $img;
+        $width=$geo['width']; $height=$geo['height'];
+        $lim=400;
+        $scale = $lim / min($width,$height);
+        if($scale<1){
+            $img->resizeImage($width*$scale,$height*$scale,imagick::FILTER_GAUSSIAN,0.7);
+        }
+        $img->setImageFormat('jpeg');
+        $img->setImageCompressionQuality($quality);
+        $img->writeImage($thumb_destination);
+        
+        $scale = min( $sizelim / min($width,$height),  $abs_max / max($width,$height));
+        if($scale<1){
+            $img2->resizeImage($width*$scale,$height*$scale,imagick::FILTER_GAUSSIAN,0.5);
+        }
+        $img2->setImageFormat('jpeg');
+        $img2->setImageCompressionQuality($quality);
+        $img2->writeImage($destination);
+    }
+    function DoUpload(){
+        if(!isset($_FILES['upload_file_name'])) return 0;
+        if(!is_dir('images/thumb')) mkdir('images/thumb');
+        if($_FILES['upload_file_name']['error']>0){
+            echo"file upload err code ".$_FILES['upload_file_name']['error']; exit;
+            return -1;
+        }else{
+            $ext=pathinfo($_FILES['upload_file_name']['name'],PATHINFO_EXTENSION);
+            if(!in_array($ext,['jpg','jpeg','png','gif'])) return 0;
+            $fp = fopen('.la_lock',"w");
+            while (!flock($fp, LOCK_EX| LOCK_NB)){
+                usleep(10000);
+            }
+            $num=date('YmdHis'); $replace=0;
+            if(isset($_POST['image_replace_button']) && isset($_GET['pic']) && preg_match('/([0-9]{14,})/u',$_GET['pic'],$mim)){
+                $num = $mim[1]; $replace=1;
+            }
+            $base = 'images/'.$num;
+            $thumb = 'images/thumb/'.$num;
+            if($ext=='png') $ext='jpg';
+            $final_path = $base.'.'.$ext; $final_thumb = $thumb.'.'.$ext; $i=0;
+            if(!$replace) while(file_exists($final_path)){
+                $final_path = $base.strval($i).'.'.$ext; $final_thumb = $thumb.strval($i).'.'.$ext; $i++;
+            }
+            if($ext!='gif'){
+                $compress = (isset($_GET['compress'])&&$_GET['compress']);
+                $this->CompressImage($_FILES['upload_file_name']['tmp_name'], $final_path, $final_thumb, 90,
+                    $compress?800:1920, $compress?1920:2560);
+            }else{
+                move_uploaded_file($_FILES['upload_file_name']['tmp_name'], $final_path);
+            }
+            flock($fp, LOCK_UN);
+            fclose($fp);
+            $this->ReadImages(true);
+            $this->WriteImages();
+            echo '<uploaded>'.pathinfo($final_path,PATHINFO_BASENAME)."</uploaded>";
+            if(isset($_POST['image_replace_button'])){
+                header('Location: '.$_SERVER['REQUEST_URI']);exit;
+            }
+            exit;
+            return 1;
+        }
+        return 0;
+    }
+    
+    function &GetGallery($name){
+        if(isset($this->Galleries[0])) foreach($this->Galleries as &$g){
+            if($g['name'] == $name) return $g;
+        }
+        return $this->NULL_GALLERY;
+    }
+    function EditGallery($name, $new_name=null, $delete=false, $do_rw=true, $set_featured=null, $set_experimental=null){
+        if($do_rw) $this->ReadImages();
+        $gallery = &$this->GetGallery($name);
+        if(!isset($gallery)){
+            if(!isset($new_name) || preg_match('/main|trash|\s/u',$new_name))return;
+            $g = []; $g['name']=$new_name;
+            $this->Galleries[]=$g;
+        }else{
+            if(isset($new_name)) {
+                if(preg_match('/main|trash|\s/u',$new_name))return;
+                $gallery['name'] = $new_name;
+                foreach ($this->Images as &$im){
+                    if(isset($im['galleries'])&&isset($im['galleries'][0]))foreach($im['galleries'] as &$g){
+                        if($g == $name){ $g = $new_name; }
+                    }
+                }
+            }
+            //if(isset($count)) $gallery['count'] = $count;
+            if(isset($delete) && $delete) $gallery['deleted'] = true;
+            if(isset($set_featured)) $gallery['featured'] = $set_featured;
+            if(isset($set_experimental)) $gallery['experimental'] = $set_experimental;
+        }
+        if($do_rw) { $this->WriteImages(); $this->ClearData(); }
+    }
+    
+    function ClearData(){
+        $this->Posts = [];
+        $this->Threads = [];
+        $this->Images = [];
+    }
+    
+    function ReadPostsFromFile($path){
+        if(!file_exists($path)){
+            $f = fopen($path,'w');
+            fclose($f);
+        }
+        $c = file_get_contents($path);
+        if(preg_match_all('/\[LAMDWIKIPOST\s+([0-9]{14})\s*;\s*([\s\S]*?)\]([\S\s]*?)(?=\[LAMDWIKIPOST|$)/u',$c,$matches,PREG_SET_ORDER)){
+            foreach($matches as $m){
+                $post = [];
+                $post['id'] = $m[1];
+                $post['content'] = trim($m[3]);
+                if(preg_match('/COMMENT\s+([0-9]{14})\s*;/u', $m[2], $n)) $post['comment_to'] = $n[1];
+                if(preg_match('/EMAIL\s+([^;]+)\s*;/u', $m[2], $n)) $post['email'] = $n[1];
+                if(preg_match('/NAME\s+([^;]+)\s*;/u', $m[2], $n)) $post['name'] = $n[1];
+                if(preg_match('/LINK\s+([^;]+)\s*;/u', $m[2], $n)) $post['link'] = $n[1];
+                if(preg_match('/IP\s+([^;]+)\s*;/u', $m[2], $n)) $post['ip'] = $n[1];
+                if(preg_match('/NEXT\s+([0-9]{14})\s*;/u', $m[2], $n)) $post['next'] = $n[1];
+                if(preg_match('/PREV\s+([0-9]{14})\s*;/u', $m[2], $n)) $post['prev'] = $n[1];
+                if(preg_match('/MDEL\s*;/u', $m[2]))                   $post['mark_delete'] = True;
+                if(preg_match('/MVAL\s*([^;]+);/u', $m[2], $n))        $post['mark_value'] = trim($n[1]);
+                if(preg_match('/REFS\s*([^;]+);/u', $m[2], $ma)){
+                    $entries = [];
+                    if(preg_match_all('/([0-9]{14})/u',$ma[1],$links,PREG_SET_ORDER)){ 
+                        foreach($links as $l){
+                            $entries[] = $l[1];
+                        }
+                        $post['refs'] = $entries;
+                    }
+                }
+                if(isset($post['mark_value']) && $post['mark_value']==5){
+                    $post['product']=[];
+                }
+                /* marks add here */
+                $this->Posts[] = $post;
+                
+                if(isset($post['comment_to']) && ($target_post = &$this->GetPost($post['comment_to']))){
+                    if(!isset($target_post['comments']) || !isset($target_post['comments'][0])) $target_post['comments']=[];
+                    $target_post['comments'][]=&$this->Posts[count($this->Posts) - 1];
+                }
+            }
+        }
+    }
+    
+    function SortPosts(){
+        $cmpp = function($a, $b){
+            if ($a['id'] == $b['id']) return 0;
+            return (($a['id'] > $b['id']) ? 1 : -1);
+        };
+        if(isset($this->Posts[0]))usort($this->Posts,$cmpp);
+    }
+    
+    function ReadPosts(){
+        if ((!file_exists('la_config.md') || is_readable('la_config.md') == false) ||
+            (!is_dir('posts') || is_readable('posts') == false) ||
+            (!is_dir('images') || is_readable('images') == false) ||
+            (!is_dir('styles') || is_readable('styles') == false)){
+            $this->Install();
+        }
+        
+        $file_list = [];
+        $glob = glob('posts/*');
+        foreach($glob as $file) {
+            if(preg_match('/[0-9]{6}\.md/', $file)) {
+                $file_list[] = $file;
+            }
+        }
+        sort($file_list, SORT_NATURAL | SORT_FLAG_CASE);
+        
+        foreach($file_list as $f) {
+            $this->ReadPostsFromFile($f);
+        }
+        
+        $this->SortPosts();
+        
+        $this->DetectThreads();
+    }
+    
+    function GetThreadForPost(&$post){
+        if(isset($post['tid'])) return;
+        if(!(isset($post['prev']) || isset($post['next']))) return;
+        $th = [];
+        $post['tid'] = &$th; $th['first'] = &$post; $th['last'] = &$post;
+        $iterp = NULL; $count = 1;
+        if(isset($post['prev']))for($p = $post['prev']; $p!=NULL; $p = $iterp){
+            $np = &$this->GetPost($p); if(!$np) { $post['prev']=NULL; break; }
+            $np['tid'] = &$th;
+            $th['first'] = &$np;
+            $iterp = isset($np['prev'])?$np['prev']:NULL;
+            $count++;
+        }
+        if(isset($post['next']))for($p = $post['next']; $p!=NULL; $p = $iterp){
+            $np = &$this->GetPost($p); if(!$np) { $post['next']=NULL; break; }
+            $np['tid'] = &$th;
+            $th['last'] = &$np;
+            $iterp = isset($np['next'])?$np['next']:NULL;
+            $count++;
+        }
+        if(isset($th['first']['mark_value'])){
+            if($th['first']['mark_value']==6)      $th['exp'] = true;
+            else if($th['first']['mark_value']==7) $th['slf'] = true;
+        }
+        if($th['first'] == $th['last']){ unset($post['tid']); return; }
+        $th['count'] = $count;
+        $this->Threads[] = &$th;
+    }
+    
+    function IdentifyThreadCategory(&$th,&$first_post){
+        if(preg_match('/^\s*\@(.*?)$/mu',$first_post['content'],$m)){
+            $first_post['categories']=[]; if(preg_match_all('/(\S+)(\s|$)/u',$m[1],$matches,PREG_SET_ORDER)){
+                foreach($matches as $ma){ $first_post['categories'][] = $ma[1]; }
+            }
+            if(isset($th) && $th){ $th['categories'] = &$first_post['categories']; }
+        }
+        if(isset($th) && preg_match('/\{\s*INTERESTING\s+(.*?)\}/imu',$first_post['content'],$m)){
+            $th['interesting'] = []; if(preg_match_all('/(\S+)(\s|$)/u',$m[1],$matches,PREG_SET_ORDER)){
+                foreach($matches as $ma){ $th['interesting'][] = $ma[1]; }
+            }
+        }
+    }
+    function IsInterestingPost(&$p){
+        if(isset($p['tid']) && isset($p['tid']['interesting']) && isset($p['tid']['interesting'][0])) return true;
+        return false;
+    }
+    
+    function DetectThreads(){
+        foreach($this->Posts as &$p){
+            if(isset($p['tid'])) { continue; }
+            $this->GetThreadForPost($p);
+            if(!isset($p['tid'])) { $this->IdentifyThreadCategory($this->NULL_POST, $p); }
+        }
+        foreach($this->Threads as &$th){
+            $this->IdentifyThreadCategory($th, $th['first']);
+        }
+        if(!isset($this->Threads) || !isset($this->Threads[0])) return;
+        $now = date_timestamp_get(date_create());
+        foreach($this->Threads as &$t){
+            $lasttime = DateTime::createFromFormat('YmdHis', $t['last']['id']);
+            $diff_days = ($now - date_timestamp_get($lasttime))/3600/24;
+            $t['score'] = (float)$t['count']*0.2 - min($diff_days,200);
+        }
+        function cmp($a, $b){
+            if ($a['score'] == $b['score']) return 0;
+            return ($a['score'] > $b['score']) ? -1 : 1;
+        }
+        usort($this->Threads,"cmp");
+    }
+    
+    function &GetPost($id){
+        if(!isset($id)) return $this->NULL_POST;
+        $i=0; $found=0;
+        if(isset($this->Posts[0])) foreach($this->Posts as $p){
+            if($p&& $p['id'] == $id) { $found = 1; break; }
+            $i++;
+        }
+        if($found) return $this->Posts[$i];
+        return $this->NULL_POST;
+    }
+    
+    function WritePosts(){
+        $cf = NULL;$opened =NULL;
+        $this->SortPosts();
+        foreach($this->Posts as $p){
+            $nid = substr($p['id'], 0,6);
+            if($cf != $nid){
+                if($opened){
+                    fflush($opened);
+                    fclose($opened);
+                }
+                $cf = $nid;
+                $opened = fopen("posts/$cf.md", 'w');
+            }
+            $info = "[LAMDWIKIPOST {$p['id']}; ".
+                    ((isset($p['comment_to']) && $p['comment_to'])?"COMMENT {$p['comment_to']}; ":"").
+                    ((isset($p['email']) && $p['email'])?"EMAIL {$p['email']}; ":"").
+                    ((isset($p['name']) && $p['name'])?"NAME {$p['name']}; ":"").
+                    ((isset($p['link']) && $p['link'])?"LINK {$p['link']}; ":"").
+                    ((isset($p['ip']) && $p['ip'])?"IP {$p['ip']}; ":"").
+                    ((isset($p['mark_delete']) && $p['mark_delete'])?"MDEL; ":"").
+                    ((isset($p['mark_value']) && $p['mark_value']>=0)?"MVAL {$p['mark_value']}; ":"").
+                    ((isset($p['next']) && $p['next'])?"NEXT {$p['next']}; ":"").
+                    ((isset($p['prev']) && $p['prev'])?"PREV {$p['prev']}; ":"").
+                    ((isset($p['refs']) && isset($p['refs'][0]))?("REFS ".implode(" ",$p['refs'])."; "):"").
+                    ']';
+                    
+            fwrite($opened, $info.PHP_EOL.PHP_EOL.$p['content'].PHP_EOL.PHP_EOL);
+        }
+    }
+    
+    function CachePostLinks(){
+        if(isset($this->Posts) && isset($this->Posts[0]))foreach ($this->Posts as &$post){
+            $this->ConvertPost($post);
+            unset($post['refs']);
+        }else return;
+        if(isset($this->Images) && isset($this->Images[0])) foreach ($this->Images as &$im){
+            unset($im['refs']);
+        }
+        foreach ($this->Posts as &$post){
+            if(preg_match_all('/<a[^>]*href=[\'\"]\?post=([0-9]{14})[\'\"][^>]*>.*?<\/a>/u',$post['html'],$matches,PREG_SET_ORDER)){
+                foreach($matches as $m){
+                    $ref = &$this->GetPost($m[1]);
+                    if($ref!=NULL){
+                        if(!isset($ref['refs']))$ref['refs']=[];
+                        if(!in_array($post['id'],$ref['refs'])){ $ref['refs'][]=$post['id']; }
+                    }
+                }
+            }
+            if(preg_match_all('/!\[([^\]]*)\]\(images\/([0-9]{14,}\.(jpg|jpeg|png|gif))\)/u', $post['content'],$matches,PREG_SET_ORDER)){
+                foreach($matches as $m){  
+                    if(($im = &$this->FindImage($m[2]))!=NULL){
+                        if(!isset($im['refs'])){$im['refs']=[];}
+                        if(!in_array($post['id'], $im['refs']))$im['refs'][] = $post['id'];
+                    }
+                }
+            }
+        }
+    }
+    
+    function CreatePostAnchor(&$post){
+        $that=&$this;
+        $post['html'] = preg_replace_callback('/<h([0-9])>(.+?)(?=<\/h[0-9]>)/u', function ($ma) use ($that){
+                $id = '_heading_'.sizeof($this->Anchors);
+                $entry=[(int)$ma[1], $id, $ma[2]];
+                $that->Anchors[] = $entry;
+                return "<h${ma[1]} id='${id}'>${ma[2]}";
+            }, $post['html']);
+    }
+    
+    function RenamePost(&$post, $rename){
+        foreach($this->Posts as &$p){
+            if($p['id']==$rename && $p!==$post) { return; /* don't overwrite */ }
+        }
+        foreach($this->Posts as &$p){
+            if(isset($p['prev']) && $p['prev']==$post['id']) { $p['prev']=$rename; }
+            if(isset($p['next']) && $p['next']==$post['id']) { $p['next']=$rename; }
+        }
+        $post['id'] = $rename;
+    }
+    
+    function &EditPost($id_if_edit, $content, $mark_delete, $reply_to, $get_original_only=false, $mark_value=NULL, $rename=NULL){
+        $this->ReadImages();
+        $this->ReadPosts();
+        $p_success = NULL;
+        if(isset($id_if_edit)){
+            $post = &$this->GetPost($id_if_edit);
+            if($post===$this->NULL_POST) return $this->NULL_POST;
+            if($get_original_only){
+                return $post['content'];
+            }
+            if(isset($content)) $post['content'] = $content;
+            if(isset($mark_delete)) $post['mark_delete'] = $mark_delete;
+            if(isset($mark_value)) $post['mark_value'] = $mark_value;
+            if(isset($rename) && preg_match('/^[0-9]{14}$/u',$rename)) $this->RenamePost($post,$rename);
+            $p_success = &$post;
+        }else{
+            if(!isset($content)) return $this->NULL_POST;
+            $id = date('YmdHis');
+            if($this->GetPost($id)!== $this->NULL_POST) return $this->NULL_POST;
+            $post = [];
+            $post['id'] = $id;
+            $post['content'] = $content;
+            if(isset($reply_to) && ($rep = &$this->GetPost($reply_to))!== $this->NULL_POST){
+                if(!(isset($rep['next']) && $rep['next'])){$rep['next'] = $id; $post['prev'] = $rep['id'];}
+                else $post['content'] = "[引用的文章]($reply_to)".$post['content'];
+            }
+            $this->Posts[] = $post;
+            $p_success = &$this->Posts[count($this->Posts) - 1];
+        }
+        $this->CachePostLinks();
+        $this->WritePosts();
+        $this->WriteImages();
+        $this->ClearData();
+        return $p_success;
+    }
+    
+    function &EditComment($id_if_edit, $comment_to_id, $content, $email, $name, $link=NULL, $ip=NULL){
+        $this->ReadPosts();
+        $p_success = NULL;
+        if(isset($id_if_edit)){
+            $post = &$this->GetPost($id_if_edit);
+            if($post===$this->NULL_POST || !isset($post['comment_to'])) return $this->NULL_POST;
+            if(isset($content)) $post['content'] = $content;
+            if(isset($comment_to_id)) $post['comment_to'] = $comment_to_id;
+            if(isset($email)) $post['email'] = $email; if(isset($name)) $post['name'] = $name; if(isset($link)) $post['link'] = $link;
+            if(isset($ip)) $post['ip'] = $ip;
+            $p_success = &$post;
+        }else{
+            if(!isset($content) || !isset($comment_to_id)) return $this->NULL_POST;
+            $id = date('YmdHis');
+            if($this->GetPost($id)!== $this->NULL_POST) return $this->NULL_POST;
+            if(!($to_post=$this->GetPost($comment_to_id))) return $this->NULL_POST;
+            $post = []; $post['id'] = $id; $post['content'] = $content; $post['comment_to'] = $comment_to_id;
+            if(isset($email)) $post['email'] = $email; if(isset($name)) $post['name'] = $name; if(isset($link)) $post['link'] = $link;
+            if(isset($ip)) $post['ip'] = $ip;
+            $this->Posts[] = $post;
+            $p_success = &$this->Posts[count($this->Posts) - 1];
+        }
+        $this->WritePosts();
+        $this->ClearData();
+        return $p_success;
+    }
+    
+    function InsertReplacementSymbols($MarkdownContent){
+        $replacement = preg_replace('/<!--[\s\S]*-->/U',"",$MarkdownContent);
+        $replacement = preg_replace_callback("/(```|`)([^`]*)(?1)/U",
+                    function($matches){
+                        $rep = preg_replace('/->/','-@>',$matches[0]);
+                        $rep = preg_replace('/=>/','=@>',$rep);
+                        $rep = preg_replace('/<=/','<@=',$rep);
+                        $rep = preg_replace('/<-/','<@-',$rep);
+                        $rep = preg_replace('/\R([+]{3,})\R/',PHP_EOL.'@$1'.PHP_EOL,$rep);
+                        $rep = preg_replace('/\[-/','[@-',$rep);
+                        $rep = preg_replace('/\{/','{@',$rep);
+                        return $rep;
+                    },
+                    $replacement);
+        $replacement = preg_replace("/<[-]+>/","↔",$replacement);
+        $replacement = preg_replace("/([^-])->/","$1→",$replacement);
+        $replacement = preg_replace("/<-([^-])/","←$1",$replacement);
+        $replacement = preg_replace("/([^-])[-]+->/","$1⟶",$replacement);
+        $replacement = preg_replace("/<-[-]+([^-])/","⟵$1",$replacement);
+        $replacement = preg_replace("/<[=]+>/","⇔",$replacement);
+        $replacement = preg_replace("/[=]+>/","⇒",$replacement);
+        $replacement = preg_replace("/<[=]+/","⇐",$replacement);
+        $replacement = preg_replace("/\R([+]{3,})\R/","<div class='page_break'></div>",$replacement);
+        $replacement = preg_replace("/\[-(.*)-\]/U","<span class='text_highlight'>$1</span>",$replacement);
+        $replacement = preg_replace("/{支付宝(\s+[^}]*?)?}/u","<span class='special_alipay'>支付宝$1</span>",$replacement);
+        $replacement = preg_replace("/{PayPal(\s+[^}]*?)?}/ui",
+            "<span class='special_paypal'>Pay<span class='special_paypal_inner'>Pal</span>$1</span>",$replacement);
+        $replacement = preg_replace_callback("/(```|`)([^`]*)(?1)/U",
+                    function($matches){
+                        $rep = preg_replace('/-@>/','->',$matches[0]);
+                        $rep = preg_replace('/<@-/','<-',$rep);
+                        $rep = preg_replace('/=@>/','=>',$rep);
+                        $rep = preg_replace('/<@=/','<=',$rep);
+                        $rep = preg_replace('/\R@([+]{3,})\R/',PHP_EOL.'$1'.PHP_EOL,$rep);
+                        $rep = preg_replace('/\[@-/','[-',$rep);
+                        $rep = preg_replace('/\{@/','{',$rep);
+                        return $rep;
+                    },
+                    $replacement);
+        return $replacement;
+    }
+    
+    function CanShowPost(&$p){
+        if(isset($p['comment_to'])) return false;
+        $is_mark_exp = (isset($p['tid'])&&isset($p['tid']['exp'])&&$p['tid']['exp']) || (isset($p['mark_value'])&&$p['mark_value']==6);
+        $is_mark_slf = (isset($p['tid'])&&isset($p['tid']['slf'])&&$p['tid']['slf']) || (isset($p['mark_value'])&&$p['mark_value']==7);
+        if($is_mark_slf && !$this->LoggedIn){ return false; }
+        if(!$this->InExperimentalMode){
+            if(!$this->LoggedIn){
+                if($is_mark_exp) return false;
+                return true;
+            }
+            return true;
+        }else{
+            if($is_mark_exp) return true;
+            return false;
+        }
+    }
+    
+    function SkipProduct(&$p){
+        if($this->LoggedIn) return false;
+        return (isset($p['mark_value']) && $p['mark_value']==5);
+    }
+    
+    function GetRedirect($args=NULL){
+        $str = 'index.php?';
+        if(isset($args['post'])) $str.='&post='.$args['post'];
+            else if(isset($_GET['post'])) $str.='&post='.$_GET['post'];
+        if(isset($args['gallery'])) $str.='&gallery='.$args['gallery'];
+            else if(isset($_GET['gallery'])) $str.='&gallery='.$_GET['gallery'];
+        if(isset($args['pic'])) $str.='&pic='.$args['pic'];
+            else if(isset($_GET['pic'])) $str.='&pic='.$_GET['pic'];
+        if(isset($args['settings'])) $str.='&settings='.$args['settings'];
+            else if(isset($_GET['settings'])) $str.='&settings='.$_GET['settings'];
+        if(isset($args['extras'])) $str.='&extras='.$args['extras'];
+            else if(isset($_GET['extras'])) $str.='&extras='.$_GET['extras'];
+        if(isset($args['category'])) $str.='&category='.$args['category'];
+            else if(isset($_GET['category'])) $str.='&category='.$_GET['category'];
+        return $str;
+    }
+    
+    function ProcessRequest(&$message=NULL, &$redirect=NULL){
+        if(isset($_GET['gallery']) && $_GET['gallery']=='default'){
+            $redirect = "index.php?gallery=".(isset($this->DefaultGallery)&&$this->DefaultGallery!=''?$this->DefaultGallery:"main");
+            return 0;
+        }
+        if(isset($_GET['set_language'])){
+            setcookie('la_language',$_GET['set_language'],time()+3600*24*7); $_COOKIE['la_language'] = $_GET['set_language'];
+            $redirect=$this->GetRedirect(); return 0;
+        }
+        if(isset($_GET['post'])){
+            $this->CurrentPostID = $_GET['post'];
+        }
+        if(isset($_GET['offset'])){
+            $this->CurrentOffset = $_GET['offset'];
+        }
+        if(isset($_GET['part'])){
+            if($_GET['part'] == 'hot') $this->ExtraScripts.="ShowLeftSide();";
+            else if ($_GET['part'] == 'recent') $this->ExtraScripts.="ShowCenterSide();";
+        }
+        if(isset($_GET['post'])){
+            $this->ExtraScripts.="window.addEventListener('load', (event) => {ScrollToPost('".$_GET['post']."');});";
+        }
+        if(isset($_POST['search_content'])){
+            $redirect='index.php?search='.$_POST['search_content'];return 0;
+        }
+        if(isset($_GET['image_info'])){
+            $m=$_GET['image_info'];
+            $this->ReadImages();
+            $this->ReadPosts();
+            $im = &$this->FindImage($m);
+            if($im==NULL || !isset($im['refs']) || !isset($im['refs'][0])){ echo "not_found"; exit; }
+            echo "<ref>".sizeof($im['refs'])."</ref>";
+            echo "<insert><ul>";
+            foreach(array_reverse($im['refs']) as $ref){
+                $p = $this->GetPost($ref);
+                if(!$p || !$this->CanShowPost($p)) continue;
+                $this->MakeSinglePost($p, false, true, "post_preview", true, false, false, false, true);"</li>";
+            }
+            echo "</ul></insert>";
+            exit;
+        }
+        if(isset($_GET['confirm_enter']) && $_GET['confirm_enter']!=false){
+            setcookie('la_experimental','confirmed'); $_COOKIE['la_experimental'] = $_GET['confirmed'];
+            $redirect='index.php'.(isset($_GET['post'])?'?post='.$_GET['post']:"");return 0;
+        }
+        if($this->LoggedIn){
+            $this->DoUpload();
+            
+            if(isset($_POST['settings_button'])){
+                if(isset($_POST['settings_title'])) $this->Title=$_POST['settings_title'];
+                if(isset($_POST['settings_short_title'])) $this->ShortTitle=$_POST['settings_short_title'];
+                if(isset($_POST['settings_display_name'])) $this->DisplayName=$_POST['settings_display_name'];
+                if(isset($_POST['settings_email'])) $this->EMail=$_POST['settings_email'];
+                if(isset($_POST['settings_special_navigation'])) $this->SpecialNavigation=$_POST['settings_special_navigation'];
+                if(isset($_POST['settings_special_footer'])) $this->SpecialFooter=$_POST['settings_special_footer'];
+                if(isset($_POST['settings_special_footer2'])) $this->SpecialFooter2=$_POST['settings_special_footer2'];
+                if(isset($_POST['settings_special_pinned'])) $this->SpecialPinned=$_POST['settings_special_pinned'];
+                if(isset($_POST['settings_default_gallery'])) $this->DefaultGallery=$_POST['settings_default_gallery'];
+                if(isset($_POST['settings_enable_comments'])) $this->CommentEnabled=True; else $this->CommentEnabled=False;
+                if(isset($_POST['settings_exp_host'])) $this->ExpHost=$_POST['settings_exp_host'];
+                if(isset($_POST['settings_exp_title'])) $this->ExpTitle=$_POST['settings_exp_title'];
+                if(isset($_POST['settings_exp_short_title'])) $this->ExpShortTitle=$_POST['settings_exp_short_title'];
+                if(isset($_POST['settings_exp_caution'])) $this->ExpCaution=$_POST['settings_exp_caution'];
+                if(isset($_POST['settings_exp_index'])) $this->ExpIndex=$_POST['settings_exp_index'];
+                if(isset($_POST['settings_exp_navigation'])) $this->ExpNavigation=$_POST['settings_exp_navigation'];
+                if(isset($_POST['settings_exp_footer'])) $this->ExpFooter=$_POST['settings_exp_footer'];
+                if(isset($_POST['settings_old_password'])&&password_verify($_POST['settings_old_password'], $this->Password)){
+                    if(isset($_POST['settings_id'])) $this->Admin=$_POST['settings_id'];
+                    if(isset($_POST['settings_new_password']) && isset($_POST['settings_new_password_redo']) && 
+                        $_POST['settings_new_password'] = $_POST['settings_new_password_redo'])
+                        {$this->Password=password_hash($_POST['settings_new_password'], PASSWORD_DEFAULT);}
+                    $redirect=$_SERVER['REQUEST_URI'];
+                    $this->DoLogout();
+                }
+                $this->WriteConfig();
+                return 0;
+            }
+            if(isset($_POST['settings_save_redirect'])){
+                if(isset($_POST['settings_redirect'])){
+                    $this->BuildRedirectConfig($_POST['settings_redirect']);
+                }
+                $this->WriteConfig();
+                $redirect = 'index.php?extras=true'; return 0;
+            }
+            if(isset($_POST['settings_save_translation'])){
+                if(isset($_POST['settings_translation'])){
+                    $f = fopen("custom_translations.md", "w"); fwrite($f,$_POST['settings_translation']); fflush($f); fclose($f);
+                }
+                $redirect = 'index.php?extras=true'; return 0;
+            }
+            if(isset($_GET['post'])){
+                if(isset($_GET['post_original'])){
+                    echo $this->EditPost($_GET['post'],NULL,false,NULL,true,NULL,NULL);
+                    exit;
+                }
+            }
+            if(isset($_GET['mark_delete']) && isset($_GET['target'])){
+                $this->EditPost($_GET['target'],NULL,$_GET['mark_delete']=='true',NULL,false,NULL,NULL);
+                if(isset($_GET['post'])) $redirect='?post='.$_GET['target']; else $redirect='index.php';
+                return 0;
+            }
+            if(isset($_GET['set_mark']) && isset($_GET['target'])){
+                $this->EditPost($_GET['target'],NULL,NULL,NULL,NULL,$_GET['set_mark'],NULL);
+                if(isset($_GET['post'])) $redirect='?post='.$_GET['target']; else $redirect='index.php';
+                return 0;
+            }
+            if(isset($_POST['post_button']) && isset($_POST['post_content'])){
+                $c = $_POST['post_content'];
+                if('有什么想说的' == $c){ return 0;}
+                if(preg_match('/\[LAMDWIKIPOST/u',$c))
+                    { $message='Can\'t use character sequence"[LAMDWIKIPOST" anywhere in the post...'; return 1; }
+                $reply_to = (isset($_POST['post_reply_to'])&&$_POST['post_reply_to']!="")?$_POST['post_reply_to']:NULL;
+                $edit_id = (isset($_POST['post_edit_target'])&&$_POST['post_edit_target']!="")?$_POST['post_edit_target']:NULL;
+                if(($edited = $this->EditPost($edit_id, $c, NULL, $reply_to,NULL,NULL,NULL))!=NULL){
+                    $redirect='?post='.$edited['id'];
+                    return 0;
+                };
+            }
+            if(isset($_POST['post_rename_confirm']) && isset($_POST['post_rename_name']) && isset($_GET['rename_post'])){
+                if(($edited = $this->EditPost($_GET['rename_post'], NULL, NULL, NULL,NULL,NULL,$_POST['post_rename_name']))!=NULL){
+                    $redirect='?post='.$edited['id'];
+                    return 0;
+                };
+            }
+            if ($this->CommentEnabled && isset($_POST['comment_confirm']) && (isset($_GET['comment_to']))
+                && isset($_POST['comment_box']) && isset($_POST['comment_email']) && isset($_POST['comment_name'])){
+                $c = $_POST['comment_box'];
+                if(preg_match('/\[LAMDWIKIPOST/u',$c))
+                    { $message='Can\'t use character sequence"[LAMDWIKIPOST" anywhere in the post...'; return 1; }
+                $comment_to = ($_GET['comment_to']!="")?$_GET['comment_to']:NULL;
+                if(($edited = $this->EditComment(NULL,
+                        $_GET['comment_to'], $c, $_POST['comment_email'], $_POST['comment_name'], 
+                        isset($_POST['comment_link'])?$_POST['comment_link']:NULL,
+                        $_SERVER['REMOTE_ADDR']))!=NULL){
+                    $redirect='?post='.$_GET['post'];
+                    return 0;
+                };
+            }
+            if(isset($_POST['gallery_edit_confirm']) && isset($_POST['gallery_edit_new_name']) && $_POST['gallery_edit_new_name']!=''){
+                $old_name = isset($_POST['gallery_edit_old_name'])?$_POST['gallery_edit_old_name']:"";
+                $new_name = $_POST['gallery_edit_new_name'];
+                if($old_name!=''){
+                    $this->EditGallery($old_name, $new_name, false, true, null, null);
+                    $redirect='?gallery='.$new_name;
+                }else{
+                    $this->EditGallery(null, $new_name, false, true, null);
+                    if(isset($_GET['gallery'])) $redirect='?gallery='.$_GET['gallery']; else $redirect='index.php';
+                }
+                return 0;
+            }
+            if(isset($_GET['gallery_edit_delete'])&&$_GET['gallery_edit_delete']!=null){
+                $this->EditGallery($_GET['gallery_edit_delete'], null, true, true, null, null);
+                if(isset($_GET['gallery'])) $redirect='?gallery=main'; else $redirect='index.php';
+                return 0;
+            }
+            if(isset($_POST['gallery_move_ops'])&&isset($_POST['gallery_move_ops'])){
+                if(preg_match('/^(REM|ADD)\s+(\S+)\s+(.*)$/u', $_POST['gallery_move_ops'], $ma)){
+                    $this->ReadImages();
+                    if(preg_match_all('/(\S+)/u', $ma[3], $files, PREG_SET_ORDER)) foreach($files as $name){
+                        $this->EditImage($name[1], $ma[2], ($ma[1]=='REM'), NULL, NULL);
+                    }
+                    $this->WriteImages();
+                    $this->ClearData();
+                }
+                if(isset($_GET['gallery'])) $redirect='?gallery='.$_GET['gallery']; else $redirect='index.php';
+                return 0;
+            }
+            if(isset($_GET['gallery_set_featured'])&&isset($_GET['value'])){
+                $this->EditGallery($_GET['gallery_set_featured'], null, false, true, $_GET['value']!='false', null);
+                if(isset($_GET['gallery'])) $redirect='?gallery='.$_GET['gallery']; else $redirect='index.php';
+                return 0;
+            }
+            if(isset($_GET['gallery_set_experimental'])&&isset($_GET['value'])){
+                $this->EditGallery($_GET['gallery_set_experimental'], null, false, true, null, $_GET['value']!='false');
+                if(isset($_GET['gallery'])) $redirect='?gallery='.$_GET['gallery']; else $redirect='index.php';
+                return 0;
+            }
+            if(isset($_GET['image_list'])&&$_GET['image_list']!=""){
+                $this->ReadImages();
+                $gallery = $_GET['image_list'];
+                foreach($this->Images as $im){
+                    if($gallery=='main'){ echo "[".$im['name'].",".(isset($im['thumb'])?$im['thumb']:$im['name'])."]"; continue; }
+                    if(isset($im['galleries']) && isset($im['galleries'][0]) && in_array($gallery,$im['galleries'])) {
+                        echo "[".$im['name'].",".(isset($im['thumb'])?$im['thumb']:$im['name'])."]"; }
+                }
+                exit;
+            }
+            $do_image_redirect = 0;
+            if(isset($_POST['image_button'])){
+                $this->ReadImages();
+                if(isset($_POST['image_ops_product_link'])){
+                    if(preg_match('/([0-9]{14,}\.(jpg|png|jpeg|gif))/u',$_SERVER['REQUEST_URI'],$ma)){
+                        $this->EditImage($ma[1], NULL, false, $_POST['image_ops_product_link'], NULL);
+                        $redirect=$_SERVER['REQUEST_URI'];
+                        $do_image_redirect = 1;
+                    }
+                }
+                if(isset($_GET['pic'])&&isset($_POST['image_edit_new_name'])){
+                    if (preg_match('/([0-9]{14,}\.(jpg|png|jpeg|gif))/u',$_GET['pic'],$ma) && 
+                        preg_match('/\s*([0-9]{14,})\s*/u',$_POST['image_edit_new_name'],$man)){
+                        $this->EditImage($ma[1], NULL, false, NULL, $man[1]);
+                        $redirect=$this->GetRedirect(['pic'=>'images/'.$man[1].'.'.$ma[2]]);
+                        $do_image_redirect = 1;
+                    }
+                }
+                $this->WriteImages();
+                if($do_image_redirect) return 0;
+            }
+            if(isset($_GET['rewrite_styles'])){
+                $this->WriteStyles();
+                $redirect='?extras=true'; return 0;
+            }
+            if(isset($_GET['regenerate_thumbnails'])){
+                $this->RegenerateThumbnails();
+                $redirect='?extras=true'; return 0;
+            }
+            if(isset($_GET['clear_all_logins'])){
+                $this->LoginTokens=[];
+                $this->WriteTokens();
+            }
+            
+        }
+        return 0;
+    }
+    
+    function PostProcessHTML($html,&$added_images=null,$do_product_info=false, &$product_info=null){
+        if(!$this->LoggedIn){
+            $html = preg_replace("/(<a[^>]*href=[\'\"])(.*?)([\'\"][^>]*)>(\#.*?<\/a>)/u","",$html);
+        }
+        $html = preg_replace("/(<a[^>]*href=[\'\"])([0-9]{14})([\'\"][^>]*)>(.*?<\/a>)/u","$1?post=$2$3 onclick='ShowWaitingBar()'>$4",$html);
+        $html = preg_replace("/(<a[^>]*href=[\'\"])\@(.*?)([\'\"][^>]*)>(.*?<\/a>)/u","$1?category=$2$3 onclick='ShowWaitingBar()'>$4",$html);
+        $html = preg_replace("/(<a[^>]*href=[\'\"])((.*?:\/\/).*?)([\'\"][^>]*)(>)(.*?)(<\/a>)/u",
+                             "$1$2$4 target='_blank'$5$6<sup>↗</sup>$7",$html);
+        $html = preg_replace("/<p>\s*\@.*?<\/p>/mu","",$html);
+        $html = preg_replace("/<p>\s*\{\s*INTERESTING\s+(.*?)\}\s*<\/p>/imu","",$html);
+        $images = [];
+        $images_noclick = [];
+        $search_str = 
+        $html = preg_replace_callback(
+                    "/(<p>\s*)?(<img([^>]*)src=[\'\"])(images\/([0-9]{14,}\.(jpg|png|jpeg|gif)))([\'\"][^>]*)\/>(\s*<\/p>)?/u",
+                    function($m) use (&$images,&$images_noclick) {
+                        $orig_src = $src = $m[5]; $keep = false; $original = false;
+                        if (preg_match('/alt=[\'\"].*keep_inline.*[\'\"]/u',$m[3]) ||
+                            preg_match('/alt=[\'\"].*keep_inline.*[\'\"]/u',$m[7])) { $keep=true; }
+                        if ($keep && preg_match('/alt=[\'\"].*original.*[\'\"]/u',$m[3]) ||
+                                     preg_match('/alt=[\'\"].*original.*[\'\"]/u',$m[7])) { $original=true; }
+                        if(($im = &$this->FindImage($m[5]))!=NULL && isset($im['thumb'])){ 
+                            $src = $im['thumb']; $orig_src=$im['file'];
+                        }
+                        if($this->InExperimentalMode){
+                            $click = "<a href='".$orig_src."' class='original_img' target='_blank'>".
+                                        $m[2].$orig_src.$m[7]."></a>";
+                            return $click;
+                        }else{
+                            $click = $m[2].($original?$orig_src:$src).$m[7]." data-imgsrc='".$m[5]."'".
+                                (isset($im['product'])?" data-product='".$im['product']."'":"").
+                                ($original?" class='original_img'":"").">";
+                            $images_noclick[]=$m[2].$src.$m[7].">";
+                            $ret = "";
+                            if($keep) { $ret = $click; }
+                            else { $images[] = $click; }
+                            if(isset($m[1])&&isset($m[8])&&$m[1]&&$m[8]) return $ret;
+                            else return ((isset($m[1])&&$m[1]?$m[1]:"").$ret.(isset($m[8])&&$m[8]?$m[8]:""));
+                        }
+                    },$html,-1,$count);
+        $html = preg_replace('/<p>\s*<\/p>/u',"", $html); if($html==""){$html="<p>&nbsp;</p>";}
+        if(sizeof($images)){
+            if(sizeof($images)==1){$html.=$images[0];}
+            else{
+                $html.="<div class='p_row'>";
+                foreach($images as $img){
+                    $html.="<div class='p_thumb'>".$img."</div>";
+                }
+                $html.="<div class='p_thumb' style='flex-grow:10000;box-shadow:none;height:0;'></div></div>";
+            }
+        }
+        if(sizeof($images_noclick)){
+            $added_images = $images_noclick;
+        }
+        if($do_product_info){
+            $html = preg_replace_callback("/\{PRICE\s+([^]]+?)\}/u",
+                    function($m) use (&$product_info) { $product_info['price']=$m[1];return ""; },$html);
+            $html = preg_replace_callback("/\{SHORT\s+([^]]+?)\}/u",
+                    function($m) use (&$product_info) { $product_info['short']=$m[1];return ""; },$html);
+            $html = preg_replace('/<p>\s*<\/p>/u',"", $html);
+            if (preg_match('/<h[1-5]>(.+?)<\/h/u',$html,$title)){ $product_info['title']=$title[1]; }
+            else { $product_info['title'] = $this->T('商品'); }
+            if(!isset($product_info['price'])) $product_info['price']=$this->T('未设置价格');
+            $html = preg_replace_callback("/\{PURCHASE\s+([^]]+?)\}/u",function($m) use (&$product_info) {
+                    return "<a class='purchase_button' href=\"mailto:".$this->T($this->DisplayName)."<".$this->EMail.">?subject=".
+                            $this->T('购买').' '.$product_info['title'].
+                            "&body=".$this->T('你好!我想购买').$product_info['title'].urlencode(PHP_EOL.PHP_EOL).
+                            $this->FullURL().urlencode(PHP_EOL.PHP_EOL)."\">".$m[1]."</a>"; },$html);
+        }
+        return $html;
+    }
+    
+    function ConvertPost(&$post){
+        if(!isset($post['html'])){
+            $info=[];
+            $post['html'] = $this->PostProcessHTML($this->PDE->text($this->InsertReplacementSymbols($post['content'])),
+                                                   $post['images'],
+                                                   isset($post['product']), $info);
+            if(isset($post['product'])) $post['product']=$info;
+        }
+    }
+    function GetPostTitle(&$post, $h1_only=false){
+        if(!isset($post['title'])){
+            if($h1_only){
+                if(preg_match('/^#\s+(.*?)$/mu',$post['content'],$m)){return $m[1];}
+                return NULL;
+            }
+            if(preg_match('/^#{1,6}\s+(.*?)$/mu',$post['content'],$m)){$post['title']=$m[1];}
+            else{ $post['title'] = $this->T('未命名'); if(preg_match('/(\s.*)$/mu',$post['content'],$m))
+                                                            {$post['title'].=' ('.strip_tags($this->PDE->text($m[1])).')';} }
+        }
+        return $post['title'];
+    }
+    
+    function DetectPageType(){
+        if($this->InExperimentalMode) $this->PageType='experimental';
+        else if(isset($_GET['extras'])) $this->PageType='extras';
+        else if(isset($_GET['settings'])) $this->PageType='settings';
+        else if(isset($_GET['gallery'])) $this->PageType='gallery';
+        else if(isset($_GET['search'])) $this->PageType='search';
+        else if(isset($_GET['category'])) $this->PageType='category';
+        else if(isset($this->CurrentPostID)) $this->PageType = "post";
+        else if(isset($_GET['comments'])) $this->PageType='comments';
+        else $this->PageType = "main";
+    }
+    
+    function MakeHeader(&$p){?>
+        <!DOCTYPE html><html lang='zh-Hans-CN'>
+        <head>
+        <meta charset='utf-8'>
+        <meta content="width=device-width, initial-scale=1" name="viewport">
+        <title><?=$this->InExperimentalMode?$this->T($this->ExpTitle):$this->T($this->Title)?></title>
+        <link href='styles/main.css' rel='stylesheet' type="text/css">
+        </head>
+        <div class='page'>
+        <script type='text/javascript'>
+            function toggle_mobile_show(a){a.classList.toggle('hidden_on_mobile')}
+            function ShowWaitingBar(){
+                wait = document.querySelector("#waiting_bar");
+                wait.style.display='';
+            }
+            function HideWaitingBar(){
+                wait = document.querySelector("#waiting_bar");
+                wait.style.display='none';
+            }
+        <?php if(!$this->InExperimentalMode){ ?>
+            function la_auto_grow(element){
+                s=window.scrollY;element.style.height="30px";element.style.height=(element.scrollHeight)+"px";window.scroll(0, s);
+            }
+            var scroll_l=0,scroll_c=0;in_center=1;
+            function ShowLeftSide(){
+                scroll_c = window.scrollY;
+                document.getElementById('div_left').classList.remove('hidden_on_mobile');
+                document.getElementById('div_center').classList.add('hidden_on_mobile');
+                window.scroll(0, scroll_l); in_center=0;
+            }
+            function ShowCenterSide(){
+                scroll_l = window.scrollY;
+                document.getElementById('div_center').classList.remove('hidden_on_mobile');
+                document.getElementById('div_left').classList.add('hidden_on_mobile');
+                window.scroll(0, scroll_c); in_center=1;
+            }
+            function ShowBackdrop(alpha=0.5){
+                b = document.getElementById('backdrop');
+                b.style="background-color:rgba(0,0,0,"+alpha+");";
+                b.style.animation='backdrop_fade_in 0.3s forwards';
+                b.style.display = 'block';
+            }
+            function HideBackdrop(){
+                b = document.getElementById('backdrop');
+                b.style.animation='backdrop_fade_out 0.3s forwards';
+                setTimeout(function(e1){e1.style.display='none';}, 300, b);
+            }
+            function ShowRightSide(big,elem_to_clone){
+                p = document.getElementById('pop_right');
+                if(elem_to_clone){
+                    c = elem_to_clone.cloneNode(true);
+                    c.id='side_bar_cloned';c.className="";c.style="";
+                    p.querySelector('._content').appendChild(c);
+                }
+                p.classList.remove('pop_right','pop_right_big');
+                if(big) {p.classList.add('pop_right_big');p.style.animation='pop_slide_in_big 0.3s forwards';}
+                else {p.classList.add('pop_right');p.style.animation='pop_slide_in 0.3s forwards';}
+                p.style.display='block';
+                ShowBackdrop(0.5);
+            }
+            function HideRightSide(){
+                p = document.getElementById('pop_right');
+                if(side=p.querySelector('#side_bar_cloned')){
+                    side.remove();
+                }
+                put = document.querySelector("#_uploader");
+                if(p.classList.contains('pop_right')){p.style.animation='pop_slide_out 0.3s forwards';}
+                else{p.style.animation='pop_slide_out_big 0.3s forwards';}
+                setTimeout(function(e1, e2){e1.style.display='none';if(e2)e2.style.display='none';}, 300, p, put);
+                HideBackdrop();
+            }
+            function ToggleLeftSide(){if(in_center)ShowLeftSide();else ShowCenterSide();}
+            function ScrollToPost(id){
+                if(!(post = document.querySelector("[data-post-id='"+id+"']"))) return;
+                post.scrollIntoView({ behavior: 'smooth', block: 'start'});
+            }
+        <?php } ?>
+        </script>
+        <header>
+        <?php if($this->InExperimentalMode){
+            $this->MakeExpNavButtons($p);
+        }else{
+            $this->MakeNavButtons($p);
+        } ?>
+        <hr />
+        </header>
+        <div id='waiting_bar' style='display:none;'></div>
+        <script>ShowWaitingBar();window.addEventListener('load',(event) =>{HideWaitingBar();}); </script>
+    <?php if(!$this->InExperimentalMode){ ?>
+        <div id='post_menu' style='display:none;' class='pop_menu clean_a'>
+            <ul>
+            <li><span id='_time_hook' class='smaller'>时间</span>&nbsp;&nbsp;<a href='javascript:HidePopMenu();'>×</a></li>
+            <li>
+                <a id='share_copy'>📋︎</a>
+                <a id='share_pin' target='_blank'>Pin</a>
+                <a id='share_twitter' target='_blank'>Twitter</a>
+                <a id='share_weibo' target='_blank'><?=$this->T('微博')?></a>
+            </li>
+            <?php if($this->LoggedIn){ ?>
+                <hr />
+                <li><a id='menu_edit'><?=$this->T('修改')?></a></li>
+                <li>   
+                    <a id='menu_refer_copy'><?=$this->T('只复制')?></a>
+                    <a id='menu_refer'><?=$this->T('引用')?></a><br class='hidden_on_desktop' />
+                </li>
+                <li><a id='menu_mark' href='javascript:ToggleRenameDetails()'><?=$this->T('改名')?></a>
+                    <a id='menu_mark' href='javascript:ToggleMarkDetails()'><?=$this->T('标记')?></a></li>
+                <li id='mark_details' style='display:none;'><b>
+                    <a id='mark_set_clear' href='javascript:SetMark(-1);'>_</a>
+                    <a id='mark_set_0' href='javascript:SetMark(0);'><?=$this->Markers[0]?></a>
+                    <a id='mark_set_1' href='javascript:SetMark(1);'><?=$this->Markers[1]?></a>
+                    <a id='mark_set_2' href='javascript:SetMark(2);'><?=$this->Markers[2]?></a>
+                    <a id='mark_set_3' href='javascript:SetMark(3);'><?=$this->Markers[3]?></a>
+                    <a id='mark_set_4' href='javascript:SetMark(4);'><?=$this->Markers[4]?></a>
+                    <a id='mark_set_5' href='javascript:SetMark(5);'><?=$this->Markers[5]?></a>
+                    <a id='mark_set_6' href='javascript:SetMark(6);'><?=$this->Markers[6]?></a>
+                    <a id='mark_set_7' href='javascript:SetMark(7);'><?=$this->Markers[7]?></a>
+                </b></li>
+                <li id='rename_details' style='display:none;text-align:left;' class='smaller'>
+                    <form action="" method="post" style='display:none;' id='post_rename_form'></form>
+                    <input type='text' id='post_rename_name' name='post_rename_name' form="post_rename_form" style='width:9em;'>
+                    <input class='button' type='submit' form='post_rename_form'
+                        name='post_rename_confirm' id='post_rename_confirm' value='<?=$this->T('确认')?>'>
+                </li>
+                <hr />
+                <li><a id='menu_delete' class='smaller'></a></li>
+            <?php } ?>
+            </ul>
+        </div>
+        <div id='backdrop' style='display:none;' class='backdrop' onclick='HideRightSide()'
+            ondrop="_dropHandler(event);" ondragover="_dragOverHandler(event);"></div>
+        <div id='pop_right' class='pop_right' onclick='event.stopPropagation()'>
+            <div style='text-align:right;position:sticky;top:0;'><a class='clean_a' href='javascript:HideRightSide();'>
+                <?=$this->T('关闭')?>→</a></div>
+            <?php if($this->LoggedIn && in_array($this->PageType,['main','post'])){ ?>
+                <div id='_uploader' style='display:none;'>
+                    <?php $this->MakeUploader(true); ?>
+                    <p>&nbsp;</p>
+                    <?php $this->MakeSideGalleryCode(); ?>
+                </div>
+            <?php } ?>
+            <div class='_content'></div>
+        </div>
+    <?php } ?>
+    <?php
+    }
+    
+    function TranslatePostParts($html){
+        $html = preg_replace_callback('/>([^><]+?)</u', function($ma){
+                return ">".$this->T($ma[1])."<";
+            }, $html);
+        return $html;
+    }
+    
+    function MakeNavButtons(&$p){?>
+        <a href='index.php' class='clean_a hidden_on_mobile' onclick='ShowWaitingBar()'><b><?=$this->T($this->Title)?></b></a>
+        <b><a class='hidden_on_desktop'
+            href='javascript:toggle_mobile_show(document.getElementById("mobile_nav"))'><?=$this->T($this->ShortTitle)?>...</a></b> 
+        <div class='header_nav'>
+        <?php if($this->PageType=='post'){ ?>
+            <div style='display:inline;'><a id='button_back' class='hidden_on_print'>←</a></div>
+            <div style='float:right;' class='hidden_on_print'>
+                <?php if(isset($p) && isset($p['refs']) && isset($p['refs'][0])){ ?>
+                    <span class='hidden_on_desktop'><a id='button_ref' href='javascript:ToggleLeftSide();'>
+                        <?=$this->T('链接')?>(<?=sizeof($p['refs'])?>)</a></span>
+                <?php } ?>
+                <span class='hidden_on_wide hidden_on_print'>
+                    <a id='button_toc'
+                        href='javascript:ShowRightSide(true,document.querySelector("#div_right"));'><?=$this->T('目录')?></a></span></div>
+        <?php } ?>
+        <ul class='hidden_on_mobile hidden_on_print' id='mobile_nav' style='text-align:center;'>
+            <li class='hidden_on_mobile'><a href='?gallery=default' onclick='ShowWaitingBar()'><?=$this->T('画廊')?></a></li>
+            <li class='hidden_on_desktop_force block_on_mobile'>
+            <?php if($this->PageType!='main'){ ?>
+                <a href='index.php?part=recent' onclick='ShowWaitingBar()'><?=$this->T('最近')?></a> |
+                <a href='index.php?part=hot' onclick='ShowWaitingBar()'><?=$this->T('热门')?></a> |
+            <?php } else { ?>
+            <li class='hidden_on_desktop_force block_on_mobile'>
+                <a href='javascript:ShowCenterSide();toggle_mobile_show(document.getElementById("mobile_nav"));'><?=$this->T('最近')?></a> |
+                <a href='javascript:ShowLeftSide();toggle_mobile_show(document.getElementById("mobile_nav"));'><?=$this->T('热门')?></a> |
+            <?php } ?>
+                <a href='index.php?gallery=default' onclick='ShowWaitingBar()'><?=$this->T('画廊')?></a>
+                <?php if($this->LoggedIn){ ?>
+                    | <span class='gray invert_a'><a href='index.php?comments=all'>@</a></span><?php } ?></li>
+            <?php $this->SpecialNavigation;if(isset($this->SpecialNavigation) && ($p = &$this->GetPost($this->SpecialNavigation))!=NULL){
+                echo $this->TranslatePostParts($this->GenerateSinglePost($p, false, false, false, false,$this->NULL_POST));
+            } ?>
+            <li><a href='?search=' onclick='ShowWaitingBar()'><?=$this->T('搜索')?></a></li>
+            <?php if($this->LanguageAppendix=='zh'){ ?>
+                <li class='invert_a smaller'>
+                    <a href='<?=$this->GetRedirect().'&set_language=en'?>' onclick='ShowWaitingBar()'><b>汉语</b>/English</a></li>
+            <?php }else { ?>
+                <li class='smaller'>
+                    <a class='invert_a' href='<?=$this->GetRedirect().'&set_language=zh'?>' onclick='ShowWaitingBar()'>
+                        汉语/<b>English</b></a>
+                    <br class='hidden_on_desktop' />
+                    <span class='text_highlight'><a id='translate_button' target='_blank'>&nbsp;Google Translate&nbsp;</a></span></li>
+            <?php } ?>
+        </ul>
+        </div>
+    <?php
+    }
+    
+    function MakeExpNavButtons(&$p){?>
+        <b><a href='index.php' class='hidden_on_mobile'><?=$this->T($this->ExpTitle)?></a></b>
+        <b><a class='hidden_on_desktop'
+            href='javascript:toggle_mobile_show(document.getElementById("mobile_nav"))'><?=$this->T($this->ExpShortTitle)?>...</a></b>
+        <ul class='hidden_on_mobile' id='mobile_nav' style='text-align:center;'>
+        <li class='hidden_on_desktop block_on_mobile'><a href='index.php'><?=$this->T('索引')?></a></li>
+        <?php $this->ExpNavigation;if(isset($this->ExpNavigation) && ($p = &$this->GetPost($this->ExpNavigation))!=NULL){
+            echo $this->TranslatePostParts($this->GenerateSinglePost($p, false, false, false, false,$this->NULL_POST));
+        } ?>
+        <?php if($this->LanguageAppendix=='zh'){ ?>
+            <li class='invert_a smaller'><a href='<?=$this->GetRedirect().'&set_language=en'?>'><b>汉语</b>/English</a></li>
+        <?php }else { ?>
+            <li class='invert_a smaller'><a href='<?=$this->GetRedirect().'&set_language=zh'?>'>汉语/<b>English</b></a></li>
+            <span class='text_highlight'><a id='translate_button' target='_blank'>&nbsp;Google Translate&nbsp;</a></span></li>
+        <?php } ?>
+        </ul>
+    <?php
+    }
+    
+    function GenerateLinkedPosts($ht){
+        $ht = preg_replace_callback('/<p>[\s]*<a[^>]*href=[\'\"]\?post=([0-9]{14})[\'\"][^>]*>(.*)<\/a>[\s]*<\/p>/u',
+            function($m){
+                $rp = &$this->GetPost($m[1]);
+                $s="<div class='smaller block post ref_compact gray'>".
+                    "<a href='?post=".$m[1]."' class='post_access invert_a' onclick='ShowWaitingBar()'>→</a>".
+                    "<div class='post_ref'><div class='smaller'>".$m[2]."</div>".
+                    (($rp!==NULL && $this->CanShowPost($rp))?$this->TranslatePostParts(
+                                $this->GenerateSinglePost($rp,false,false,false,true,$this->NULL_POST,true)):$this->T("未找到该引用。")).
+                    "</div></div>";
+                return $s;
+            },
+            $ht
+        );
+        $ht = preg_replace_callback('/<li>[\s]*<a[^>]*href=[\'\"]\?post=([0-9]{14})[\'\"][^>]*>PRODUCT\s+(.*)<\/a>[\s]*<\/li>/u',
+            function($m){
+                $rp = &$this->GetPost($m[1]);
+                $s="<div class='product_ref block post ref_compact'><a href='?post=".$m[1]."' class='clean_a' onclick='ShowWaitingBar()'>".
+                    (($rp!==NULL && $this->CanShowPost($rp))?$this->TranslatePostParts(
+                                $this->GenerateSinglePost($rp,true,false,false,true,$this->NULL_POST,true)):$this->T("未找到该引用。")).
+                    "</a></div>";
+                return $s;
+            },
+            $ht
+        );
+        return $ht;
+    }
+    
+    function ExtractBigTables($html, &$table_out){
+        $table = NULL;
+        $new_html = preg_replace_callback('/<p>\s*\{big_table\}\s*<\/p>\s*(<table>[\s\S]*?<\/table>)/u', function ($m) use (&$table){
+            $table = $m[1];
+            return "";
+        }, $html);
+        $table_out = $table;
+        return $new_html;
+    }
+
+    function GenerateSinglePost(&$post, $strip_tags, $generate_anchor=false, $generate_refs=false,
+                                 $generate_thumbs=false, &$table_out, $hide_long = false){
+        $this->ConvertPost($post);
+        if($generate_anchor){ $this->CreatePostAnchor($post); }
+        $ht = $post['html'];
+        if(isset($post['mark_value']) && $post['mark_value']==5 && ($generate_thumbs || !$generate_anchor)){
+            $ht="<div class='product_thumb'>".$post['images'][0]."</div>".
+                "<p class='bold'>".$post['product']['title']."</p>".
+                (isset($post['product']['short'])?("<p class='smaller gray'>".$post['product']['short']."</p>"):"").
+                "<p class='smaller bold'>".$post['product']['price']."</p>";
+            return $ht;
+        }
+        if($hide_long){
+            $ht = preg_replace('/<p>\s*\{read_more\}\s*<\/p>[\s\S]*/u',"<p class='smaller bold'>[".$this->T("阅读更多")."]</p>", $ht);
+        }else{
+            $ht = preg_replace('/<p>\s*\{read_more\}\s*<\/p>/u',"", $ht);
+        }
+        if ($strip_tags){
+            $ht = strip_tags($ht,'<b><i><h1><h2><h3><h4><p><blockquote>');
+            $ht = preg_replace('/<p>\s*<\/p>/u',"", $ht);
+            $ht = "<div class='post_ref_main'>".$ht."</div>";
+        }
+        else{
+            if($generate_refs) $ht = $this->GenerateLinkedPosts($ht);
+            if($table_out!==$this->NULL_POST){
+                $table = NULL; $ht = $this->ExtractBigTables($ht,$table);
+                $table_out = $table;
+            }else{
+                $ht = preg_replace('/<p>\s*\{big_table\}\s*<\/p>/u','',$ht);
+            }
+        }
+        if ($strip_tags && $generate_thumbs && isset($post['images']) && isset($post['images'][0])){
+            $ht.="<div class='post_ref_images'><ul class='side_thumb ref_thumb'>";
+            foreach($post['images'] as $im){
+                $ht.="<li class='file_thumb'>".$im."</li>";
+            }
+            $ht.="</ul></div>";
+        }
+        return $ht;
+    }
+    
+    function MakeSinglePostExp(&$post){ 
+        $big_table = ""; ?>
+        <li class='post post_dummy'>
+            <?=$this->TranslatePostParts(
+               $this->GenerateSinglePost($post, false, false, true, false, $big_table, false)); ?>
+        </li>
+        <?php if($big_table!==$this->NULL_POST) echo "</ul></li><div class='table_top'>".$big_table.'</div>';?>
+    <?php
+    }
+    function MakeSinglePost(&$post, $show_link=false, $side=false, $extra_class_string=NULL,
+                                    $strip_tags=false, $show_thread_link=false, $show_reply_count=false, $generate_anchor=false,
+                                    $generate_thumb = false, $is_top = false){
+        $is_deleted = (isset($post['mark_delete'])&&$post['mark_delete']);
+        $mark_value = isset($post['mark_value'])?$this->Markers[$post['mark_value']]:-1;
+        $ref_count = isset($post['refs'])?sizeof($post['refs']):0;
+        $big_table = ($show_thread_link)?$this->NULL_POST:"";
+        $is_product = isset($post['mark_value'])&&$post['mark_value']==5;
+        $title = $generate_anchor?$this->GetPostTitle($post, true):NULL;
+        ?>
+        <?=$title?"<li class='print_title'><h1>".$title."</h1></li>":""?>
+        <li class='post<?=isset($extra_class_string)?' '.$extra_class_string:''?><?=$side?" post_box":""?>'
+            data-post-id='<?=$post['id']?>' <?=$is_deleted?"data-mark-delete='true'":""?>>
+            <?php if($mark_value>=0 && !$show_link && $mark_value!='P'){?>
+                <div class='smaller <?=$is_deleted?"gray":""?>'><?=$mark_value?> <?=$this->T('标记')?></div>
+            <?php } ?>
+            <?php if($is_top){?>
+                <div class='top_post_hint'><?=$this->T('置顶帖子')?><hr /></div>
+            <?php } ?>
+            <?php if(!$side && $show_link){ ?>
+                <a href='?post=<?=$post['id']?>' onclick='ShowWaitingBar()'>
+                <div class='post_access <?=($mark_value<0 || $ref_count)?"invert_a":""?> hover_dark'>
+                    <?=isset($post['mark_value'])?$this->Markers[$post['mark_value']]:($ref_count?"":"→")?>
+                </div>
+                <?php if($ref_count){ ?>
+                    <div class='post_access ref_count'><?=$ref_count?></div>
+                <?php } ?>
+                </a>
+            <?php } ?>
+            <?=$side?"<a href='?post={$post['id']}' onclick='ShowWaitingBar()'>":""?>
+            <div class='<?=$side?"":($show_link?'post_width':'post_width_big')?><?=$is_deleted?" deleted_post":""?>'>
+                    <?php if(!$side && !$strip_tags){?>
+                        <div class='post_menu_button _menu_hook' >+</div>
+                    <?php } if($is_product&&!$generate_anchor){
+                        echo "<p class='smaller gray'>".$this->T('商品')."</p>";
+                        echo "<div class='product_ref clean_a'><a href='?post={$post['id']}'>";} ?>
+                    <?=$this->TranslatePostParts(
+                           $this->GenerateSinglePost($post, $strip_tags, $generate_anchor, true,
+                                                     $generate_thumb,$big_table,$show_thread_link||$side)); ?>
+                    <?php if($is_product&&!$generate_anchor){echo "</a></div>";} ?>
+            </div>
+            <?=$side?"</a>":""?> <?php
+            if(!$show_thread_link && $big_table!==$this->NULL_POST && !$side){
+                echo "</ul></li><div class='table_top'>".$big_table.'</div>';
+            ?><ul class='print_column'>
+                <li class='post<?=isset($extra_class_string)?' '.$extra_class_string:''?>' <?=$is_deleted?"data-mark-delete='true'":""?>>
+            <?php
+            }
+            if($show_thread_link && isset($post['tid']) && $this->CanShowPost($post['tid']['first']) && 
+                $post['tid']['first']['id']!=$post['id']){ ?>
+                <div class='gray smaller block opt_compact post'>
+                    <a href='?post=<?=$post['tid']['first']['id']?>' onclick='ShowWaitingBar()'>
+                        <div class='post_access invert_a hover_dark'><?=isset($post['tid']['first']['mark_value'])?
+                                $this->Markers[$post['tid']['first']['mark_value']]:"→"?></div></a>
+                    <div class='post_width'><div class='smaller'><?=$this->T('回复给主题帖:')?></div>
+                        <?=$this->TranslatePostParts(
+                                $this->GenerateSinglePost($post['tid']['first'], false, false, false, true,$this->NULL_POST,true));?>
+                    </div>
+                </div>
+            <?php }
+            if($show_reply_count && isset($post['tid'])){ ?>
+                <a class='smaller block invert_a' href='?post=<?=$post['tid']['last']['id']?>'><?=$post['tid']['count']?>
+                    <?=$this->T('个回复')?></a>
+            <?php }
+            ?>    
+        </li>
+    <?php
+    }
+    
+    function MakePostingFields($reply_to=NULL, $show_hint=false){?>
+        <div class='smaller' id='post_hint_text' style='display:<?=$show_hint?"block":"none"?>;'><?=$this->T('继续补充该话题:')?></div>
+        <form action="<?=$_SERVER['REQUEST_URI']?>" method="post" style='display:none;' id='post_form'></form>
+        <textarea id="post_content" name="post_content" rows="4" form='post_form'
+                  onfocus="if (value =='<?=$this->T('有什么想说的')?>'){value ='';}la_auto_grow(this);"
+                  onblur="if (value ==''){value='<?=$this->T('有什么想说的')?>';la_auto_grow(this);}"    
+                  oninput="la_auto_grow(this);" onload="la_auto_grow(this);"><?=$this->T('有什么想说的')?></textarea>
+        <input class='button' form="post_form" type="submit" name='post_button' value=<?=$this->T('发送')?> 
+            onclick='ShowWaitingBar();' />
+        | <a class='gray smaller pointer' onclick='ShowSideUploader();'><?=$this->T('图片')?></a>
+        <div style='float:right;'>
+            <a class='gray smaller pointer' onclick='t=document.querySelector("#post_content");t.value="";la_auto_grow(t);'>
+                <?=$this->T('清空')?></a>
+            <a class='gray smaller pointer' style='display:none;' id='post_reply_restore' href='javascript:RestoreReply()'></a>
+        </div>
+        <input style='display:none;' type=input form="post_form" id='post_edit_target' name='post_edit_target' />
+        <script> la_auto_grow(document.getElementById("post_content"));</script>
+        <?php if($reply_to){ ?>
+            <input style='display:none;' type=input form="post_form" id='post_reply_to' name='post_reply_to' value='<?=$reply_to?>' />
+        <?php } ?>
+    <?php
+    }
+    
+    function MakeCommentPosts(){
+        if(!$this->LoggedIn) return; ?>
+        <h2><?=$this->T('评论')?></h2><div class='spacer'></div>
+        <?php if(!$this->CommentEnabled){
+            echo "<p><span class='text_highlight'>&nbsp;".$this->T("已关闭评论")."&nbsp;</span></p><br />"; } ?>
+        <div class='comment'><ul>
+        <?php $i=0;
+            foreach(array_reverse($this->Posts) as $p){
+                if(!isset($p['comment_to'])) continue;
+                if($i < $this->CommentsPerPage * $this->CurrentOffset) {$i++; continue;}
+                
+                $ht = $this->TranslatePostParts($this->GenerateSinglePost($p, false, false, false, false, $t, false));
+                $name = isset($p['link'])?("<a href='".$p['link']."'>".$p['name']."↗</a>"):$p['name'];
+                $post_to = $this->GetPost($p['comment_to']); $post_title = $this->GetPostTitle($post_to);
+                if(!$post_to) $post_title = "?";
+                $mail = "<span class='gray hidden_on_print'>&nbsp;".
+                        "<a href='mailto:".$p['email']."'>@</a>&nbsp;".
+                        (isset($p['ip'])?$p['ip']:"?")."&nbsp;<br class='hidden_on_desktop'/>".
+                        "<a href='index.php?post=".$p['comment_to']."'>(".$post_title.")</a></span>";
+                echo "<li><p><b>".$name.":</b>".$mail."<br /></p>".$ht."</li>";
+                
+                $i++;
+            }
+        ?>
+        </ul><br /></div>
+    <?php
+    }
+    
+    function MakeRecentPosts($search_term=NULL, $category=NULL){?>
+        <div class='center' id='div_center'>
+            <h2><?=isset($search_term)?$this->T('搜索'):
+                                (isset($category)?("<span class='gray'>".$this->T('分类')."</span> ".
+                                    ($category=='none'?$this->T('未分类'):$this->T($category))):($this->T('最近')).
+                    ($this->LoggedIn?" <span class='gray invert_a hidden_on_print'><a href='index.php?comments=all'>@</a></span>":""))?></h2>
+            <?php if(isset($search_term)){ ?>
+                <form action="index.php" method="post" style='display:none;' id='search_form'></form>
+                <input id="search_content" name="search_content" rows="4" form='search_form' type='text' value='<?=$search_term?>'>
+                <input class='button' form="search_form" type="submit" name='search_button' value=<?=$this->T('搜索')?>
+            <?php }else if(isset($category)){ ?>
+                <div></div>
+            <?php }else if($this->LoggedIn){ ?>
+                <div class='post_box_top _input_bundle'>
+                    <?php $this->MakePostingFields(NULL,false); ?>
+                </div>
+            <?php } ?>
+            <ul class='print_column'>
+                <?php
+                    if(!isset($search_term) && !isset($category) &&
+                       (isset($this->SpecialPinned) && ($p = &$this->GetPost($this->SpecialPinned))!=NULL && !$this->CurrentOffset) &&
+                       $this->CanShowPost($p)){
+                        $this->MakeSinglePost($p, true, false, false, false, true, false, false, false, true);
+                    }
+                    $i = 0;
+                    foreach(array_reverse($this->Posts) as &$p){
+                        if(!$this->CanShowPost($p) || $this->SkipProduct($p)) continue;
+                        if(isset($search_term)){
+                            if ($search_term=='' || !preg_match("/".preg_quote($search_term)."/iu", $p['content'])) continue;
+                        }else if(isset($category)){
+                            $cat = isset($p['tid'])?(isset($p['tid']['categories'])?($p['tid']['categories']):[]):
+                                                    (isset($p['categories'])?($p['categories']):[]);
+                            if ($category=='none') { if($cat!=[]) continue; }
+                            else{ if ($category=='' || !in_array($category, $cat)) continue; }
+                            if(isset($p['tid'])){ if(isset($p['tid']['displayed'])) continue; $p['tid']['displayed'] = True; }
+                        }else{
+                            if(in_array($p['id'],
+                                [$this->SpecialPinned,$this->SpecialFooter,$this->SpecialFooter2,$this->SpecialNavigation])) continue;
+                            if(isset($p['tid'])){ if(isset($p['tid']['displayed'])) continue; $p['tid']['displayed'] = True; }
+                        }
+                        if($i < $this->PostsPerPage * $this->CurrentOffset) {$i++; continue;}
+                        $this->MakeSinglePost($p, true, false, NULL, false, true, false, false, false, false);
+                        $i++;
+                        if($i >= $this->PostsPerPage * (1+$this->CurrentOffset)) {break;}
+                    }?>
+            </ul>
+            <div class='page_selector clean_a'>
+                <hr />
+                <a <?=$this->CurrentOffset>0?("href='index.php?offset=".($this->CurrentOffset-1).
+                            (isset($search_term)?"&search=".$search_term:(isset($category)?"&category=".$category:""))."'"):""?>
+                   <?=$this->CurrentOffset==0?"class='gray'":""?>><?=$this->T('上一页')?> ←</a>
+                <?=$this->CurrentOffset+1?>
+                <a href='index.php?offset=<?=($this->CurrentOffset+1).
+                    (isset($search_term)?"&search=".$search_term:(isset($category)?"&category=".$category:""))?>'>
+                    → <?=$this->T('下一页')?></a>
+            </div>
+        </div>
+    <?php
+    }
+    
+    function MakeHotPosts($placeholder_only=false){?>
+        <div class='left hidden_on_mobile' id='div_left'>
+            <?php if ($placeholder_only){ ?>&nbsp;
+            <?php }else{ ?>
+            <h2><?=$this->T('热门')?></h2>
+            <ul>
+                <?php
+                    $i=0;
+                    foreach($this->Threads as &$th){
+                        if(!$this->CanShowPost($th['first'])) continue;
+                        if($i>=$this->HotPostCount) break;
+                        $this->MakeSinglePost($th['first'], false, true, "post_preview", true, false, true, false, true, false);
+                        $i++;
+                    } ?>
+            </ul>
+            <?php } ?>
+        </div>
+    <?php
+    }
+    
+    function MakeLinkedPosts(&$p){
+        $has_ref = isset($p['refs'])&&isset($p['refs'][0]); ?>
+        <div class='left hidden_on_mobile' id='div_left'>
+        <h2<?=$has_ref?"":" class='gray'"?>><?=$this->T('链接')?></h2>
+        <?php
+            if($has_ref){ ?>
+                <span class='smaller'><?=sizeof($p['refs'])?> <?=$this->T('个引用:')?></span>
+                <ul><?php $count_product=0;
+                foreach(array_reverse($p['refs']) as &$pr){
+                    $po = $this->GetPost($pr); if(isset($post['mark_value']) && $po['mark_value']==5){ $count_product++; continue; }
+                    if(!$this->CanShowPost($po)){ continue; }
+                    $this->MakeSinglePost($po, false, true, "post_preview", true, false, false, false, true, false);
+                } 
+                ?></ul>
+            <?php if($count_product){ ?> <span class='smaller'><?=$this->T('和').' '.$count_product.' '.$this->T('个商品')?></span> <?php }
+                }else{ ?>
+                <span class='gray smaller'><?=$this->T('没有帖子链接到这里。')?></span>
+            <?php } ?>
+        </div>
+    <?php
+    }
+    function MakeLinkedPostsExp(&$p){
+        $has_ref = isset($p['refs'])&&isset($p['refs'][0]); ?>
+        <div class='center_exp post_dummy smaller'>
+        <hr />
+        <h3<?=$has_ref?"":" class='gray'"?>><?=$this->T('链接')?></h3>
+        <?php
+            if($has_ref){ ?>
+                <div class='smaller'><?=sizeof($p['refs'])?> <?=$this->T('个引用:')?></div>
+                <ul><?php $count_product=0;
+                foreach(array_reverse($p['refs']) as &$pr){
+                    echo "<li class='post_dummy'><a href='?post=".$pr."'>".$this->T($this->GetPostTitle($this->GetPost($pr),false)).
+                         "</a></li>";
+                } 
+                ?></ul>
+            <?php if($count_product){ ?> <span class='smaller'><?=$this->T('和').' '.$count_product.' '.$this->T('个商品')?></span> <?php }
+                }else{ ?>
+                <div class='gray smaller'><?=$this->T('没有帖子链接到这里。')?></div>
+            <?php } ?>
+        <div style='margin-bottom:4rem;'>&nbsp;</div>
+        </div>
+    <?php
+    }
+    
+    function MakeCommentSection(&$post){
+        if(!$this->CommentEnabled){ return; }
+        $to_post = isset($post['tid'])?$post['tid']['first']:$post;
+        $comment_count = (isset($to_post['comments']) && isset($to_post['comments'][0]))?count($to_post['comments']):0;
+        ?><div class='comment'>
+        <br class='hidden_on_print' /><h2><?=$this->T('评论')?> (<?=$comment_count;?>)</h2><div class='spacer'></div>
+            <?php if($comment_count) { echo "<ul>";
+                    foreach($to_post['comments'] as $p){
+                        $ht = $this->TranslatePostParts($this->GenerateSinglePost($p, false, false, false, false, $t, false));
+                        $name = isset($p['link'])?("<a href='".$p['link']."'>".$p['name']."↗</a>"):$p['name'];
+                        $mail = $this->LoggedIn?("<span class='gray clean_a hidden_on_print'>&nbsp;".
+                                                 "<a href='mailto:".$p['email']."'>@</a>&nbsp;</span>"):"";
+                        echo "<li><p><b>".$name.":</b>".$mail."</p>".$ht."</li>";
+                    }
+                    echo "</ul>";
+            } else {
+                echo $this->T('还没有评论');
+            } ?>
+            <div class='hidden_on_print'>
+                <div class='spacer'></div>
+                <form action="index.php?post=<?=$this->CurrentPostID?>&comment_to=<?=$to_post['id']?>"
+                    method="post" style="display:none;" id="comment_form"></form>
+                <p><a class='text_highlight bold clean_a'
+                      onclick='document.getElementById("comment_box").style.display="block";this.parentNode.style.display="none"'>
+                      &nbsp;<?=$this->T('写评论');?>&nbsp;</a></p>
+                <div id='comment_box' style='display:none;'>
+                    <p class='gray' style='margin-bottom:0.5em;'><?=$this->T('您的邮箱不会公开展示。');?></p>
+                    <table style='white-space:nowrap;'>
+                        <tr><td colspan='2'>
+                            <textarea id="comment_box" name="comment_box" rows="4" class='full_box' form='comment_form'
+                                oninput="CommentUpdated();" ></textarea>
+                        </td></tr>
+                        <tr><td><?=$this->T('电子邮件')?>*</td><td><?=$this->T('称呼')?>*</td></tr>
+                        <tr><td><input type="text" form="comment_form" id='comment_email' name='comment_email'
+                                    class='full_box' oninput="CommentUpdated();" /></td>
+                            <td><input type="text" form="comment_form" id='comment_name' name='comment_name'
+                                    class='full_box' oninput="CommentUpdated();" /></td></tr>
+                        <tr><td colspan='2'><?=$this->T('个人网站')?></td></tr>
+                        <tr><td colspan='2'>
+                            <input type="text" form="comment_form" id='comment_link' name='comment_link'
+                                    class='full_box' oninput="CommentUpdated();" />
+                        </td></tr>
+                        <tr><td colspan='2'>
+                            <div class='spacer'></div>
+                            <input class='button text_highlight bigger' type='submit' form='comment_form'
+                                name='comment_confirm' id='comment_confirm' value='&nbsp;<?=$this->T('发送')?>&nbsp;'>
+                        </td></tr>
+                    </table></div>
+                <script>
+                const IsValidEmail = (email) => {
+                  return String(email).toLowerCase().match(/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/);
+                };
+                function IsValidHttpUrl(string) {
+                  let url; try{ url = new URL(string); } catch (_) { return false; }
+                  return url.protocol === "http:" || url.protocol === "https:";
+                }
+                var cbox = document.getElementById('comment_box');
+                var cemail = document.getElementById('comment_email');
+                var cname = document.getElementById('comment_name');
+                var clink = document.getElementById('comment_link');
+                var cconfirm = document.getElementById('comment_confirm');
+                function CommentUpdated(){
+                    cconfirm.disabled=true;
+                    if (cemail.value!="" && IsValidEmail(cemail.value) &&
+                        cbox.innerText.length>2 && cname.value.length>2 &&
+                        clink.value=="" || IsValidHttpUrl(clink.value)){
+                        cconfirm.removeAttribute("disabled");
+                    }
+                }
+                CommentUpdated();
+                </script>
+            </div>
+        </div>
+    <?php
+    }
+    
+    function MakeInterestingSection(&$th){
+        $this->Anchors = [];  
+        $c = &$th['first']['content'];
+        $this->ConvertPost($th['first']);
+        $ht = $th['first']['html'];
+        ?>
+        <script>function ClickImage(post_access){im=post_access.querySelector('img');if(im){im.click();}}</script>
+        <div class='center_full' id='div_center'>
+            <h2 class='hidden_on_print'><?=$this->T('有趣')?>
+                <a class='gray clean_a' href='?post=<?=$th['first']['id']?>'>→</a></h2>
+            <ul><li class='post post_width_big' data-post-id='<?=$th['first']['id']?>'>
+                <div class='post_menu_button _menu_hook' onclick='ShowPostMenu(this.parentNode);'>+</div><?=$this->TranslatePostParts($ht)?>
+                <?php if($this->LoggedIn && (!$this->InExperimentalMode)){ ?>
+                    <div class='post_width_big hidden_on_print'><br />
+                        <?php $this->MakePostingFields($th['last']['id'], true);?><br />
+                    </div>
+                <?php } ?>
+                <table><thead><tr><th></th><?php foreach($th['interesting'] as $header){ ?>
+                    <th><?=$this->T($header);?></th>
+                <?php } ?></tr></thead><tbody class='interesting_tbody'>
+                <?php $plist = []; for($p = &$this->GetPost($th['first']['next']); $p!=$this->NULL_POST;
+                                       $p = &$this->GetPost(isset($p['next'])?$p['next']:NULL)){ $plist[] = &$p; }
+                foreach(array_reverse($plist) as &$p){
+                    $this->ConvertPost($p);
+                    $mark_value = isset($p['mark_value'])?$this->Markers[$p['mark_value']]:-1;
+                    $ref_count = isset($p['refs'])?sizeof($p['refs']):0;
+                    $is_current = $p['id']==$this->CurrentPostID;
+                    $is_deleted = (isset($p['mark_delete'])&&$p['mark_delete']);  ?>
+                    <tr class='<?=$is_current?"post_current_row":""?><?=$is_deleted?" deleted_post":""?>'
+                        data-post-id='<?=$p['id']?>' <?=$is_deleted?"data-mark-delete='true'":""?>
+                        onDblClick='ClickImage(this)'><td>
+                    <a <?=$is_current?"":("href='?post=".$p['id']."' onclick='ShowWaitingBar()'");?> class='clean_a paa'>
+                        <div class='post_access <?=($mark_value<0 || $ref_count)?"invert_a":""?> hover_dark'>
+                            <?=isset($p['mark_value'])?$this->Markers[$p['mark_value']]:($ref_count?"":"→")?>
+                        </div>
+                        <?php if($ref_count){ ?>
+                            <div class='post_access ref_count'><?=$ref_count?></div>
+                        <?php } ?></a>
+                    <?=$this->TranslatePostParts($p['html']);?>
+                    <a class='_menu_hook clean_a post_menu_button' onclick='ShowPostMenu(this.parentNode.parentNode);'>+</a></td></tr>
+                    <?php if($is_current && $ref_count>0){ ?><tr class='post_current_row'><td><p>&nbsp;</p><ul class='smaller'>
+                        <?php foreach($p['refs'] as $ref){
+                            $po = $this->GetPost($ref);
+                            if(isset($post['mark_value']) && $post['mark_value']==5){ $count_product++; continue; }
+                            if(!$this->CanShowPost($po)){ continue; }
+                            echo "<li><a href='?post=".$po['id']."'>".$this->GetPostTitle($po)."</li>";
+                        } ?>
+                    </ul></td></tr><?php } ?>
+                <?php } ?>
+                </tbody></table>
+            </li></ul>
+            <br />
+            <?php $this->MakeCommentSection($th['first']); ?>
+        </div>
+        <?php return true;
+    }
+    
+    function MakePostSection(&$post){
+        $this->Anchors = []; ?>
+        <div class='center' id='div_center'>
+            <?php $th=NULL; $is_thread = isset($post['tid']); if($is_thread){ $th = $post['tid'];?>
+                <h2 class='hidden_on_print'><?=$this->T('话题')?></h2>
+            <?php }else{ ?>
+                <h2 class='hidden_on_print'><?=$this->T('详细')?></h2>
+            <?php } ?>
+            <?php $cat = NULL; 
+            if($is_thread) { if(isset($th['categories']) && isset($th['categories'][0])){ $cat = $th['categories']; } }
+            else { if(isset($post['categories']) && isset($post['categories'][0])) { $cat = $post['categories']; } }
+            if($cat){ ?>
+                <p><b><?=$this->T('分类')?></b> | <?php foreach($cat as $c){ 
+                    echo "<a href='?category=".$c."'>".($c=='none'?$this->T('未分类'):$this->T($c))."</a> "; } ?></p>
+            <?php } ?>
+            <ul class='print_column'>
+            <?php
+                if($is_thread){
+                    for($p = &$th['first']; $p!=$this->NULL_POST; $p = &$this->GetPost(isset($p['next'])?$p['next']:NULL)){
+                        $use_class = ($p == $post)?'focused_post':'';
+                        $show_link = ($p == $post)?false:true;
+                        $make_title = ($p == $post);
+                        $this->MakeSinglePost($p,$show_link,false,$use_class,false, false, false, true, false, false);
+                        if($make_title){?>
+                        <script>
+                        document.title+=" | <?=addslashes(preg_replace('/\r|\n/u', ' ', mb_substr(strip_tags($p['html']),0,1000)))?>";
+                        </script>
+                        <?php }
+                    }
+                }else{
+                    $this->MakeSinglePost($post,false, false, 'focused_post',false, false, false, true, false, false);
+                    ?><script>
+                    document.title+=" | <?=addslashes(preg_replace('/\r|\n/u', ' ', mb_substr(strip_tags($post['html']),0,1000)))?>";
+                    </script><?php
+                } ?>
+                <?php if($this->LoggedIn && (!$this->InExperimentalMode)){ ?>
+                    <div class='post_width_big hidden_on_print'>
+                        <br /><?php $this->MakePostingFields($is_thread?$th['last']['id']:$post['id'], true);?>
+                    </div>
+                <?php } 
+                $this->MakeCommentSection($post);
+                ?>
+            </ul>
+        </div>
+    <?php
+    }
+    function MakePostSectionExp(&$post){
+        $this->Anchors = []; $is_thread = isset($post['tid']);
+        ?>
+        <div class='center_exp' id='div_center'>
+            <ul>
+            <?php
+                if($is_thread){
+                    $th = $post['tid'];
+                    for($p = &$th['first']; $p!=$this->NULL_POST; $p = &$this->GetPost(isset($p['next'])?$p['next']:NULL)){
+                        $this->MakeSinglePostExp($p);
+                        if($p == $th['first']){?>
+                        <script>
+                        document.title+=" | <?=addslashes(preg_replace('/\r|\n/u', ' ', mb_substr(strip_tags($p['html']),0,1000)))?>";
+                        </script>
+                        <?php }
+                    }
+                }else{
+                    $this->MakeSinglePostExp($post);
+                    if($post['id']!=$this->ExpIndex){ ?><script>
+                    document.title+=" | <?=addslashes(preg_replace('/\r|\n/u', ' ', mb_substr(strip_tags($post['html']),0,1000)))?>";
+                    </script><?php }
+                } ?>
+            </ul>
+        </div>
+    <?php
+    }
+    
+    function MakeSideGalleryCode(){?>
+        <div>
+            <h3><?=$this->T('点击图片以插入:')?></h3>
+            <select id="side_gallery_select" onchange="RefreshSideGallery()">
+                <option value="main"><?=$this->T('全部')?></option>
+                <?php if($this->LoggedIn){ ?>
+                    <option value="trash"><?=$this->T('垃圾桶')?></option>
+                <?php }?>
+                <?php if(isset($this->Galleries[0])) foreach($this->Galleries as $g){ ?>
+                    <option value="<?=$g['name']?>"><?=$g['name']?></option>
+                <?php } ?>
+            </select>
+            <div id='side_gallery'>
+            </div>
+        </div>
+        <script>
+            function InsertImage(imgname){
+                t = document.querySelector("#post_content");
+                v = t.value;
+                t.value = v.slice(0, t.selectionStart) + "![<?=$this->T('图片')?>](images/"+imgname+")"+ v.slice(t.selectionEnd);
+                la_auto_grow(t);
+            }
+            function RefreshSideGallery(){
+                let xhr = new XMLHttpRequest();
+                xhr.onreadystatechange = (function(){
+                    if (this.readyState === this.DONE) {
+                        if (this.status === 200) {
+                            ind = document.querySelector("#side_gallery");
+                            if(res = xhr.responseText.matchAll(/\[(.*?),(.*?)\]/gu)){
+                                str = "<ul class='side_thumb'>"
+                                for (const m of res) {
+                                    str+="<li><div class='file_thumb' onclick='InsertImage(\""+m[1]+"\")'>"+
+                                        "<img src='"+m[2]+"' /></div>"
+                                }
+                                str += "</ul>"
+                                ind.innerHTML = str;
+                            }
+                        }
+                    }
+                });
+                xhr.open("GET", "?image_list="+document.querySelector("#side_gallery_select").value, true);
+                xhr.send();
+            }
+        </script>
+    <?php
+    }
+    
+    function MakeUploader($is_side=false){ ?>
+        <div id='upload_operation_area' class='hidden_on_print'>
+            <p><?=$this->T('选择、粘贴或者拖动到页面以上传图片。')?></p>
+            <input type="file" id='upload_selector' accept="image/x-png,image/png,image/gif,image/jpeg" multiple/>
+            <ul id='file_list'>
+            </ul>
+            <div class='smaller gray' id='upload_hint'><?=$this->T('就绪')?></div>
+        </div>
+        <a id='upload_click' class='hidden_on_print'
+            href='javascript:UploadList()'<?=$is_side?" data-is-side='true'":""?>><?=$this->T('上传列表中的文件')?></a>
+        <script>
+            function pastehandler(event){
+                var items = (event.clipboardData || event.originalEvent.clipboardData).items;
+                for (index in items) {
+                    var item = items[index];
+                    if (item.kind === 'file') {
+                        put = document.querySelector("#_uploader");
+                        if(put) ShowSideUploader();
+                        var blob = item.getAsFile();
+                        AddImageFile(blob);
+                        return;
+                    }
+                }
+            }
+            let _fd_list = [];  
+            let hint=document.querySelector('#upload_hint');
+            function ToggleCompress(button){
+                li = button.parentNode;
+                num=li.dataset.number;
+                for(i=0;i<_fd_list.length;i++){
+                    if (_fd_list[i][0] == num){
+                        state = _fd_list[i][2];
+                        if(state){_fd_list[i][2] = 0; button.innerHTML='1920';break;}
+                        else{_fd_list[i][2] = 1; button.innerHTML='800';break;}
+                    }
+                }
+            }
+            function RemoveFromUpload(button){
+                li = button.parentNode;
+                num=li.dataset.number;
+                for(i=0;i<_fd_list.length;i++){
+                    if (_fd_list[i][0] == num){
+                        _fd_list.splice(i, 1);break;
+                    }
+                }
+                li.parentNode.removeChild(li);
+            }
+            function EndThisUpload(){
+                unfinished = 0;
+                for(i=0;i<_fd_list.length;i++){
+                    if (_fd_list[i][3]==0){
+                        unfinished=1;
+                    }
+                }
+                if(!unfinished){
+                    hint.innerHTML="<?=$this->T('上传完成。')?>";
+                    cl=document.querySelector('#upload_click');
+                    if(!cl.dataset.isSide){
+                        cl.innerHTML="<?=$this->T('刷新页面')?>";
+                        cl.href=window.location.href;
+                    }else{
+                        _fd_list.splice(0,_fd_list.length);
+                        fl = document.querySelector("#file_list");
+                        fl.innerHTML = "";
+                        list=document.querySelector('#upload_operation_area');
+                        list.style.pointerEvents='';
+                        list.style.opacity='';
+                        document.onpaste=pastehandler;
+                        RefreshSideGallery();
+                    }
+                }
+            }
+            function UploadList(){
+                if(!_fd_list.length) return;
+                hint.innerHTML="<?=$this->T('正在上传...')?>";
+                list=document.querySelector('#upload_operation_area');
+                list.style.pointerEvents='none';
+                list.style.opacity='0.5';
+                document.onpaste="";
+                for(i=0;i<_fd_list.length;i++){
+                    let xhr = new XMLHttpRequest();
+                    //xmlHTTP.upload.addEventListener("loadstart", loadStartFunction, false);
+                    //xmlHTTP.upload.addEventListener("progress", progressFunction, false);
+                    //xmlHTTP.addEventListener("load", transferCompleteFunction, false);
+                    //xmlHTTP.addEventListener("error", uploadFailed, false);
+                    //xmlHTTP.addEventListener("abort", uploadCanceled, false);
+                    var li = list.querySelector('[data-number="'+_fd_list[i][0].toString()+'"]')
+                    xhr.open("POST", "?compress="+_fd_list[i][2].toString(), true);
+                    function wrap(li, i){
+                        var ind = li.querySelector('._compress_toggle')
+                        return function(){
+                            if (this.readyState === this.DONE) {
+                                if (this.status === 200) {
+                                    var response = xhr.responseText;
+                                    if(res = response.match(/<uploaded>(.*)<\/uploaded>/)){
+                                        ind.innerHTML = "<?=$this->T('已上传为')?> "+res[1];
+                                        _fd_list[i][3] = 1;
+                                    }else{
+                                        ind.innerHTML = "<?=$this->T('出现错误。')?>";
+                                        _fd_list[i][3] = 1;
+                                    }
+                                    EndThisUpload();
+                                }
+                            }
+                        }
+                    }
+                    xhr.onreadystatechange = wrap(li, i);
+                    xhr.send(_fd_list[i][1]);
+                }
+            }
+            function AddImageFile(blob){
+                var ext="";
+                if(blob.name.match(/png$/))ext = 'png';
+                else if(blob.name.match(/jpg$/))ext = 'jpg';
+                else if(blob.name.match(/jpeg$/))ext = 'jpeg';
+                else if(blob.name.match(/gif$/))ext = 'gif';
+                else  return;
+                var fd = new FormData();
+                blob.name = blob.name=generateUID().toString();
+                fd.append("upload_file_name", blob, "_upload_"+blob.name+"."+ext);
+                _fd_list.push([blob.name, fd, 1, 0]);/* number original is_compress uploaded */
+                var reader = new FileReader();
+                reader.onload = function(event){
+                    fl = document.querySelector("#file_list");
+                    ht = "<li id='_upload_"+blob.name+"' data-number='"+blob.name+"'>"+
+                         "<a class='_remove_file pointer invert_a' onclick='RemoveFromUpload(this)'>×</a> "+"<div class='file_thumb'>"+
+                         "<img class='no_pop' src='"+event.target.result+"'>"+"</div>"+
+                         " →<a class='_compress_toggle pointer' onclick='ToggleCompress(this)'>800</a></li>";
+                    fl.innerHTML+=ht;
+                };
+                reader.readAsDataURL(blob);
+            }
+            sel = document.querySelector('#upload_selector');
+            sel.addEventListener("change", function(){
+                var input = this;
+                function ff(file){
+                    return function(e){
+                        let blob = new Blob([new Uint8Array(e.target.result)], {type: file.type});
+                        blob.name = file.name;
+                        AddImageFile(blob);
+                    }
+                }
+                for(i=0;i<input.files.length;i++){
+                    if(input.files[i].size){
+                        let reader = new FileReader();
+                        reader.onload = ff(input.files[i]);
+                        reader.readAsArrayBuffer(input.files[i]);
+                    }
+                }
+            });
+            function generateUID() {
+                var firstPart = (Math.random() * 46656) | 0;
+                var secondPart = (Math.random() * 46656) | 0;
+                firstPart = ("000" + firstPart.toString(36)).slice(-3);
+                secondPart = ("000" + secondPart.toString(36)).slice(-3);
+                return firstPart + secondPart;
+            }
+            document.onpaste = pastehandler;
+            function dropHandler(ev) {
+              put = document.querySelector("#_uploader");
+              if(put) ShowSideUploader();
+              bkg=document.querySelector('#dropping_background');
+              bkg.style.display="none";
+              console.log('File(s) dropped');
+              ev.preventDefault();
+              if (ev.dataTransfer && ev.dataTransfer.items) {
+                for (var i = 0; i < ev.dataTransfer.items.length; i++) {
+                  if (ev.dataTransfer.items[i].kind === 'file') {
+                    var file = ev.dataTransfer.items[i].getAsFile();
+                    function ff(file){
+                        return function(e){
+                            let blob = new Blob([new Uint8Array(e.target.result)], {type: file.type});
+                            blob.name = file.name;
+                            AddImageFile(blob);
+                        }
+                    }
+                    if(file){
+                        let reader = new FileReader();
+                        reader.onload = ff(file);
+                        reader.readAsArrayBuffer(file);
+                    }
+                  }
+                }
+              } else {
+                for (var i = 0; i < ev.dataTransfer.files.length; i++) {
+                    function ff(file){
+                        return function(e){
+                            let blob = new Blob([new Uint8Array(e.target.result)], {type: file.type});
+                            blob.name = file.name;
+                            AddImageFile(blob);
+                        }
+                    }
+                    if(ev.dataTransfer.files[i].size){
+                        let reader = new FileReader();
+                        reader.onload = ff(ev.dataTransfer.files[i]);
+                        reader.readAsArrayBuffer(ev.dataTransfer.files[i]);
+                    }
+                }
+              }
+            }
+            function dragOverHandler(ev) {
+                bkg=document.querySelector('#dropping_background');
+                bkg.style.display="";
+                console.log('File(s) in drop zone');
+                ev.preventDefault();
+            }
+        </script>
+    <?php
+    }
+    
+    function CanShowGallery(&$g){
+        if(!$this->LoggedIn && isset($g['experimental']) && $g['experimental']) return false;
+        return true;
+    }
+    function CanShowImage(&$im){
+        if(!$this->LoggedIn && isset($im['galleries']) && isset($im['galleries'][0])) foreach($im['galleries'] as $ga){
+            if(($g=$this->GetGallery($ga)) && isset($g['experimental']) && $g['experimental']) return false;
+        }
+        return true;
+    }
+    
+    function MakeGallerySection(){
+        if(!isset($_GET['gallery'])) return; 
+        $name=NULL; if($_GET['gallery']!='main' && $_GET['gallery']!='trash'){
+            $name=$_GET['gallery'];}?>
+        <script>
+        document.title+=" | <?=$this->T('画廊')?>";
+        </script>
+        <div class='center_wide' id='div_center' style='position:relative;'>
+            <h2><?=(isset($name) && ($gal=$this->GetGallery($name))!=NULL)?
+                                    ("<span class='gray album_hint'>".$this->T('相册').":</span>".$this->T($name)):
+                                    ($_GET['gallery']=='trash'?$this->T('垃圾桶'):$this->T('画廊'))?></h2>
+            <div class='hidden_on_desktop'>
+                <?=$this->T('前往')?>
+                <select id="gallery_go_to" onchange="window.location.href='?gallery='+this.value;">
+                <?php if(isset($this->Galleries[0])) foreach($this->Galleries as $g){
+                    if(!isset($g['featured']) || !$g['featured'] ||
+                        !$this->CanShowGallery($g)){ continue; } $is_this = ($_GET['gallery']==$g['name']);?>
+                        <option value="<?=$g['name']?>" <?=$is_this?"selected":""?>>
+                            <?=(isset($g['experimental'])&&$g['experimental'])?'E ':''?><?=$this->T($g['name'])?></option>
+                <?php } ?>
+                <option value="main" <?=$_GET['gallery']=='main'?"selected":""?>>[<?=$this->T('全部')?>]</option>
+                <?php if($this->LoggedIn){ ?>
+                    <option value="trash" <?=$_GET['gallery']=='trash'?"selected":""?>>[<?=$this->T('垃圾桶')?>]</option>
+                <?php } ?>
+                <?php if(isset($this->Galleries[0])) foreach($this->Galleries as $g){
+                    if((isset($g['featured']) && $g['featured']) ||
+                        !$this->CanShowGallery($g)){ continue; } $is_this = ($_GET['gallery']==$g['name']);?>
+                        <option value="<?=$g['name']?>" <?=$is_this?"selected":""?>>
+                            <?=(isset($g['experimental'])&&$g['experimental'])?'E ':''?><?=$this->T($g['name'])?></option>
+                <?php } ?>
+                </select>
+            </div>
+            <?php if($this->LoggedIn){?>
+                <div>
+                    <?php if(isset($name)){ ?>
+                        <div style='text-align:right;position:absolute;right:0;top:0;width:100%;' class='invert_a smaller hidden_on_print'>
+                            <a href='javascript:ShowDeleteMenu();'  class='smaller'><?=$this->T('删除相册')?></a><br />
+                            <?php if(isset($gal['featured']) && $gal['featured']!=false){ ?>
+                                <a href='?gallery=<?=$_GET['gallery']?>&gallery_set_featured=<?=$_GET['gallery']?>&value=false'
+                                    class='smaller'><?=$this->T('取消精选')?></a>
+                            <?php }else{ ?>
+                                <a href='?gallery=<?=$_GET['gallery']?>&gallery_set_featured=<?=$_GET['gallery']?>&value=true'
+                                    class='smaller'><?=$this->T('设为精选')?></a>
+                            <?php } ?><br />
+                            <?php if(isset($gal['experimental']) && $gal['experimental']!=false){ ?>
+                                <a href='?gallery=<?=$_GET['gallery']?>&gallery_set_experimental=<?=$_GET['gallery']?>&value=false'
+                                    class='smaller'><?=$this->T('取消实验')?></a>
+                            <?php }else{ ?>
+                                <a href='?gallery=<?=$_GET['gallery']?>&gallery_set_experimental=<?=$_GET['gallery']?>&value=true'
+                                    class='smaller'><?=$this->T('设为实验')?></a>
+                            <?php } ?>
+                            <div class='pop_menu smaller invert_a' id='gallery_delete_menu' style='display:none;'>
+                                <div style='float:left;' class='gray'><?=$this->T('该操作不删除图片。')?></div>
+                                <a href='javascript:HidePopMenu();'>×</a>
+                                <hr />
+                                <a href='?gallery=main&gallery_edit_delete=<?=$_GET['gallery']?>'><b><?=$this->T('删除相册')?></b></a>
+                            </div>
+                        </div>
+                    <?php } ?>
+                    <?php $this->MakeUploader(false); ?>
+                    <div style='text-align:right;position:relative;' class='invert_a smaller hidden_on_print'>
+                        <div style='position:relative'>
+                        <?php if(isset($name)){ ?>
+                            <a href='javascript:ShowGalleryEditMenu("<?=$name?>")'><?=$this->T('改名')?></a>
+                        <?php } ?>
+                            <a href='javascript:ShowGalleryEditMenu(null)'><?=$this->T('添加')?></a>
+                            <div class='pop_menu smaller invert_a' id='gallery_edit_menu' style='display:none;max-width:90%;'>
+                                <form action="<?=$_SERVER['REQUEST_URI']?>&edit_gallery=true"
+                                    method="post" style='display:none;' id='gallery_edit_form'></form>
+                                <a style='float:left;'><?=$this->T('相册名字:')?></a>
+                                <a href='javascript:HidePopMenu();'>×</a>
+                                <input type='text' form='gallery_edit_form' name='gallery_edit_new_name' id='gallery_edit_new_name'>
+                                <input type='text' form='gallery_edit_form' name='gallery_edit_old_name'
+                                    id='gallery_edit_old_name' style='display:none'>
+                                <input class='button' type='submit' form='gallery_edit_form'
+                                    name='gallery_edit_confirm' id='gallery_edit_confirm' value='<?=$this->T('确认')?>'>
+                            </div>
+                        </div>
+                    </div>
+                    <div class='smaller hidden_on_print'>
+                        <form action="<?=$_SERVER['REQUEST_URI']?>"
+                            method="post" style='display:none;' id='gallery_move_form'></form>
+                        <input type='text' form='gallery_move_form' name='gallery_move_ops'
+                            id='gallery_move_ops' style='display:none'>
+                        <p><?=$this->T('选择了')?> <span id='gallery_selected_count'>0</span> <?=$this->T('个图片。')?>
+                            <a href='javascript:ClearSelectedImages()'><?=$this->T('清除')?></a></p>
+                        <p><?=$this->T('添加到')?>
+                            <select id="gallery_move_to">
+                            <option value="trash"><?=$this->T('垃圾桶')?></option>
+                            <?php if(isset($this->Galleries[0])) foreach($this->Galleries as $g){ ?>
+                                <option value="<?=$g['name']?>"><?=$this->T($g['name'])?></option>
+                            <?php } ?>
+                            </select>
+                            <a href='javascript:AddToGallery()'><?=$this->T('执行')?></a>
+                        </p>
+                        <p>
+                            <?=$this->T('或者')?><a href='javascript:RemoveFromGallery()'><?=$this->T('从相册移除')?></a>
+                        </p>
+                    </div>
+                </div>
+            <?php } ?>
+            <p>&nbsp;</p>
+            <div>
+                <?php $opened=0; $prev_year=""; if(isset($this->Images[0])) foreach($this->Images as $im){
+                    $year = substr($im['name'], 0, 4);
+                    if($year!=$prev_year){
+                        if($opened) { ?><div class='p_thumb' style='flex-grow:10000;box-shadow:none;height:0;'></div></div></div><?php } ?>
+                        <div><h2 class='sticky_title'><?=$year;?></h2><div class='p_row'><?php $prev_year=$year; $opened=1;
+                    }
+                    if($_GET['gallery']=='trash') $name='trash';
+                    if($_GET['gallery']!='main'){ if(!isset($im['galleries']) || !in_array($name, $im['galleries'])) continue;}
+                    if(!$this->CanShowImage($im)){ continue; } ?>
+                    <div class='p_thumb'>
+                        <?php if($this->LoggedIn){ ?>
+                            <div class="post_menu_button _select_hook white" onclick='ToggleSelectImage(this, "<?=$im["name"]?>")'>●</div>
+                        <?php } ?>
+                        <img src='<?=$im['thumb']?>' data-imgsrc='<?=$im["name"]?>'<?=isset($im['product'])?
+                            'data-product="'.$im["product"].'"':""?>/>
+                    </div>
+                <?php } ?>
+                <div class='p_thumb' style='flex-grow:10000;box-shadow:none;height:0;'></div>
+                </div>
+            </div>
+        </div>
+        <?php if($this->LoggedIn){ ?>
+            <script type='text/javascript'>
+            var selected_image = new Array();
+            function RemoveFromGallery(){
+                form = document.querySelector('#gallery_move_form');
+                ops = document.querySelector('#gallery_move_ops');
+                sel = document.querySelector('#gallery_move_to');
+                ops.value = "REM <?=$_GET['gallery']?>"+" "+selected_image.join(' ');
+                form.submit();
+            }
+            function AddToGallery(){
+                form = document.querySelector('#gallery_move_form');
+                ops = document.querySelector('#gallery_move_ops');
+                sel = document.querySelector('#gallery_move_to');
+                ops.value = "ADD "+sel.value+" "+selected_image.join(' ');
+                form.submit();
+            }
+            function ClearSelectedImages(){
+                selected_image.splice(0, selected_image.length);
+                checks = document.querySelectorAll('._select_hook');
+                [].forEach.call(checks, function(c){
+                    c.classList.remove('p_thumb_selected');
+                });
+                count = document.querySelector('#gallery_selected_count');
+                count.innerHTML="0";
+            }
+            function ToggleSelectImage(elem, name){
+                count = document.querySelector('#gallery_selected_count');
+                if(selected_image.indexOf(name) == -1){ selected_image.push(name); elem.classList.add('p_thumb_selected'); }
+                else{ selected_image.splice(selected_image.indexOf(name), 1); elem.classList.remove('p_thumb_selected'); }
+                count.innerHTML=selected_image.length.toString()
+            }
+            function ShowGalleryEditMenu(old_name){
+                m = document.querySelector('#gallery_edit_menu');
+                old = document.querySelector('#gallery_edit_old_name');
+                m.style.display='block';
+                if(old_name!=''){ old.value=old_name; }else{ old.value=''; }
+            }
+            function ShowDeleteMenu(){
+                m=document.querySelector('#gallery_delete_menu');
+                m.style.display='block';
+            }
+            </script>
+        <?php } ?>
+    <?php
+    }
+    
+    function MakeGalleryLeft(){?>
+        <div class='left hidden_on_mobile gallery_left' id='div_left'>
+            <h2><?=$this->T('相册')?></h2>
+            <div>
+                <span class='gray smaller bold'><?=$this->T('精选')?><hr>  </span>
+                <ul>
+                    <?php if(isset($this->Galleries[0])) foreach($this->Galleries as $g){ 
+                        if(!isset($g['featured']) || !$g['featured'] || !$this->CanShowGallery($g)){ continue; } ?>
+                        <a href='?gallery=<?=$g['name']?>' onclick='ShowWaitingBar()'>
+                            <li class='<?=$_GET['gallery']==$g['name']?'selected':""?>'>
+                                <?=(isset($g['experimental'])&&$g['experimental'])?'E ':''?><?=$this->T($g['name'])?></li></a>
+                    <?php } ?>
+                </ul>
+                <div class='smaller'><span class='gray'><?=$this->T('其他相册')?><hr></span><ul>
+                    <a href='?gallery=main' onclick='ShowWaitingBar()'>
+                        <li class='<?=$_GET['gallery']=='main'?' selected':""?>'>[<?=$this->T('全部图片')?>]</li></a>
+                    <?php if(isset($this->Galleries[0])) foreach($this->Galleries as $g){ 
+                        if((isset($g['featured']) && $g['featured']) || !$this->CanShowGallery($g)){ continue; } ?>
+                        <a href='?gallery=<?=$g['name']?>' onclick='ShowWaitingBar()'>
+                            <li class='<?=$_GET['gallery']==$g['name']?' selected':""?>'>
+                                <?=(isset($g['experimental'])&&$g['experimental'])?'E ':''?><?=$this->T($g['name'])?></li></a>
+                    <?php } ?>
+                    <?php if($this->LoggedIn){ ?>
+                        <a href='?gallery=trash' onclick='ShowWaitingBar()'>
+                            <li class='<?=$_GET['gallery']=='trash'?' selected':""?>'>[<?=$this->T('垃圾桶')?>]</li></a>
+                    <?php } ?>
+                </ul></div>
+                <p>&nbsp;</p>
+                <script>
+                </script>
+            </div>
+        </div>
+    <?php
+    }
+    
+    function MakeTOC(){?>
+        <div class='right hidden_on_mobile' id='div_right'>
+            <?php if(isset($this->Anchors[0])){?>
+                <h2><?=$this->T('目录')?></h2><ul>
+                <?php
+                    foreach($this->Anchors as $a){?>
+                        <li class='toc_entry_<?=$a[0]>5?5:$a[0]?>'><a href='#<?=$a[1]?>'><?=$this->T($a[2])?></a></li>
+                    <?php }
+                ?></ul>
+            <?php }else{ ?>
+                <h2 class='gray'><?=$this->T('目录')?></h2>
+                <span class='gray smaller'><?=$this->T('未找到目录')?></span>
+            <?php } ?>
+        </div>
+    <?php
+    }
+    
+    
+    function MakeSettings(){?>
+        <div class='settings'>
+            <h2><?=$this->T('设置')?></h2>
+            <table style='white-space:nowrap;'>
+                <?php if($this->LoggedIn){ ?>
+                    <form action="index.php?settings=true" method="post" style='display:none;' id='settings_form'></form>
+                    <colgroup><col style='min-width:5em;'><col style='width:100%;min-width:5em;'></colgroup>
+                    <thead><tr><td><?=$this->T('选项')?></td><td><?=$this->T('值')?></td></tr></thead>
+                    <tr><td><?=$this->T('网站标题')?></td>
+                        <td><input type="text" form="settings_form" id='settings_title' name='settings_title'
+                            value='<?=$this->Title?>'/></td></tr>
+                    <tr><td><?=$this->T('短标题')?></td>
+                        <td><input type="text" form="settings_form" id='settings_short_title' name='settings_short_title'
+                            value='<?=$this->ShortTitle?>'/></td></tr>
+                    <tr><td><?=$this->T('显示名称')?></td>
+                        <td><input type="text" form="settings_form" id='settings_display_name' name='settings_display_name'
+                        value='<?=$this->DisplayName?>'/></td></tr>
+                    <tr><td><?=$this->T('电子邮件')?></td>
+                        <td><input type="text" form="settings_form" id='settings_email' name='settings_email'
+                        value='<?=$this->EMail?>'/></td></tr>
+                    <tr><td><?=$this->T('导航栏')?>
+                        <?=isset($this->SpecialNavigation)?"<a href='?post=".$this->SpecialNavigation."'>→</a>":""?></td>
+                        <td><input type="text" form="settings_form" id='settings_special_navigation' name='settings_special_navigation'
+                        value='<?=$this->SpecialNavigation?>'/></td></tr>
+                    <tr><td><?=$this->T('脚注')?> 1<?=isset($this->SpecialFooter)?"<a href='?post=".$this->SpecialFooter."'>→</a>":""?></td>
+                        <td><input type="text" form="settings_form" id='settings_special_footer' name='settings_special_footer'
+                        value='<?=$this->SpecialFooter?>'/></td></tr>
+                    <tr><td><?=$this->T('脚注')?> 2<?=isset($this->SpecialFooter2)?"<a href='?post=".$this->SpecialFooter2."'>→</a>":""?></td>
+                        <td><input type="text" form="settings_form" id='settings_special_footer2' name='settings_special_footer2'
+                        value='<?=$this->SpecialFooter2?>'/></td></tr>
+                    <tr><td><?=$this->T('置顶文')?><?=isset($this->SpecialPinned)?"<a href='?post=".$this->SpecialPinned."'>→</a>":""?></td>
+                        <td><input type="text" form="settings_form" id='settings_special_pinned' name='settings_special_pinned'
+                        value='<?=$this->SpecialPinned?>'/></td></tr>
+                    <tr><td><?=$this->T('默认相册')?></td>
+                        <td><input type="text" form="settings_form" id='settings_default_gallery' name='settings_default_gallery'
+                        value='<?=$this->DefaultGallery?>'/></td></tr>
+                    <tr><td><?=$this->T('启用评论')?></td>
+                        <td><input type="checkbox" id="settings_enable_comments" name="settings_enable_comments"
+                        form="settings_form" <?=$this->CommentEnabled?"checked":""?>/></td></tr>
+                    <tr><td><?=$this->T('附加操作')?></td><td><a class='gray' href='index.php?extras=true'><?=$this->T('进入')?></a></td></tr>
+                        
+                    <tr><td class='smaller gray'>&nbsp;</td></tr>
+                    <tr><td class='smaller gray'><?=$this->T('实验访问')?></td></tr>
+                    <tr><td><?=$this->T('主机')?><?=isset($this->ExpHost)?"<a href='?post=".$this->ExpHost."'>→</a>":""?></td>
+                        <td><input type="text" form="settings_form" id='settings_exp_host' name='settings_exp_host'
+                        value='<?=$this->ExpHost?>'/></td></tr>
+                    <tr><td><?=$this->T('网站标题')?></td>
+                        <td><input type="text" form="settings_form" id='settings_exp_title' name='settings_exp_title'
+                            value='<?=$this->ExpTitle?>'/></td></tr>
+                    <tr><td><?=$this->T('短标题')?></td>
+                        <td><input type="text" form="settings_form" id='settings_exp_short_title' name='settings_exp_short_title'
+                            value='<?=$this->ExpShortTitle?>'/></td></tr>
+                    <tr><td><?=$this->T('首次提示')?><?=isset($this->ExpCaution)?"<a href='?post=".$this->ExpCaution."'>→</a>":""?></td>
+                        <td><input type="text" form="settings_form" id='settings_exp_caution' name='settings_exp_caution'
+                        value='<?=$this->ExpCaution?>'/></td></tr>
+                    <tr><td><?=$this->T('索引')?><?=isset($this->ExpIndex)?"<a href='?post=".$this->ExpIndex."'>→</a>":""?></td>
+                        <td><input type="text" form="settings_form" id='settings_exp_index' name='settings_exp_index'
+                        value='<?=$this->ExpIndex?>'/></td></tr>
+                    <tr><td><?=$this->T('导航栏')?>
+                        <?=isset($this->ExpNavigation)?"<a href='?post=".$this->ExpNavigation."'>→</a>":""?></td>
+                        <td><input type="text" form="settings_form" id='settings_exp_navigation' name='settings_exp_navigation'
+                        value='<?=$this->ExpNavigation?>'/></td></tr>
+                    <tr><td><?=$this->T('脚注')?><?=isset($this->ExpFooter)?"<a href='?post=".$this->ExpFooter."'>→</a>":""?></td>
+                        <td><input type="text" form="settings_form" id='settings_exp_footer' name='settings_exp_footer'
+                        value='<?=$this->ExpFooter?>'/></td></tr>
+                        
+                    <tr><td class='smaller gray'>&nbsp;</td></tr>
+                    <tr><td class='smaller gray'><?=$this->T('管理员')?></td><td class='smaller'>
+                        <a href='index.php?logout=true'><?=$this->T('登出')?></a></td></tr>
+                    <tr><td><?=$this->T('帐号')?></td>
+                        <td><input type="text" form="settings_form" id='settings_id' name='settings_id'
+                            value='<?=$this->Admin?>'/></td></tr>
+                    <tr><td><?=$this->T('新密码')?></td>
+                        <td><input type="password" form="settings_form"
+                            id='settings_new_password' name='settings_new_password' /></td></tr>
+                    <tr><td><?=$this->T('再次输入')?></td>
+                        <td><input type="password" form="settings_form"
+                            id='settings_new_password_redo' name='settings_new_password_redo' /></td></tr>
+                    <tr><td><?=$this->T('旧密码')?></td>
+                        <td><input type="password" form="settings_form" id='settings_old_password' name='settings_old_password' /></td></tr>
+                <?php }else{ ?>
+                    <form action="<?=$_SERVER['REQUEST_URI']?>" method="post" style='display:none;' id='login_form'></form>
+                    <colgroup><col style='min-width:3em;'><col style='width:100%;min-width:5em;'></colgroup>
+                    <tr><td class='smaller gray'><?=$this->T('请登录')?></td></tr>
+                    <tr><td><?=$this->T('帐号')?></td>
+                        <td><input type="text" form="login_form" id='login_id' name='login_id' /></td></tr>
+                    <tr><td><?=$this->T('密码')?></td>
+                        <td><input type="password" form="login_form" id='login_password' name='login_password' /></td></tr>
+                <?php } ?>
+            </table>
+            <?php if($this->LoggedIn){ ?>
+                <input class='button' form="settings_form" type="submit" name='settings_button' value='<?=$this->T('保存设置')?>' />
+            <?php }else{ ?>
+                <input class='button' form="login_form" type="submit" name='login_button' value='<?=$this->T('登录')?>' />
+            <?php } ?>
+        </div>
+    <?php
+    }
+    
+    function MakeExtraOperations(){?>
+        <div class='settings' style='overflow:auto;'>
+            <h2><?=$this->T('附加操作')?></h2>
+            <a href='?index.php&settings=true'><?=$this->T('返回一般设置')?></a>
+            <p>&nbsp;</p>
+            <h3><?=$this->T('自动重定向')?></h3>
+            <span class='smaller gray'>
+                <?=$this->T('P为帖子跳转,匹配REQUEST_URI跳到目标文章;S为站点跳转,可以重定向来源域名,例子:')?>
+            <br /><pre>P discount:20001001010101;<br />S old_domain:www.new_domain.com:20001001010101;</pre></span>
+            <form action="<?=$_SERVER['REQUEST_URI']?>" method="post" style='display:none;' id='settings_form2'></form>
+            <textarea id="settings_redirect" name="settings_redirect" rows="3" class='full_box' wrap="off"
+                form='settings_form2'><?=$this->DisplayRedirectConfig()?></textarea>
+            <input class='button' form="settings_form2" type="submit" name='settings_save_redirect'
+                value='<?=$this->T('保存重定向设置')?>' />
+            <p>&nbsp;</p>
+            <h3><?=$this->T('自定义翻译')?></h3>
+            <span class='smaller gray'>
+                <?=$this->T('填写格式:')?>
+            <br /><pre>- 语言 | Language</pre></span>
+            <form action="<?=$_SERVER['REQUEST_URI']?>" method="post" style='display:none;' id='settings_form3'></form>
+            <textarea id="settings_translation" name="settings_translation" rows="3" class='full_box' wrap="off"
+                form='settings_form3'><?=$this->CustomTranslationContent?></textarea>
+            <input class='button' form="settings_form3" type="submit" name='settings_save_translation'
+                value='<?=$this->T('保存翻译')?>' />
+            <p>&nbsp;</p>
+            <p class='smaller gray'><?=$this->T('当心!下列操作将立即执行:')?></p>
+            <ul>
+                <li><a href='index.php?rewrite_styles=true'><?=$this->T('重新写入默认CSS')?></a></li>
+                <li><a href='index.php?regenerate_thumbnails=true'><?=$this->T('重新生成图片缩略图')?></a></li>
+                <li><a href='index.php?clear_all_logins=true'><?=$this->T('删除所有登录')?>(<?=sizeof($this->LoginTokens);?>)</a></li>
+            </ui>
+            <br /><br /><br /><a href='?index.php&settings=true'><?=$this->T('返回一般设置')?></a><br />&nbsp;
+        </div>
+    <?php
+    }
+    
+    function MakeMainBegin(){?>
+        <div class='main' <?php if(!$this->InExperimentalMode){ ?>
+            ondrop="_dropHandler(event);" ondragover="_dragOverHandler(event);"<?php } ?>>
+    <?php
+    }
+    function MakeMainEnd(){?>
+        </div>
+    <?php
+    }
+    
+    function MakeExpFooter(){?>
+        <div class='small_footer'>
+            <hr />
+            <b><?=$this->T($this->ExpTitle)?></b>&nbsp;©<?=$this->T($this->DisplayName)?>
+        </div>
+        <div class='footer'>
+            <div style='white-space:nowrap;'>
+                <div class='footer_additional'>
+                <?php if(isset($this->ExpFooter) && ($p = &$this->GetPost($this->ExpFooter))!=NULL){
+                    echo $this->TranslatePostParts($this->GenerateSinglePost($p, false, false, false, false,$this->NULL_POST,false));
+                } ?>
+                </div>
+            </div>
+        </div>
+        </div><!-- page -->
+        </body>
+        </html>
+        <script>
+            if(trans = document.getElementById('translate_button')){
+                trans.href='https://translate.google.com/translate?sl=auto&tl=en-US&u='+encodeURIComponent(document.location.href);
+            }
+        </script>
+    <?php    
+    }
+    
+    function MakeFooter(){?>
+        <div class='small_footer'>
+            <hr />
+            <b><?=$this->T($this->Title)?></b>
+            <span onclick='event.stopPropagation()'
+                ondblclick='javascript:window.location.href="index.php?settings=true"'>©</span><?=$this->T($this->DisplayName)?>
+        </div>
+        <div class='footer'>
+            <div style='white-space:nowrap;'>
+                <div class='footer_additional'>
+                <?php if(isset($this->SpecialFooter) && ($p = &$this->GetPost($this->SpecialFooter))!=NULL){
+                    echo $this->TranslatePostParts($this->GenerateSinglePost($p, false, false, false, false,$this->NULL_POST,false));
+                } ?>
+                </div>
+                <div class='footer_additional'>
+                <?php if(isset($this->SpecialFooter2) && ($p = &$this->GetPost($this->SpecialFooter2))!=NULL){
+                    echo $this->TranslatePostParts($this->GenerateSinglePost($p, false, false, false, false,$this->NULL_POST,false));
+                } ?>
+                </div>
+            </div>
+        </div>
+        <p>&nbsp;<p>
+        <div id='dropping_background' style='display:none;' onclick='this.style.display="none";'
+            ondrop="_dropHandler(event);" ondragover="_dragOverHandler(event);">
+            <h2 style='width:100%;'><?=$this->T('上传到这里')?></h2>
+        </div>
+        <div id='big_image_overlay' style='display:none'>
+            <div class='big_image_box clean_a' onclick='HideBigImage(1)'>
+                <div style='display:flex;align-items:center;height:100%;justify-content:center;width:100%;'><?=$this->T('请稍候')?></div>
+                <img id='big_image' onload="HideWaitingBar();"/>
+            </div>
+            <div class='big_side_box' onclick='HideBigImage(1);'>
+            <div class='big_image_box clean_a image_nav' onclick='HideBigImage(1)'>
+                <div ><a id='prev_image' class='image_nav_prev img_btn_hidden' onclick="event.stopPropagation();">
+                    <div class='lr_buttons'>←</div></a></div>
+                <div ><a id='next_image' class='image_nav_next img_btn_hidden' onclick="event.stopPropagation();">
+                    <div class='lr_buttons'>→</div></a></div>
+            </div>
+                <div class='side_box_mobile_inner'>
+                    <div class='inquiry_buttons img_btn_hidden' id='inquiry_buttons' onclick="event.stopPropagation();">
+                        <span style='display:none;' id='image_purchase' class='clean_a'>
+                            <b><a id='image_purchase_button' target="_blank">¥<?=$this->T('购买印刷品')?></a></b>
+                            <br class='hidden_on_desktop block_on_mobile' /></span>
+                        <b><a class='clean_a' id='image_download'><span id='download_processing'>↓</span><?=$this->T('下载')?></a></b>
+                        <?php if(isset($this->EMail) && $this->EMail!=""){ ?>
+                            &nbsp;<a class='clean_a' id='image_inquiry'>
+                            <b>@</b><?=$this->T('咨询')?></a>
+                        <?php } ?><hr class='hidden_on_desktop block_on_mobile' />
+                    </div>
+                    <div id='big_image_share' class='clean_a' onclick="event.stopPropagation();">
+                        <li>
+                            <a id='big_share_copy'>📋︎</a>
+                            <a id='big_share_pin' target='_blank'>Pin</a>
+                            <a id='big_share_twitter' target='_blank'>Twitter</a>
+                            <a id='big_share_weibo' target='_blank'><?=$this->T('微博')?></a>
+                        </li>
+                        <hr />
+                    </div>
+                    <div id='big_image_info' onclick="event.stopPropagation();"></div>
+                    <?php if($this->LoggedIn){ ?><div id='big_image_ops' onclick="event.stopPropagation();">
+                        <br /><?=$this->T('印刷品链接')?>
+                        <form action="" method="post" style='display:none;' id='image_ops_form'></form>
+                        <input type='text' id='image_ops_product_link' name='image_ops_product_link' form="image_ops_form" >
+                        <?=$this->T('重命名')?>
+                        <input type='text' id='image_edit_new_name' name='image_edit_new_name' form="image_ops_form" >
+                        <input class='button' form="image_ops_form" type="submit" name='image_button' value=<?=$this->T('保存')?> />
+                        <br /><br /><?=$this->T('替换图像')?>
+                        <form action="" method="post" style='display:none;' id='image_edit_form' enctype="multipart/form-data"></form>
+                        <input type="file" form='image_edit_form'
+                            id='big_image_upload' name='upload_file_name' accept="image/x-png,image/png,image/gif,image/jpeg"/><br />
+                        <input class='button' form="image_edit_form" type="submit" name='image_replace_button'
+                            value=<?=$this->T('执行')?> />
+                    </div><?php } ?>
+                </div>
+            </div>
+        </div>
+        
+        </div><!-- page -->
+        </body>
+        </html>
+        <script>
+            <?=$this->ExtraScripts?>
+            if(back = document.getElementById('button_back')){
+                if(document.referrer.indexOf(location.protocol + "//" + location.host) == 0){
+                    back.href='javascript:history.back()';
+                }else{
+                    back.href='index.php';
+                }
+            }
+            function copy_text(t) {
+                ta = document.createElement('textarea');
+                document.body.appendChild(ta);
+                ta.value = t;
+                ta.select();
+                document.execCommand("copy");
+                document.body.removeChild(ta);
+            }
+            if(trans = document.getElementById('translate_button')){
+                trans.href='https://translate.google.com/translate?sl=auto&tl=en-US&u='+encodeURIComponent(document.location.href);
+            }
+            <?php if($this->LoggedIn){ ?>
+                function ShowSideUploader(){
+                    ShowRightSide(true,null);
+                    put = document.querySelector("#_uploader");
+                    put.style.display='block';
+                    RefreshSideGallery();
+                }
+                dmark = document.querySelector("#mark_details");
+                drename = document.querySelector("#rename_details");
+                drename_form = document.querySelector("#post_rename_form");
+                drename_input = document.querySelector("#post_rename_name");
+                var rp = document.querySelector('#post_reply_to');
+                var _reply_to = rp?rp.defaultValue:"";
+                function RestoreReply(){
+                    if(rp){
+                        rp.defaultValue = _reply_to;
+                    }
+                    document.querySelector('#post_reply_restore').style.display='none';
+                    ht = document.querySelector('#post_hint_text');
+                    if (_reply_to!=""){
+                        ht.style.display='block';
+                        ht.innerHTML="<?=$this->T('继续补充该话题:')?>";
+                    }else{
+                        ht.style.display='none';
+                    }
+                    document.querySelector('#post_edit_target').value="";
+                }
+                function MakeRefer(id){
+                    t = document.querySelector('#post_content')
+                    t.focus();
+                    v = t.value;
+                    t.value = v.slice(0, t.selectionStart) + "[<?=$this->T('引用文章')?>]("+id.toString()+")" + v.slice(t.selectionEnd);
+                    la_auto_grow(t);
+                    if(rp){
+                        rp.defaultValue = "";
+                        ht = document.querySelector('#post_hint_text');
+                        ht.innerHTML = "<?=$this->T('引用并发送新话题:')?>"; ht.style.display='block';
+                        document.querySelector('#post_reply_restore').style.display='inline';
+                        rs = document.querySelector('#post_reply_restore');
+                        rs.style.display='inline'; rs.innerHTML="<?=$this->T('切换为回复')?>";
+                        document.getElementById('post_menu').style.display='none';
+                    }
+                }
+                function CopyRefer(id){
+                    copy_text(id.toString());
+                    menu.querySelector('#menu_refer_copy').innerHTML="<?=$this->T('已复制')?>";
+                }
+                function MakeEdit(id){
+                    ed = document.getElementById('menu_edit');
+                    ed.innerHTML="<?=$this->T('稍等')?>";
+                    ed.href="#";
+                    var xhr = new XMLHttpRequest();
+                    xhr.onreadystatechange = function() {
+                        if (this.readyState == 4 && this.status == 200) {
+                            ed.innerHTML='——';
+                            ht = document.querySelector('#post_hint_text');
+                            ht.innerHTML = "<?=$this->T('修改帖子:')?>"; ht.style.display='block';
+                            rs = document.querySelector('#post_reply_restore');
+                            rs.style.display='inline'; rs.innerHTML="<?=$this->T('取消')?>";
+                            t = document.querySelector('#post_content');
+                            t.value=xhr.responseText.trim();
+                            t.focus();
+                            la_auto_grow(t);
+                            document.querySelector('#post_edit_target').value=id;
+                            document.getElementById('post_menu').style.display='none';
+                        }
+                    };
+                    xhr.open("GET", "index.php?post="+id.toString()+'&post_original=true', true);
+                    xhr.send();
+                }
+                function MarkDelete(id){
+                    p = document.querySelector('[data-post-id="'+id+'"]');
+                    op = p.dataset.markDelete?"false":"true";
+                    window.location.href=
+                        "index.php?<?=isset($_GET['post'])?('post='.$_GET['post']):""?>&mark_delete="+op+'&target='+id.toString();
+                }
+                function SetMark(mark){
+                    menu = document.getElementById('post_menu');
+                    window.location.href=
+                        "index.php?<?=isset($_GET['post'])?('post='.$_GET['post']):""?>&target="+
+                        menu.parentNode.dataset.postId+"&set_mark="+mark;
+                }
+                function ToggleMarkDetails(){
+                    if(dmark.style.display=='block') dmark.style.display='none';
+                    else dmark.style.display='block';
+                }
+                function ToggleRenameDetails(){
+                    if(drename.style.display=='block') drename.style.display='none';
+                    else drename.style.display='block';
+                }
+            <?php } ?>
+            function ShowPostMenu(post){
+                menu = document.getElementById('post_menu');
+                menu.style.display='block';
+                menu.parentNode.removeChild(menu);
+                post.appendChild(menu);
+                menu.style.display='block';
+                id = post.dataset.postId
+                menu.querySelector('#_time_hook').innerHTML = ''+id.substring(2,4)+'/'+id.substring(4,6)+'/'+id.substring(6,8)+
+                                                              ' '+id.substring(8,10)+':'+id.substring(10,12);
+                window.onClick="HidePopMenu()";
+                menu.onClick=(function(event){
+                  event.stopPropagation();
+                });
+                <?php if($this->LoggedIn){ ?>
+                    menu.querySelector('#menu_refer').href='javascript:MakeRefer(id)';
+                    menu.querySelector('#menu_refer_copy').href='javascript:CopyRefer(id)';
+                    ed = menu.querySelector('#menu_edit')
+                    ed.href='javascript:MakeEdit(id)'; ed.innerHTML="<?=$this->T('修改')?>";
+                    d = menu.querySelector('#menu_delete');
+                    d.href='javascript:MarkDelete(\"'+id+'\")';
+                    p = document.querySelector('[data-post-id="'+id+'"]');
+                    d.innerHTML = p.dataset.markDelete?"<?=$this->T('恢复')?>":"<?=$this->T('删除')?>";
+                    menu.querySelector('#mark_details').dataset.id=id;
+                    drename_input.value=id;
+                    drename_form.action="<?=$_SERVER['REQUEST_URI'];?>"+"&rename_post="+id;
+                <?php } ?>
+                
+                title = document.title;
+                copy = document.getElementById('share_copy');
+                copy.innerHTML='&#128203;&#xfe0e;';
+                copy.addEventListener("click", function(){
+                    url = window.location
+                    path = location.pathname
+                    copy_text(url.protocol+"//"+url.host+path+"?post="+id);
+                    this.innerHTML='&#128203;&#xfe0e;&#10003;&#xfe0e;';
+                });
+                document.getElementById('share_pin').href='https://www.pinterest.com/pin/create/button/?url='+
+                    encodeURIComponent(window.location.href)+
+                    //'&media='+abs_img+
+                    '&description='+encodeURIComponent(title);
+                document.getElementById('share_twitter').href='http://twitter.com/share?text='+
+                    encodeURIComponent(title)+
+                    '&url='+window.location.href+
+                    "&hashtags="
+                document.getElementById('share_weibo').href='https://service.weibo.com/share/share.php?title='+
+                    encodeURIComponent(title)+
+                    ': '+window.location.href
+            }
+            function HidePopMenu(){
+                var menus = document.querySelectorAll('.pop_menu');
+                [].forEach.call(menus, function(m){m.style.display='none';});
+            }
+            var posts = document.querySelectorAll('.center .post');
+            [].forEach.call(posts, function(p){
+                if(s=p.querySelector('._menu_hook')) s.addEventListener("click", function() {
+                    ShowPostMenu(this.parentNode.parentNode);
+                });
+            });
+            var post_clickables = document.querySelectorAll('.center .post a');
+            [].forEach.call(post_clickables, function(p){
+                p.addEventListener("click", function(event){
+                    event.stopPropagation();
+                });
+            });
+            
+            function FindImage(imgsrc){
+                for(var i=0;i<document.images_filtered.length;i++){
+                    if (document.images_filtered[i].dataset.imgsrc==imgsrc) return document.images_filtered[i]
+                }
+                return null
+            }
+            
+            var dottime1;
+            var dottime2;
+            
+            function ToDataURL(url) {
+                return fetch(url).then((response) => {return response.blob();}).then(blob => {
+                    clearTimeout(dottime1);clearTimeout(dottime2);
+                    ps = document.querySelector('#download_processing');
+                    ps.innerHTML='↓';ps.style.opacity='';
+                    return URL.createObjectURL(blob);});
+            }
+            async function DownloadAsImage(url,name) {
+                const a = document.createElement("a"); a.href = await ToDataURL(url); a.download = name;
+                document.body.appendChild(a); a.click(); document.body.removeChild(a);
+            }
+            function basename(url){return url.split(/[\\/]/).pop();}
+            
+            pushed=0;
+            function ShowBigImage(imgsrc,do_push){
+                ShowWaitingBar();
+                share = document.querySelector('#big_image_share');
+                img = document.querySelector('#big_image');
+                down = document.querySelector('#image_download');
+                img.src = "";
+                img.src = src = "images/"+imgsrc;
+                down.href="images/"+imgsrc;
+                var downname='<?=$this->T($this->Title);?>_<?=$this->T($this->DisplayName);?>_+<?=$this->GiveSafeEMail()?>+_'+
+                    basename(down.href);
+                img.alt = downname; down.download=downname;
+                var use_href = "images/"+imgsrc;
+                
+                down.onclick=function(event){event.stopPropagation();event.preventDefault();
+                ps = this.querySelector('#download_processing');ps.innerText='…';
+                var dotwait = function(ps){ var self = this;
+                    dottime1 = setTimeout(function(ps){
+                        ps.style.opacity='0'; dottime2 = setTimeout(function(ps){ps.style.opacity='1'; dotwait(ps);},300,ps); },300,ps) }
+                dotwait(this.querySelector('#download_processing'));
+                DownloadAsImage(use_href,downname);};
+
+                
+                if(do_push){PushGalleryHistory(src)}
+                
+                page_url = encodeURIComponent(window.location.href);
+                
+                <?php if(isset($this->EMail) && $this->EMail!=""){ ?>
+                    inqb = document.querySelector('#image_inquiry');
+                    inqb.href="mailto:<?=$this->T($this->DisplayName);?>\<<?=$this->EMail?>\>?subject=<?=$this->T('网站图片咨询');?>&body="+
+                        encodeURIComponent("<?=$this->T('你好!我对你网站上的这张图片感兴趣:')?>"+'\n\n')+
+                        (page_url)+encodeURIComponent('\n\n');
+                <?php } ?>
+                
+                this_image = FindImage(imgsrc);
+                if(this_image.dataset.prevsrc){
+                    new_image = FindImage(this_image.dataset.prevsrc);
+                    new_prev = new_image?new_image.dataset.prevsrc:null;
+                    im = document.querySelector('#prev_image');
+                    if(new_prev) {im.href='javascript:ShowBigImage("'+this_image.dataset.prevsrc+'",'+do_push+')';im.style.opacity='';}
+                    else {im.style.opacity='0';im.removeAttribute("href");}
+                }
+                if(this_image.dataset.nextsrc){
+                    new_image = FindImage(this_image.dataset.nextsrc);
+                    new_next = new_image?new_image.dataset.nextsrc:null;
+                    im = document.querySelector('#next_image');
+                    if(new_next) {im.href='javascript:ShowBigImage("'+this_image.dataset.nextsrc+'",'+do_push+')';im.style.opacity='';}
+                    else {im.style.opacity='0';im.removeAttribute("href");}
+                }
+                purchase = document.querySelector('#image_purchase');
+                purchase_btn = document.querySelector('#image_purchase_button');
+                <?php if($this->LoggedIn){ ?>
+                    product_link = document.querySelector('#image_ops_product_link');
+                    product_form = document.querySelector('#image_ops_form');
+                    edit_form = document.querySelector('#image_edit_form');
+                    edit_new_name = document.querySelector('#image_edit_new_name');
+                    product_form.action = window.location.href;
+                    edit_form.action = window.location.href;
+                    edit_new_name.value = imgsrc.split('.')[0];
+                    if(this_image.dataset.product){
+                        product_link.value = this_image.dataset.product;
+                    }else{
+                        product_link.value = "";
+                    }
+                <?php } ?>
+                if(this_image.dataset.product){
+                    purchase.style.display='';
+                    href = this_image.dataset.product;
+                    if(this_image.dataset.product.match('^[0-9]{14}$')){
+                        href = '?post='+href;
+                    }
+                    purchase_btn.href = href;
+                }else{
+                    purchase.style.display='none';
+                    purchase_btn.removeAttribute("href");
+                }
+                
+                DelayHideImgBtn(null);
+                
+                title = encodeURIComponent(document.title);
+                copy = document.getElementById('big_share_copy');
+                copy.innerHTML='&#128203;&#xfe0e;';
+                copy.addEventListener("click", function(){
+                    copy_text(window.location.href);
+                    this.innerHTML='&#128203;&#xfe0e;&#10003;&#xfe0e;';
+                });
+                document.getElementById('big_share_pin').href='https://www.pinterest.com/pin/create/button/?url='+
+                    page_url+'&media='+window.location.host+'/'+src+'&description='+title;
+                document.getElementById('big_share_twitter').href='http://twitter.com/share?text='+
+                    title+'&url='+page_url+"&hashtags=";
+                document.getElementById('big_share_weibo').href='https://service.weibo.com/share/share.php?title='+
+                    title+': '+page_url;
+                
+                o = document.querySelector('#big_image_overlay');
+                info = document.querySelector('#big_image_info');
+                info.innerHTML="<?=$this->T('正在查询……')?>";
+                o.style.display="block";
+                ShowBackdrop(0.8);
+                var xhr = new XMLHttpRequest();
+                xhr.onreadystatechange = function() {
+                    if (this.readyState == 4 && this.status == 200) {
+                        info = document.querySelector('#big_image_info');
+                        var response = xhr.responseText;
+                        let content=""
+                        if(res = response.match(/<ref>(.*)<\/ref>/u)){
+                            content="<span class='small'><?=$this->T('该图片出现在')?> "+res[1]+" <?=$this->T('个帖子中')?></span>"+content;
+                        }else{content="<span class='smaller gray'><?=$this->T('该图片未被引用')?></span>"+content;}
+                        if(res = response.match(/<insert>([\s\S]*)<\/insert>/u)){
+                            content+="<div class='clean_a'>"+res[1]+"</div>";
+                        }
+
+                        info.innerHTML=content;
+                    }
+                };
+                xhr.open("GET", "index.php?image_info="+imgsrc+"", true);
+                xhr.send();
+            }
+            function HideBigImage(do_push){
+                o = document.querySelector('#big_image_overlay');
+                img = document.querySelector('#big_image');
+                img.src = "";
+                o.style.display="none";
+                HideBackdrop();
+                if(do_push){PushGalleryHistory("");}
+                HideWaitingBar();
+            }
+            var lbtn=document.querySelector('#prev_image'),rbtn=document.querySelector('#next_image');
+            var inq=document.querySelector('#inquiry_buttons');
+            var overlay=document.querySelector('#big_image_overlay');
+            var hide_timeout;
+            function DontHideImgBtn(e){ clearTimeout(hide_timeout); e.stopPropagation(); }
+            function DelayHideImgBtn(e){
+                lbtn.classList.remove('img_btn_hidden');
+                rbtn.classList.remove('img_btn_hidden');
+                inq.classList.remove('img_btn_hidden');
+                clearTimeout(hide_timeout);
+                hide_timeout = setTimeout(function(e1,e2,e3){e1.classList.add('img_btn_hidden');
+                    e2.classList.add('img_btn_hidden');
+                    e3.classList.add('img_btn_hidden');}, 1000, lbtn, rbtn, inq);
+            }
+            lbtn.addEventListener('mousemove',DontHideImgBtn);lbtn.addEventListener('mouseover',DontHideImgBtn);
+            rbtn.addEventListener('mousemove',DontHideImgBtn);rbtn.addEventListener('mouseover',DontHideImgBtn);
+            inq.addEventListener('mousemove',DontHideImgBtn);inq.addEventListener('mouseover',DontHideImgBtn);
+            overlay.addEventListener('mousemove',DelayHideImgBtn);
+            var images = document.querySelectorAll('img');
+            var images_filtered=new Array(); var imgadded = new Array();
+            var images_remaining=new Array();
+            [].forEach.call(images, function(img){
+                if(img.classList.contains("no_pop") || (!(imgsrc = img.dataset.imgsrc))) return;
+                if(imgadded.indexOf(imgsrc)>=0) {images_remaining.push(img); return;}
+                images_filtered.push(img);imgadded.push(imgsrc);
+            });
+            for(var i=0; i<images_filtered.length; i++){
+                previmg = nextimg = null; img = images_filtered[i];
+                if(i>0) previmg=images_filtered[i-1];
+                if(i<images_filtered.length-1) nextimg=images_filtered[i+1];
+                prevsrc=previmg?previmg.dataset.imgsrc:null; nextsrc=nextimg?nextimg.dataset.imgsrc:null; 
+                img.dataset.prevsrc = prevsrc; img.dataset.nextsrc = nextsrc; 
+                function wrap(imgsrc){return function(){ShowBigImage(imgsrc, 1);}}
+                img.addEventListener("click", wrap(img.dataset.imgsrc));
+            }
+            for(var i=0; i<images_remaining.length; i++){
+                img = images_remaining[i];
+                function wrap(imgsrc){return function(){ShowBigImage(imgsrc, 1);}}
+                img.addEventListener("click", wrap(img.dataset.imgsrc));
+            }
+            document.images_filtered = images_filtered;
+            function PopGalleryHistory(){
+                if(pushed){
+                    pushed = 0;
+                    try{
+                        history.back();
+                    }catch{
+                        console.log("can't do it.");
+                    }
+                }
+            }
+            function PushGalleryHistory(src){
+                abs_img = window.location.protocol+"//"+window.location.host+'/'+src
+                title = "照片"
+                extra = "?";
+                sp = new URLSearchParams(window.location.search)
+                if(sp.has('post')){extra+="post="+sp.get('post')}
+                if(sp.has('gallery')){extra+="&gallery="+sp.get('gallery')}
+                try{
+                    window.history.pushState('&pic='+src, 'Title', extra+'&pic='+src);
+                }catch{
+                    console.log("can't do it.");
+                }
+                pushed = 1;
+            }
+            document.addEventListener('keydown', function(e){
+                large = document.getElementById('big_image_overlay')
+                if (large.style.display!='block') return;
+                if(e.key=='Escape'||e.key=='Esc'||e.keyCode==27){
+                    HideBigImage(1);
+                }
+            }, true);
+            window.addEventListener('popstate', (event) => {
+                if(event.state){
+                    let sp = new URLSearchParams(event.state)
+                    if(sp.has('pic')){
+                        src = sp.get('pic')
+                        if(onlyimg = src.match(/[0-9]{14,}.(jpg|png|jpeg|gif)/u)) ShowBigImage(onlyimg[0], 0);
+                        else{HideBigImage(0);}
+                    }
+                }else{HideBigImage(0);}
+            });
+            
+            let searchParams = new URLSearchParams(window.location.search)
+            if(searchParams.has('pic')){
+                src = searchParams.get('pic')
+                if(onlyimg = src.match(/[0-9]{14,}.(jpg|png|jpeg|gif)/u)){
+                    ShowBigImage(onlyimg[0], 1);
+                }
+            }
+            function _dropHandler(event){ if (typeof dropHandler === "function") dropHandler(event); }
+            function _dragOverHandler(event){ if (typeof dragOverHandler === "function") dragOverHandler(event); }
+        </script>
+        </body>
+    <?php
+    }
+    
+    function DoIdentiyExperimental(){
+        //$this->InExperimentalMode = 1;
+
+        if(!isset($this->ExpHost) || $this->ExpHost=="") return;
+        if(preg_match('/'.preg_quote($this->ExpHost).'/u', $_SERVER['HTTP_HOST'])){
+            $this->InExperimentalMode=True;
+        }
+        if(!$this->CurrentPostID && $this->InExperimentalMode){
+            $this->CurrentPostID = $this->ExpIndex;
+        }
+    }
+    function DoExperimentalTopLink($p){
+        if($this->InExperimentalMode && $p){
+            if(isset($p['tid']) && $p['tid']['first']!=$p){
+                header('Location: ?post='.$p['tid']['first']['id']); exit();
+            }
+        }
+    }
+    function MakeExperimentalConfirm(){
+        if(isset($_COOKIE['la_experimental']) && $_COOKIE['la_experimental'] == 'confirmed'){
+            return false;
+        }
+        $caution_html = 
+        $confirm = "<a class='text_highlight clean_a bold' href='index.php?confirm_enter=1".
+                      (isset($this->CurrentPostID)?("&post=".$this->CurrentPostID):"").
+                        "'>&nbsp;&nbsp;".$this->T('继续')."&nbsp;&nbsp;</a>";
+        ?>
+        <div class='center_exp'><?php
+            if(isset($this->ExpCaution) && ($p=$this->GetPost($this->ExpCaution)))$this->MakeSinglePostExp($p);
+            else echo "<li class='post post_dummy'>".$this->TranslatePostParts("<h1>注意</h1><p>您将进入实验站。</p>")."</li>";
+        ?></div>
+        <div class='center_exp'><li class='post post_dummy'><p><?=$confirm?></p></li></div>
+        <?php return true;
+    }
+}
+
+$la = new LA;
+
+$la->DoSiteRedirect();
+
+$la->DoLogin();
+
+$err = $la->ProcessRequest($message, $redirect);
+
+$la->DoIdentiyExperimental();
+
+$la->SwitchLanguage();
+
+if($err){
+    echo $message;
+    exit();
+}
+
+if(isset($redirect)){
+    header('Location: '.$redirect);
+    exit();
+}
+
+$la->DetectPageType();
+
+$la->ReadImages(false);
+$la->ReadPosts();
+
+$p = &$la->GetPost($la->CurrentPostID);
+
+if(!$la->CanShowPost($p)) $p=NULL;
+else{ $la->DoExperimentalTopLink($p); }
+
+$la->MakeHeader($p);
+$la->MakeMainBegin();
+
+if($la->PageType=='experimental'){
+    if(!$la->MakeExperimentalConfirm()){
+        if($p){
+            $la->MakePostSectionExp($p);
+            $la->MakeLinkedPostsExp($p);
+        }else{
+            echo "<h2>".$la->T('未找到这个帖子')."</h2><p>".$_SERVER['REQUEST_URI'].
+                "</p><p><a href='index.php'>".$la->T('返回首页')."</a></p><br />";
+        }
+    }
+    $la->MakeMainEnd();
+    $la->MakeExpFooter();
+}else{
+    if($la->PageType=='extras'){
+        $la->MakeExtraOperations();
+    }else if($la->PageType=='settings'){
+        $la->MakeSettings();
+    }else if($la->PageType=='gallery'){
+        $la->MakeGalleryLeft();
+        $la->MakeGallerySection();
+    }else if($la->PageType=='post'){
+        if($p){
+            $made_interesting = false;
+            if($la->IsInterestingPost($p)){
+                $la->MakeInterestingSection($p['tid']);
+            }
+            else{
+                $la->MakeLinkedPosts($p);
+                $la->MakePostSection($p);
+                $la->MakeTOC();
+            }
+        }else{
+            echo "<h2>".$la->T('未找到这个帖子')."</h2><p>".$_SERVER['REQUEST_URI'].
+                "</p><p><a href='index.php'>".$la->T('返回首页')."</a></p><br />";
+        }
+    }else if($la->PageType=='search'){
+        $la->MakeHotPosts(true);
+        $la->MakeRecentPosts($_GET['search']);
+    }else if($la->PageType=='category'){
+        $la->MakeHotPosts(true);
+        $la->MakeRecentPosts(NULL,$_GET['category']);
+    }else if($la->PageType=='comments'){
+        $la->MakeCommentPosts();
+    }else{
+        $la->MakeHotPosts();
+        $la->MakeRecentPosts();
+    }
+    $la->MakeMainEnd();
+    $la->MakeFooter();
+}
+
+?>
+

+ 19 - 0
demo/la_config.md

@@ -0,0 +1,19 @@
+- Title = 示例知识库
+- ShortTitle = 示例
+- Admin = admin
+- DisplayName = 管理员
+- Password = $2y$10$ngl/oChwiivlgxe7vA0.rue7q9IFeYUfrJgjFb6SBHB8HTUcKL68C
+- EMail = 
+- SpecialNavigation = 
+- SpecialFooter = 
+- SpecialFooter2 = 
+- SpecialPinned = 
+- DefaultGallery = 
+- CommentEnabled = True
+- ExpHost = 
+- ExpTitle = 实验访问
+- ExpShortTitle = 实验访问
+- ExpCaution = 
+- ExpIndex = 
+- ExpNavigation = 
+- ExpFooter = 

+ 0 - 0
demo/la_redirect.md


+ 0 - 0
demo/la_tokens.md


+ 41 - 0
demo/posts/202201.md

@@ -0,0 +1,41 @@
+[LAMDWIKIPOST 20220118083046; NEXT 20220118083133; ]
+
+# 基于文本的知识库系统 帖子示例
+
+[LAMDWIKIPOST 20220118083133; NEXT 20220118083152; PREV 20220118083046; ]
+
+2016到2020年期间,我的博客先后使用了数个服务器端软件,其中使用时间最长的是我在2018年开始陆续制作的“那么的维基”软件,它是一个运行在服务器上的PHP脚本,从文章文件生成一种经典的方块造型页面,并在其中附加了动态信息以及附加的符号和格式。功能的逐渐增加使其变得臃肿、运行缓慢,同时降低了我继续维护它的动力。那么的维基允许我在浏览器中登录并直接建立和编辑文章、修改任务列表、访问多媒体附件、搜索文件等。但这些功能的实现和存在仅仅是因为“我可以实现它们”,而并不是我建立知识库的目标。而到最后我所需要的仅仅是一个传达图文信息的窗口,同时便于在其中索引。
+
+我的知识库自建立以来积累了相当多不同类型的文章,由于缺乏一致整理方式,文件开始变得难以导航,过时的信息难再返回标注。此前的全部内容已经保留为存档,我将适时将之前的一些文章迁移到新的知识库组织体系里。
+
+在2020年早期,我重新制作了一套运行在本地的静态博客系统,但是由于仍然有远程修改的需求,这套系统并没有真正有效地使用起来。
+
+![图片](images/20220118083330.jpg)
+
+[LAMDWIKIPOST 20220118083152; PREV 20220118083133; ]
+
+因此我需要基于上述要求重新设计一套[新的系统](20220118083246)。
+
+[LAMDWIKIPOST 20220118083220; NEXT 20220118083246; ]
+
+# MediaWiki
+
+{read_more}
+
+MediaWiki是一个免费的开源 wiki 软件。它于 2002年开发用于Wikipedia,并于 2003 年命名为“MediaWiki”。它仍在 Wikipedia 和几乎所有其他Wikimedia 网站上使用,包括Wiktionary、Wikimedia Commons和Wikidata;这些站点继续为 MediaWiki 定义大部分需求集。MediaWiki 最初由Magnus Manske开发并由Lee Daniel Crocker改进。从那时起,它的发展一直由维基媒体基金会维持。
+
+MediaWiki 是用PHP 编程语言编写的,并将所有文本内容存储到数据库中。该软件经过优化,可有效处理大型项目,这些项目可能具有 TB 级的内容和每秒数十万的视图。因为维基百科是世界上最大的网站之一,通过多层缓存和数据库复制实现可扩展性一直是开发人员关注的主要问题。 MediaWiki 的另一个主要方面是其国际化。它的界面支持 300 多种语言。软件有1000多个配置设置以及 1,800 多个可用于添加或更改各种功能的扩展。
+除了在 Wikimedia 网站上的使用外,MediaWiki 还被用作数千个公共和私人网站的知识管理和内容管理系统,包括Fandom、wikiHow网站以及Intellipedia和Diplopedia等主要内部设施。
+
+[LAMDWIKIPOST 20220118083246; PREV 20220118083220; REFS 20220118083152; ]
+
+示例回复
+
+[LAMDWIKIPOST 20220118092407; COMMENT 20220118083220; EMAIL 12313@123123.com; NAME 1312; IP 127.0.0.1; ]
+
+示例评论
+
+[LAMDWIKIPOST 20220118122947; ]
+
+这里可以写东西
+

+ 334 - 0
demo/styles/main.css

@@ -0,0 +1,334 @@
+
+html{font-size:18px;font-family:'Noto Serif CJK SC','Times New Roman','SimSun', Georgia, serif;}
+body{background-color:#231a0d;color:#f8ca9b;}
+sup,sub{line-height:0;}
+blockquote{border-left:2px solid #f8ca9b;padding-left:0.3em;}
+*{box-sizing:border-box;padding:0;margin:0;}
+.page,.page_gallery{padding:1em;padding-top:0;}
+.hidden_on_desktop,.hidden_on_wide{display:none;}
+.hidden_on_desktop_force{display:none !important;}
+::file-selector-button{background:none;border:none;}
+a,button,::file-selector-button{text-decoration:underline;color:#f8ca9b;}
+a:hover,.button:hover,::file-selector-button:hover{text-decoration:none;color:#ac7843;}
+.button:disabled{background-color:#ac7843;pointer-events:none;}
+header{position:sticky;top:0;background-color:#231a0d;z-index:10;padding-top:0.5em;max-height:100vh;overflow:auto;z-index:30;}
+.header_nav{display:inline;}
+header a,.left a,.footer a,.clean_a,.clean_a a{text-decoration:none;}
+header a:hover,.button:hover{color:#ac7843 !important;}
+.footer{background-color:#231a0d;z-index:10;position:relative;}
+.invert_a,.invert_a a{color:#ac7843;text-decoration:none;}
+.invert_a:hover,.invert_a a:hover{color:#f8ca9b !important;}
+.gray,.gray a{color:#ac7843;}
+hr{border:1px solid #ac7843;}
+header ul{display:inline-block;}
+header li{display:inline-block;}
+header li::before{content:' - '}
+header h1,header h2,header h3,header h4,header h5,header p{display:inline;font-size:1rem;}
+.main{position:relative;word-spacing:-1em;}
+.main *{word-spacing:initial;}
+pre{overflow:auto;max-width:100%;display:block;font-size:0.75em;}
+ul{display:block;}
+li{display:block;}
+table{width:100%;border-collapse:collapse;border-bottom:2px solid #f8ca9b;border-top:3px solid #f8ca9b;}
+table input{border:none!important;}
+table img{max-width:10rem !important;}
+td{padding-left:0.1em;padding-right:0.1em;}
+td:first-child{padding-left:0;}
+td:last-child{padding-right:0;}
+tbody tr:hover{box-shadow:inset 0 -2px 0 0px #f8ca9b;}
+thead{box-shadow:inset 0 -1px 0 0px #f8ca9b;position:sticky;top:2rem;background-color:#231a0d;z-index:5;}
+.post table{font-size:0.85em;}
+.interesting_tbody{background:linear-gradient(90deg, #231a0dff, #231a0d88 20em);}
+.interesting_tbody td{display:contents;}
+.interesting_tbody tr{position:relative;scroll-margin:3.5em}
+.interesting_tbody td>*{display:table-cell;}
+.interesting_tbody .post_access{padding-top:0;}
+.interesting_tbody .post_menu_button{top:0;opacity:0;}
+.interesting_tbody td>img{position:absolute;left:1.4em;z-index:-1;height:1em;width:20em;
+display:block;top:0.2em;object-fit:cover;max-width:calc(100% - 1.4em) !important;}
+.interesting_tbody .p_row{display:flex;position:absolute;left:1.4em;top:0;z-index:-1;flex-wrap:nowrap;max-width:calc(100% - 1.4em);}
+.interesting_tbody .p_thumb{height:1em;}
+.interesting_tbody .p_thumb img{max-height:10rem !important;max-width:20rem !important;}
+tr:hover .post_menu_button{opacity:1;}
+.post_current_row{background-color:#daae8010;}
+.align_right{text-align:right;}
+.left{display:inline-block;vertical-align:top;width:25%;height:calc(100vh - 5.2em);top:2em;
+position:sticky;overflow:auto;padding-right:0.2em;padding-bottom:4rem;}
+.center{display:inline-block;vertical-align:top;width:50%;padding-left:0.3em;overflow:visible;padding-bottom:4rem;}
+.center_wide{display:inline-block;vertical-align:top;width:75%;padding-left:0.3em;overflow:visible;padding-bottom:4rem;}
+.center_full{display:inline-block;vertical-align:top;width:100%;overflow:visible;padding-bottom:4rem;}
+.center_wide .p_thumb{height:10rem;}
+.sticky_title{position:sticky;top:1.35em;z-index:1;box-shadow:6em 3.5em 0.75em -3em inset #231a0d;pointer-events:none;}
+.center_exp{display:block;width:80%;margin:0 auto;overflow:visible;padding-bottom:1em;}
+.table_top{position:relative;left:calc(-50% - 0.45em);width:calc(200% + 0.6em);background:#231a0d;z-index:1;
+box-shadow:0px 0px 2em 1em #231a0d;margin-top:2em;margin-bottom:2em;}
+.right{display:inline-block;vertical-align:top;width:25%;position:sticky;top:2em;
+padding-left:0.5em;height:calc(100vh - 2.6em);overflow:auto;padding-bottom:4rem;}
+textarea,input[type=text],input[type=password]{width:100%;display:block;font-family:inherit;max-height:60vh;font-size:inherit;}
+select,textarea,input[type=text],input[type=password]{background:none;border:none;border-bottom:1px solid #f8ca9b;color:#f8ca9b;}
+.button{background:none;border:none;font-family:inherit;color:#f8ca9b;font-size:inherit;font-weight:bold;}
+.post{position:relative;scroll-margin:2.5em;border-radius:0.3em;
+padding-right:0rem;padding-left:0rem;padding-top:0.3rem;padding-bottom:0.3rem;margin-top:0.2em;margin-bottom:0.2em;}
+.center_exp .post{padding-left:0;padding-right:0;padding-top:0;padding-bottom:0;border-radius:0;}
+.post_width li,.post_width_big li,.footer_additional li,.footer_additional li,.post_dummy li
+{display:list-item;margin-left:1em;list-style:disc;}
+.post_width li li,.post_width_big li li,.footer_additional li li,.footer_additional li li,.post_dummy li li{list-style:circle;}
+.post_width > *,.post_width_big > *,.post_dummy > *,.post_ref > *{margin:0;margin-bottom:0.5em}
+.post_width > *:last-child,.post_width_big > *:last-child,.post_dummy > *:last-child,.post_ref > *:last-child{margin-bottom:0em;}
+.post_dummy > *{width:60%;margin:0 auto;margin-bottom:0.5em}
+.post_dummy > p img{display:block;width:100%;margin:0 auto;}
+.post h1,.post h2,.post h3,.post h4{margin-bottom:0.5rem;}
+.gallery_left li{display:list-item;margin-left:1em;list-style:none;}
+.gallery_left .selected{list-style:'→';}
+.focused_post{font-size:1.2em;margin-top:0.1em;margin-bottom:0.1em;padding:0.5rem !important;border:2px dashed #ac7843;}
+.post_width{position:relative;left:1.4rem;width:calc(100% - 1.7rem);padding-left:0.2em;overflow:visible;}
+.post_width_big{position:relative;left:0;width:100%;overflow:visible;}
+.post .post{padding:0;padding-top:0.3rem;}
+.post_menu_button{position:absolute;display:none;right:0rem;width:1.5rem;
+text-align:center;border-radius:0.3em;user-select:none;cursor:pointer;z-index:10;}
+.pointer{cursor:pointer;}
+.post:hover .post_menu_button{display:block;}
+.pop_menu{position:absolute;top:0.3rem;z-index:95;background-color:#675340;
+padding:0.3em;right:0.3rem;text-align:right;border-radius:0.3em;font-size:1rem;
+box-shadow:0px 0px 10px rgb(0, 0, 0);}
+.pop_menu li{list-style:none;margin-left:0;}
+.pop_menu hr{border:2px solid rgba(0,0,0,0.1);}
+.toc{left:60%;width:40%;top:0;position:absolute;}
+.post_access{width:1.4rem;top:0;position:absolute;height:100%;text-align:center;
+font-weight:bold;border-right:2px solid transparent;padding-top:0.3rem;}
+.post_access:hover{background-color:#daae8010;border-top-left-radius:0.3em;border-bottom-left-radius:0.3em;
+border-right:2px solid #f8ca9b !important;}
+.paa{width:1.4rem;min-width:1.4rem;}
+.opt_compact .post_access,.ref_compact .post_access{padding-top:0.2rem;border-right:2px solid #ac7843;}
+.post_box{border:1px solid #ac7843;border-radius:0.3em;padding:0.3em;}
+.post_box:hover,.post_menu_button:hover{background-color:#daae8010}
+#big_image_info .post_box:hover{background-color:#39270e;}
+.post_preview{font-size:0.9rem;overflow:hidden;}
+.post .post_ref{margin:0;padding-left:1.7rem;}
+.post_ref_main{display:inline-block;vertical-align:top;}
+.post_preview .post_ref_main{max-height:6rem;overflow:hidden;}
+.post_ref_images{overflow:hidden;}
+.page_selector{padding-top:2rem;text-align:center;}
+.smaller{font-size:0.85em;}
+.bigger{font-size:1.3em;}
+.block{display:block;}
+.opt_compact,.ref_compact{margin-top:0;}
+.opt_compact{margin-left:1.6rem;}
+.opt_compact .post_width {margin-left:0.3em;width: calc(100% - 1.8rem);}
+.post_box_top{padding-bottom:0.3em;padding-top:0.3em;}
+.post_box_fixed_bottom{position:sticky;bottom:0em;background-color:#231a0d;z-index:5;}
+.spacer{height:0.5em;}
+.pop_right,.pop_right_big{position:fixed;top:0;right:0;bottom:0;width:30%;z-index:100;background-color:#39270e;display:none;
+transition-timing-function:ease-out;padding:1rem;overflow:auto;}
+@keyframes pop_slide_in{0%{right:-30%;}100%{right:0%;}}
+@keyframes pop_slide_out{0%{right:0%;}100%{right:-30%;}}
+@keyframes pop_slide_in_big{0%{right:-30%;}100%{right:0%;}}
+@keyframes pop_slide_out_big{0%{right:0%;}100%{right:-30%;}}
+.backdrop{position:fixed;top:0;right:0;bottom:0;left:0;background-color:rgba(0,0,0,0.5);transition-timing-function:ease-out;z-index:90;}
+@keyframes backdrop_fade_in{0%{opacity:0%;}100%{opacity:100%;}}
+@keyframes backdrop_fade_out{0%{opacity:100%;}100%{opacity:0%;}}
+.toc_entry_1{font-size:1.1em;}
+.toc_entry_2{font-size:1.0em;padding-left:0.5rem;}
+.toc_entry_3{font-size:0.9em;padding-left:1rem;}
+.toc_entry_4{font-size:0.85em;padding-left:1.5rem;}
+.toc_entry_5{font-size:0.8em;padding-left:2rem;}
+h1,h2,h3,h4,h5{scroll-margin:1.5em;}
+{display:inline}
+.left ul h1,.left ul h2,.left ul h3,.left ul h4,.left ul h5,.left ul p
+{font-size:1em;}
+.deleted_post{color:#ac7843;text-decoration:line-through;}
+#file_list{margin-top:0.5em;}
+.file_thumb img{max-height:100%;max-width:100%;object-fit:cover;min-width:100%;min-height:100%;}
+#file_list li{margin-bottom:0.3em;}
+.ref_thumb{white-space:nowrap;overflow:hidden;}
+.ref_thumb .file_thumb{width:3em;height:3em;}
+.side_thumb li{margin:0.4em;display:inline-block;}
+.file_thumb{width:4em;height:4em;display:inline-block;line-height:0;vertical-align:middle;overflow:hidden;}
+.p_row{display:flex;flex-wrap:wrap;}
+.p_thumb{display:flex;flex-grow:1;height:6rem;margin-right:0.25rem;margin-bottom:0.25rem;overflow:hidden;position:relative;}
+.p_thumb img{object-fit:cover;max-height:100%;min-width:100%;}
+.ref_count,.p_thumb .post_menu_button{text-shadow: 0px 0px 10px rgb(0, 0, 0);}
+.p_thumb:hover .post_menu_button{display:block;}
+.p_thumb_selected{color:#f8ca9b !important;}
+.p_thumb_selected{display:block;}
+.post .p_thumb img{max-height:6rem;}
+.big_image_box{position:fixed;top:0;bottom:0;left:0;width:75%;z-index:95;text-align:center;pointer-events:none;}
+.big_image_box *{pointer-events:auto;}
+.big_image_box img{position:absolute;margin:auto;top:0;left:0;right:0;bottom:0;cursor:unset;}
+.big_side_box{position:fixed;top:0;bottom:0;right:0;width:25%;overflow:auto;z-index:98;color:#f8ca9b;padding:1rem;
+background:linear-gradient(to right, rgba(0,0,0,0), rgb(1, 1, 1));transition:background-size .2s linear;background-size: 300% 100%;}
+.big_side_box:hover{background-size: 100% 100%;}
+.big_side_box a,.big_side_box hr,#dropping_background{color:#f8ca9b;}
+.big_side_box a:hover{color:#ac7843;}
+#dropping_background{background-color:rgba(0,0,0,0.4);position:fixed;top:0;right:0;bottom:0;left:0;z-index:100;text-align:center;
+box-shadow:0px 0px 500px black inset;display:flex;align-items:center;}
+img{cursor:pointer;max-height:100%;max-width:100%;}
+.post img{max-height:min(70vh, 20rem);max-width:min(100%, 20rem);}
+.post > a > img{display:block;margin:0.3em auto;}
+.post .original_img{max-width:100%;display:block;margin-left:auto;margin-right:auto;max-width:100%;max-height:90vh;}
+.original_img img{max-height:90vh;max-width:100%;}
+.p_row .original_img{margin-bottom:0;}
+.post_ref .original_img{margin:unset;max-width:unset;max-height:min(70vh, 20rem);max-width:min(100%, 20rem);}
+.b ul{font-size:1.4em;}
+no_pop{cursor:unset;}
+p{min-height:0.8em;}
+.bold{font-weight:bold;}
+.footer_additional{display:inline-block;width:50%;vertical-align:text-top;white-space:normal;}
+.small_footer{position:sticky;bottom:0em;background-color:#231a0d;padding-bottom:0.5em;z-index:10;overflow:auto;white-space:nowrap;}
+.top_post_hint{margin-left:1.5em;font-weight:bold;}
+.white{color:#231a0d;}
+.full_box{border:1px solid #f8ca9b !important;padding:0.3rem;overflow:auto;}
+.image_nav_prev,.image_nav_next{z-index:100;position:absolute;line-height:0;height:100%;width:20%;display:flex;align-items:center;
+transition:background-size .2s ease;padding:0.5em;text-shadow:0px 0px 5px black;user-select:none;pointer-events:auto;}
+.image_nav_prev{left:0;justify-content:left;background:linear-gradient(to left, rgba(0,0,0,0), rgba(0,0,0,0.2));
+background-repeat:no-repeat;background-size:0% 100%;}
+.image_nav_prev:hover,.image_nav_next:hover{background-size:100% 100%;}
+.image_nav_next{right:0;justify-content:right;background:linear-gradient(to right, rgba(0,0,0,0), rgba(0,0,0,0.2));
+background-repeat:no-repeat;background-size:0% 100%;transition:background-size .2s ease;background-position-x:100%;}
+.inquiry_buttons{position:fixed;left:0;right:25%;text-align:center;bottom:1em;margin:0 auto;width:max-content;
+background-color:rgba(0,0,0,0.5);z-index:110;padding:0.2em;padding-left:1em;padding-right:1em;
+border-radius:1em;box-shadow:0px 0px 5px;text-shadow:0px 0px 5px black;opacity:1;user-select:none;}
+.lr_buttons{background-color:rgba(0,0,0,0.5);padding:0.5em;padding-top:1em;padding-bottom:1em;
+border-radius:1em;box-shadow:0px 0px 5px;font-size:1.2rem;text-shadow:0px 0px 5px black;}
+.img_btn_hidden{opacity:0;transition:opacity 0.2s;}
+.special_alipay{background-color:#027aff;color:white;white-space:nowrap;
+font-family:sans-serif;font-weight:bold;border-radius:0.7em;font-size:0.75em;padding:0.25em;}
+.special_paypal{background-color:white;color:#253b80;white-space:nowrap;
+font-family:sans-serif;font-weight:bold;border-radius:2em;font-size:0.75em;
+padding:0.25em;padding-left:0.5em;padding-right:0.65em;font-style: italic;}
+.special_paypal_inner{color:#169bd7;}
+#waiting_bar{position:fixed;z-index:200;top:0;left:0;right:0;height:0.2em;background-color:#f8ca9b;transform:translate(-100%,0);
+animation:anim_loading 1s linear infinite;}
+@keyframes anim_loading{0%{transform:translate(-100%,0);} 100%{transform:translate(100%,0);}}
+.product_ref{width:32%;padding:0.2em!important;display:inline-block;text-align:center;vertical-align:top;margin-bottom:0.8em;}
+.product_thumb{max-height:11em;max-width:11em;display:inline-flex;margin-bottom:0.2em;background-color:#39270e;}
+.product_thumb img{box-shadow:none;object-fit:contain;max-height:unset;max-width:unset;width:100%;margin:0 auto !important;}
+.product_ref p{margin-bottom:0.2em;text-align:left;}
+.post_preview .product_thumb{max-height:4em;max-width:6em;}
+.purchase_button{background-color:#f8ca9b;color:#231a0d;padding-left:0.5em;padding-right:0.5em;text-decoration:none;font-weight:bold;}
+.page_break{page-break-after:always;}
+.text_highlight,.text_highlight a{background-color:#f8ca9b;color:#231a0d;}
+.gray.text_highlight,.gray.text_highlight a{background-color:#ac7843;color:#231a0d;}
+.print_title{display:none;}
+.show_on_print{display:none;}
+.comment{font-size:0.9em;font-family:sans-serif;overflow:auto !important;width:100%;}
+.comment tbody tr:hover{box-shadow:none;}
+.comment table{border:none;}
+.comment li{display:list-item;list-style:'→';padding-left:0.3em;}
+.comment ul{padding-left:1em;}
+.comment ul li *{margin-bottom:0.5em;}
+
+@media screen and (max-width:1000px){
+.left{width:35%;}
+.center,.center_wide{width:65%;}
+.center_wide .p_thumb{height:8rem;}
+.right{display:none;}
+.post_width{width:calc(100% - 1.5rem);padding-left:0.2em;}
+.post_width_big{left:0;width:100%;}
+.hidden_on_wide{display:unset;}
+.hidden_on_narrow{display:none;}
+.pop_right{width:30%;}
+.pop_right_big{width:40%;}
+@keyframes pop_slide_in{0%{right:-30%;}100%{right:0%;}}
+@keyframes pop_slide_out{0%{right:0%;}100%{right:-30%;}}
+@keyframes pop_slide_in_big{0%{right:-40%;}100%{right:0%;}}
+@keyframes pop_slide_out_big{0%{right:0%;}100%{right:-40%;}}
+.big_side_box{width:35%;}
+.big_image_box{width:65%;}
+.inquiry_buttons{right:35%;}
+.table_top{left:calc(-50% - 1.7em);width: calc(154% + 0.5em);}
+.center_exp{display:block;width:100%;margin:0 auto;overflow:none;padding-bottom:1em;}
+.center_exp .post{overflow:auto;}
+}
+
+@media screen and (max-width:666px){
+html{font-size:16px;}
+.hidden_on_mobile{display:none !important;}
+.block_on_mobile{display:block !important;}
+.hidden_on_desktop{display:unset;}
+header ul{display:block;}
+header li{display:block;}
+header li::before{content:''}
+.left{position:relative;width:100%;position:relative;top:unset;height:unset;min-height:80vh;padding-right:0;display:block;}
+.center,.center_wide,.center_full{position:relative;left:0;top:0;width:100%;padding-left:0;display:block;}
+.center_wide .p_thumb{height:6rem;}
+.pop_right,.pop_right_big{top:unset;right:0;bottom:0;left:0;width:100%;}
+.pop_right{height:30%;}
+.pop_right_big{height:70%;}
+@keyframes pop_slide_in{0%{bottom:-30%;}100%{bottom:0%;}}
+@keyframes pop_slide_out{0%{bottom:0%;}100%{bottom:-30%;}}
+@keyframes pop_slide_in_big{0%{bottom:-70%;}100%{bottom:0%;}}
+@keyframes pop_slide_out_big{0%{bottom:0%;}100%{bottom:-70%;}}
+.big_image_box{position:fixed;top:0;bottom:8.5rem;left:0;right:0;width:100%;}
+.side_box_mobile_inner{background:linear-gradient(to bottom, rgba(0,0,0,0), rgba(1,1,1,0.9) 20%);
+transition:none;background-size:100% 100%;padding:0.5rem;padding-bottom: 5em;}
+.side_box_mobile_inner:hover{background-size:100% 100%;}
+.big_side_box{position:fixed;top:0;bottom:0;right:0;left:0;width:100%;
+height:unset;padding:0;padding-top:calc(100vh - 8.5rem);background:none;}
+.p_thumb{height:3rem;}
+.center .post{padding-right:0rem;padding-left:0rem;}
+.post{padding-right:0.3rem;padding-left:0.3rem;}
+.post .p_thumb img{max-height:3rem;}
+.page,.page_gallery{padding:0.2em;padding-top:0;}
+header{padding-top:0.3em;}
+.small_footer{padding-bottom:0.3em;}
+.footer_additional{display:block;width:100%;}
+.album_hint{display:block;font-size:1rem;}
+.image_nav{position:absolute !important;}
+.image_nav_prev,.image_nav_next{width:25%;}
+.image_nav_prev:hover,.image_nav_next:hover{background-size:0% 100%;color:#f8ca9b !important;}
+.inquiry_buttons{position:relative;left:unset;right:unset;text-align:left;bottom:unset;margin:unset;width:unset;
+background-color:unset;z-index:unset;padding:unset;padding-left:unset;padding-right:unset;
+border-radius:unset;box-shadow:unset;text-shadow:unset;}.img_btn_hidden{opacity:1;}
+.lr_buttons{background-color:unset;padding:unset;padding-top:unset;padding-bottom:unset;
+border-radius:unset;box-shadow:unset;font-size:1.3rem;text-shadow:unset;}
+.opt_compact,.ref_compact{line-break:anywhere;}
+.post_width,.post_width_big{overflow:auto;}
+.table_top{left:unset;width:100%;overflow:auto;}
+table img{max-width:30vw !important;}
+.product_ref{width:100%;display:block;}
+.post_dummy > *{width:100%;max-width:25rem;}
+.sticky_title{top:1.2em;}
+#upload_selector{width:100%;}
+.focused_post{padding:0.3rem !important;}
+.interesting_tbody{background:linear-gradient(90deg, #231a0dff, #231a0d88 10em);}
+}
+
+@media print{
+body,footer,header,.small_footer,a,.clean_a,.invert_a,.clean_a a,.invert_a a{background:none;color:black;}
+table{border-bottom:2px solid black;border-top:2px solid black;}
+table img{max-width:5em;max-width:8em !important;max-height:8em !important;}
+thead{box-shadow:inset 0 -1px 0 0px black;background:none;}
+.post,.focused_post{padding:0 !important;margin-top:0.3em;margin-bottom:0.5em;}
+.post_width,.post_width_big{overflow:hidden;left:0;width:100%;padding-left:0em;}
+.post h1,.post h2,.post h3,.post h4{margin-top:0.5rem;}
+.gray,.gray a,.deleted_post{color:rgba(0,0,0,0.5);}
+.left,.right{display:none;}
+.center, .center_wide, .center_full{width:100%;padding:0;display:block;font-size:16px;line-height:1.3}
+hr{border:1px solid black;}
+.post_box_top{display:none;}
+.opt_compact .post_access,.ref_compact .post_access{border-right:none;display:inline;}
+.text_highlight,.text_highlight a,.gray.text_highlight,.gray.text_highlight a,.purchase_button{background-color:lightgray;color:black;}
+.focused_post{border:none;font-size:1em;}
+.hidden_on_print{display:none;}
+.print_column{column-count:2;margin-top:0.5rem;margin-bottom:0.5rem;}
+.post_access{display:none;}
+.opt_compact{margin-left:0;}
+.opt_compact .post_width{left:1.4rem;width:calc(100% - 1.7rem);padding-left:0.2em;}
+.print_title{column-span:all;display:block;margin-top:2em;margin-bottom:0.5rem;font-size:1.2em;}
+.print_title:first-of-type{margin-top:1em;}
+.print_title+.post h1:first-of-type{display:none;}
+.opt_compact h1:first-of-type,.ref_compact h1:first-of-type{display:unset;}
+.table_top{position:relative;left:0;width:100%;background:none;z-index:1;box-shadow:none;margin-top:0.2em;margin-bottom:0.2em;}
+.header_nav{display:none;}
+.show_on_print{display:block;}
+blockquote{border-left:2px solid black;}
+.footer_additional{display:none;}
+.small_footer{margin-top:1rem;}
+.page_selector{display:none;}
+.p_thumb{height:4rem;}
+.post .p_thumb img{max-height:4rem;}
+.sticky_title{box-shadow:none;}
+.center_wide .p_thumb{display:inline-flex;height:5.8rem;width:5.8rem;margin-right:0;}
+.center_wide .p_row{display:block;}
+}

+ 152 - 0
demo/translations.md

@@ -0,0 +1,152 @@
+- 那么的维基 | laMDWiki
+- 基 | Ki
+- 管理员 | Administrator
+- 微博 | Weibo
+- 修改 | Edit
+- 只复制 | Copy only
+- 引用 | Ref
+- 标记 | Mark
+- 关闭 | Close
+- 恢复 | Restore
+- 删除 | Delete
+- 修改帖子: | Editing Post:
+- 取消 | Cancel
+- 链接 | Refs
+- 目录 | TOC
+- 最近 | Recent
+- 热门 | Hot
+- 画廊 | Gallery
+- 上一页 | Prev
+- 下一页 | Next
+- 未找到该引用。 | Could not find this reference.
+- 标记 | Mark
+- 置顶帖子 | Pinned Post
+- 回复给主题帖: | Reply to thread:
+- 个回复 | Replies
+- 继续补充该话题: | Continue the thread:
+- 有什么想说的 | What are you thinking
+- 发送 | Send
+- 图片 | Images
+- 清空 | Clear
+- 个引用: | References:
+- 没有帖子链接到这里。 | No posts links to here.
+- 话题 | Thread
+- 详细 | Details
+- 点击图片以插入: | Click the thumbnail to insert:
+- 全部 | All
+- 垃圾桶 | Trash Bin
+- 图片 | Image
+- 选择、粘贴或者拖动到页面以上传图片。 | Choose, Paste or drag and drop to upload images.
+- 就绪 | Ready
+- 上传列表中的文件 | Upload listed files
+- 上传完成。 | Upload completed.
+- 刷新页面 | Refresh Page
+- 正在上传... | Uploading...
+- 已上传为 | Uploaded as
+- 出现错误。| An error has occured.
+- 画廊 | Gallery
+- 相册 | Album
+- 删除相册 | Delete Album
+- 该操作不删除图片。 | This operation does not delete photos.
+- 改名 | Rename
+- 添加 | Add
+- 相册名字: | Album Name:
+- 确认 | Confirm
+- 选择了 | Selected
+- 个图片。 | images.
+- 清除 | Clear
+- 添加到 | Add to
+- 执行 | Execute
+- 或者 | Or&nbsp;
+- 从相册移除 | remove from album
+- 全部图片 | All Pictures
+- 未找到目录 | No table of contents found
+- 设置 | Settings
+- 选项 | Options
+- 值 | Value
+- 网站标题 | Website Title
+- 短标题 | Short Title
+- 显示名称 | Display Name
+- 导航栏 | Navigation
+- 脚注 | Footer
+- 置顶文 | Pinned Post
+- 附加操作 | Additional Ops
+- 进入 | Enter
+- 登出 | Log out
+- 帐号 | ID
+- 密码 | Password
+- 新密码 | New Password
+- 再次输入 | Re-enter
+- 旧密码 | Old Passowrd
+- 请登录 | Please Login
+- 保存设置 | Save Settings
+- 登录 | Log In
+- 返回一般设置 | Back to general settings
+- 自动重定向 | Auto redirect
+- P为帖子跳转,匹配REQUEST_URI跳到目标文章;S为站点跳转,可以重定向来源域名,例子: | P for passage redirect, matching REQUEST_URI to jump to a specific passage, S is for site redirect, can relocate your source domain. Examples:
+- 保存重定向设置 | Save redirect settings
+- 当心!下列操作将立即执行: | Caution! Following operations will run immediately:
+- 重新写入默认CSS | Re-write default CSS
+- 上传到这里 | Upload here
+- 继续补充该话题: | Continue the thread:
+- 引用文章 | Ref
+- 引用并发送新话题: | Refer and start a new thread:
+- 切换为回复 | Switch to reply
+- 已复制 | Copied
+- 稍等 | Wait
+- 正在查询…… | Querying...
+- 该图片出现在 | This image appeared in
+- 个帖子中 | posts
+- 该图片未被引用 | This images is not referenced
+- 精选 | Featured
+- 设为精选 | Set Featured
+- 取消精选 | Stop Featuring
+- 其他相册 | Other Albums
+- 默认相册 | Default Album
+- 下载 | Download
+- 前往 | Go to
+- 填写格式: | Format:
+- 自定义翻译 | Custom Translations
+- 保存翻译 | Save Translations
+- 请稍候 | Please Wait
+- 咨询 | Inquiry
+- 网站图片咨询 | Blog Artwork Inquiry
+- 你好!我对你网站上的这张图片感兴趣: | Hi! I'm interested in this piece of artwork on your blog:
+- 电子邮件 | E-Mail
+- 省略的表格 | Table Omitted
+- 阅读更多 | Read More
+- 未找到这个帖子 | Post not found
+- 返回首页 | Return to home page
+- 购买印刷品 | Purchase Print
+- 印刷品链接 | Link to print
+- 保存 | Save
+- 重新生成图片缩略图 | Regenerate Thumbnails
+- 删除所有登录 | Clear All Logins
+- 搜索 | Search
+- 和 | And
+- 个商品 | products
+- 商品 | Product
+- 未设置价格 | No Price
+- 购买 | Purchase
+- 你好!我想购买 | Hi! I'd like to purchase&nbsp;
+- 索引 | Index
+- 主机 | Host
+- 实验访问 | Exp Access
+- 设为实验 | Set Experimental
+- 取消实验 | Cancel Experimental
+- 首次提示 | First-time Hint
+- 继续 | Continue
+- 重命名 | Rename
+- 替换图像 | Replace Image
+- 启用评论 | Enable Comments
+- 写评论 | Write a Reply
+- 称呼 | Name
+- 个人网站 | Personal Website
+- 您的邮箱不会公开展示。 | Your e-mail will not be shown to the public.
+- 评论 | Comments
+- 还没有评论 | No comment here yet.
+- 有趣 | Interesting
+- 未分类 | None
+- 分类 | Category
+- 已关闭评论 | Commenting is currently disabled
+

+ 5 - 0
error.php

@@ -0,0 +1,5 @@
+<?php
+ error_reporting(E_ALL);
+ ini_set("display_errors", 1);
+ include("index.php");
+?>

二进制
fonts/NotoSerifSC-Black.otf


二进制
fonts/NotoSerifSC-Bold.otf


二进制
fonts/NotoSerifSC-ExtraLight.otf


二进制
fonts/NotoSerifSC-Light.otf


二进制
fonts/NotoSerifSC-Medium.otf


二进制
fonts/NotoSerifSC-Regular.otf


二进制
fonts/NotoSerifSC-SemiBold.otf


+ 656 - 0
gogs_lamdwiki.css

@@ -0,0 +1,656 @@
+/**** Syntax Highlighting ****/
+ body:not(.full-width) {
+	 background-color: #2d2214;
+	 background: linear-gradient(45deg, #3f0e13 0%, #471016 1%, #411211 1%, #521614 15%, #491634 15%, #4d0927 38%, #5f1417 38%, #5a1517 55%, #611812 55%, #3a0a06 62%, #691529 62%, #691529 63%, #2f092c 63%, #2f092c 69%, #12032d 69%, #12032d 100%);
+	 background-size: contain;
+	 background-attachment: fixed;
+	 color: #f8ca9b 88;
+}
+ .following.bar.light {
+	 background-color: #6a491c !important;
+	 border-bottom: 1px solid #2d2214;
+	 color: #f8ca9b 88 !important;
+	 position: fixed;
+	 top: 0;
+}
+ .ui.container:not(.fluid) {
+	 width: 70% !important;
+}
+ .tabular .item {
+	/* Repo tabs menu */
+	 color: #9b3838 !important;
+}
+ h1, h2, h3, h4, h5, .ui.header, .ui.menu, .ui.input input, .ui.button:not(.label) {
+	 color: #9b3838;
+}
+ #file-content h1, #file-content h2, #file-content h3, #file-content h4, #file-content h5 {
+	 color: #f8ca9b;
+}
+ pre.raw, code.raw {
+	 background-color: #6a491c;
+	 border: 1px solid #2d2214;
+}
+ .following.bar {
+	 color: #9b3838 !important;
+}
+ .overflow.menu .items .item {
+	 color: #f8ca9b;
+}
+ .following.bar .top.menu a.item.brand {
+	 color: #9b3838 !important;
+}
+ .following.bar .top.menu a.item:hover, .following.bar .top.menu .dropdown.item:hover, .following.bar .top.menu .dropdown.item.active {
+	 color: rgba(196, 45, 45, 0.95) !important;
+}
+ .following.bar .top.menu a.item:hover {
+	 color: rgba(196, 45, 45, 0.95) !important;
+}
+ .following.bar .top.menu .menu {
+	 color: #9b3838 !important;
+}
+ .following.bar .head.link.item {
+	 color: #9b3838 !important;
+}
+ .home {
+	 padding-top: 80px;
+}
+ .ui.left {
+	 padding-left: 5px;
+}
+ .ui.right {
+	 padding-right: 5px;
+}
+ .ui .header > i + .content {
+	 background-color: #2d2214 !important;
+}
+ footer {
+	 color: #f8ca9b !important;
+	 background-color: #2d2214;
+}
+ .markdown:not(code) {
+	 background-color: #6a491c;
+	 color: #f8ca9b;
+}
+ .markdown:not(code) blockquote {
+	 border-left: 4px solid #6a491c;
+}
+ .markdown:not(code) table th, .markdown:not(code) table td {
+	 border: 0px solid #9b3838 !important;
+	 background-color: #080705;
+	 color: #9b3838;
+}
+ .markdown:not(code) span.frame > span {
+	 border: 1px solid #2d2214;
+}
+ .markdown:not(code) pre code, .markdown:not(code) pre tt {
+	 color: #f8ca9b;
+}
+ .markdown:not(code) .highlight pre, .markdown:not(code) pre {
+	 background-color: #6a491c;
+}
+ .home .stackable {
+	 padding-top: 40px;
+}
+ .ui.attached.header {
+	 background: #6a491c !important;
+	 color: #9b3838 !important;
+	 border: none !important;
+}
+ .repository .header-wrapper {
+	 color: #9b3838 !important;
+	 background-color: transparent;
+	 padding-top: 80px;
+}
+ .repository {
+	 padding-top: 80px;
+}
+ .repository #clone-panel {
+	 width: 30vw !important;
+}
+ .repository #clone-panel input {
+	 max-width: 100%;
+	 background-color: #6a491c;
+}
+ .repository.file.list #git-stats {
+	 background-color: transparent;
+}
+ .repository.file.list #file-content .view-raw {
+	 border: 0 !important;
+}
+ .repository.file.list #file-content .code-view * {
+	 font-size: 14px;
+	 line-height: 19px !important;
+}
+ .repository.file.list #file-content .code-view .lines-num span {
+	 line-height: 20px !important;
+}
+ .repository.file.list #file-content .code-view table {
+	 border: 0 !important;
+}
+ .repository.file.list #file-content .code-view .lines-num {
+	 background-color: #6a491c !important;
+}
+ .repository.file.list #file-content .code-view .lines-num pre li, .repository.file.list #file-content .code-view .lines-code pre li, .repository.file.list #file-content .code-view .lines-num ol li, .repository.file.list #file-content .code-view .lines-code ol li, .repository.file.list #file-content .code-view .lines-num .hljs li, .repository.file.list #file-content .code-view .lines-code .hljs li {
+	 font-size: 16px;
+}
+ .repository.file.list #file-content .code-view .lines-num pre li.active, .repository.file.list #file-content .code-view .lines-code pre li.active, .repository.file.list #file-content .code-view .lines-num ol li.active, .repository.file.list #file-content .code-view .lines-code ol li.active, .repository.file.list #file-content .code-view .lines-num .hljs li.active, .repository.file.list #file-content .code-view .lines-code .hljs li.active, .hljs-meta .hljs-attribute .hljs-name .hljs-tag .hljs-string .hljs-doctag .hljs-subst .hljs-selector-tag .hljs-keyword .hljs-quote .hljs-comment {
+	 background: #001;
+	 color: #8f1 !important;
+}
+ .repository .diff-file-box .file-body.file-code .lines-num {
+	 background-color: #6a491c !important;
+}
+ .repository .diff-file-box .code-diff tbody tr.del-code td {
+	 background-color: #6f3a3a !important;
+	 border-color: #000 !important;
+	 color: #fff !important;
+}
+ .repository .diff-file-box .code-diff tbody tr.add-code td {
+	 background-color: #467b60 !important;
+	 border-color: #c1e9c1 !important;
+	 color: #fff !important;
+}
+ .repository .diff-file-box .code-diff tbody tr.del-code td.add-code pre {
+	 background-color: #4c296a !important;
+	 color: #fff !important;
+}
+ .repository .diff-file-box .code-diff tbody tr.del-code td.add-code {
+	 background-color: #4c296a !important;
+	 color: #fff !important;
+}
+ .repository.file.editor .commit-form-wrapper .commit-form {
+	 border: 1px solid #2d2214;
+}
+ .repository .diff-detail-box ol li {
+	 border-bottom: 1px dashed #2d2214;
+}
+ .repository .diff-file-box .file-body.file-code .lines-num-old {
+	 border-right: 1px solid #2d2214;
+}
+ .repository .head .mega-octicon {
+	 color: #9b3838;
+}
+ .mega-octicon.octicon-repo {
+	 color: steelblue !important;
+}
+ #repo-clone-url {
+	 color: #9b3838 !important;
+}
+ #repo-clone-url::-moz-selection {
+	 color: rgba(196, 45, 45, 0.95) !important;
+	 background-color: #6a491c;
+}
+ #repo-clone-url::selection {
+	 color: rgba(196, 45, 45, 0.95) !important;
+	 background-color: #6a491c;
+}
+ .repository.release #release-list {
+	 border-top: 1px solid #2d2214;
+}
+ .repository.release #release-list > li .detail {
+	 border-left: 1px solid #2d2214;
+}
+ .repository.forks .list .item {
+	 border-bottom: 1px solid #2d2214;
+}
+ .repository.settings.collaboration .collaborator.list > .item:not(:last-child) {
+	 border-bottom: 1px solid #2d2214;
+}
+ #search-user-box .results .item {
+	 border-bottom: 1px solid #2d2214;
+}
+ .ui.vertical.menu .header.item {
+	 background: #6a491c;
+	 color: #f8ca9b;
+	 text-align: center;
+	 padding-left: 0;
+}
+ .organization {
+	 padding-top: 80px;
+}
+ .organization.profile #org-avatar {
+	 width: 90px;
+	 height: 70px;
+	 margin-right: 5px;
+}
+ .organization.teams .repositories .item:not(:last-child), .organization.teams .members .item:not(:last-child) {
+	 border-bottom: 1px solid #2d2214;
+}
+ .user:not(.icon) {
+	 padding-top: 80px;
+}
+ .ui.card {
+	 background-color: transparent !important;
+	 color: #f8ca9b !important;
+	 border: 0 !important;
+}
+ .user.profile .ui.card .extra.content ul li:not(:last-child) {
+	 border-bottom: 1px solid #2d2214;
+}
+ .dashboard {
+	 padding-top: 80px;
+}
+ .feeds .list ul li:not(:last-child) {
+	 border-bottom: none;
+}
+ .feeds .list ul li a .octicon {
+	 color: steelblue;
+}
+ .explore {
+	 padding-top: 80px;
+}
+ .ui.repository.list .item .ui.header {
+	 color: #f8ca9b;
+}
+ .ui.repository.list .item .ui.header .name {
+	 color: #f8ca9b;
+}
+ .ui.repository.list .item .ui.header .name:hover {
+	 color: rgba(196, 45, 45, 0.95);
+}
+ .ui.repository.list .item:not(:first-child) {
+	 border-top: none;
+}
+ .ui.header .sub.header {
+	 color: #9b3838 !important;
+}
+ .ui.header {
+	 color: #9b3838 !important;
+}
+ .ui.form .inline.field > label, .ui.form .inline.field > p, .ui.form .inline.fields .field > label, .ui.form .inline.fields .field > p, .ui.form .inline.fields > label {
+	 color: #f8ca9b;
+}
+ .secondary {
+	 background-color: transparent !important;
+	 color: #9b3838 !important;
+}
+ .secondary .item {
+	 color: #f8ca9b !important;
+}
+ .secondary .item:hover {
+	 color: #f8ca9b;
+}
+ .repository.file.list #file-content .code-view .lines-num pre, .repository.file.list #file-content .code-view .lines-code pre, .repository.file.list #file-content .code-view .lines-num ol, .repository.file.list #file-content .code-view .lines-code ol, .repository.file.list #file-content .code-view .lines-num .hljs, .repository.file.list #file-content .code-view .lines-code .hljs {
+	 background-color: #080705 !important;
+	 color: #9ebdb1;
+	 margin: 0;
+	 padding: 0 !important;
+	 border: 0 !important;
+}
+ .feeds .news > .ui.grid {
+	 margin-left: auto;
+	 margin-right: auto;
+}
+ #ActionButtons {
+	 color: #f8ca9b !important;
+	 background-color: inherit !important;
+	 border: 0.5px solid #2d2214;
+}
+ .CodeMirror-code {
+	 background-color: #080705 !important;
+	 color: #9ebdb1 !important;
+}
+ .CodeMirror-gutters {
+	 background-color: #080705;
+	 color: #9b3838;
+}
+ .CodeMirror {
+	/* file edditor */
+	 background: #080705;
+	 color: #9ebdb1 !important;
+}
+ #repo-desc {
+	 text-align: center;
+	 font-weight: 500;
+	 color: #f8ca9b;
+}
+ #repo-clone-ssh {
+	 color: #f8ca9b;
+}
+ #repo-clone-https {
+	 color: #f8ca9b;
+}
+ #danger_zone_edited {
+	 background: #6a491c;
+	 border-color: #2d2214 !important;
+}
+ #basic_sett, #adv_sett, #adv_sett_form, #admin_sec_panel, #admin_small_panel {
+	 background: #6a491c;
+	 border-color: #2d2214 !important;
+}
+ #danger_zone_edited_header {
+	 background: #9b3838 !important;
+}
+ #commits-table-head tr th {
+	 color: #f8ca9b !important;
+}
+ .ui.table thead th {
+	 background: #6a491c;
+	 color: #f8ca9b;
+}
+ .ui.table thead tr:hover th {
+	 background: #2d2214;
+}
+ .ui.table tr td {
+	 border-top: none;
+}
+ .repository.file.list #repo-files-table tr:hover {
+	 background-color: #2d2214;
+}
+ tr:hover a {
+	 color: rgba(196, 45, 45, 0.95);
+}
+ #repo-files-table tr:hover {
+	 color: rgba(196, 45, 45, 0.95);
+}
+ #repo-files-table tr:hover span {
+	/* repo time last changed */
+	 color: rgba(196, 45, 45, 0.95) !important;
+}
+ .feeds .list ul li.private {
+	 background-color: #6a491c;
+}
+ .feeds .list ul {
+	 background-color: #2d2214;
+	 background-image: url("/img/grad1.png");
+	 background-size: contain;
+	 background-attachment: fixed;
+	 background-blend-mode: difference;
+}
+ .repository .diff-file-box .code-diff tbody tr.tag-code td {
+	 background-color: #6a491c !important;
+}
+ .repository .diff-file-box .header {
+	 background-color: #9b3838;
+}
+/* File Editor */
+ .editor-toolbar a {
+	 color: #f8ca9b 99 !important;
+}
+ .editor-toolbar a.active, .editor-toolbar a:hover {
+	 background: #807b54;
+	 color: #f8ca9b;
+	 border-color: #807b54;
+}
+ .CodeMirror {
+	 border: 1px solid #2d2214;
+}
+ .CodeMirror-gutters {
+	 background-color: #6a491c;
+	 border-right: none;
+	 border-left: none;
+}
+/*######################### Syntax Higlight Theme #####################*/
+ .hljs-comment, .hljs-quote {
+	 color: #a2a2a2 6e !important;
+	 font-weight: 400;
+}
+ .hljs-keyword, .hljs-selector-tag, .hljs-subst {
+	 color: #0086b3 !important;
+}
+ .hljs-string, .hljs-doctag {
+	 color: #90a959 !important;
+}
+ .hljs-tag, .hljs-name, .hljs-attribute {
+	 color: #1c7100 !important;
+}
+ .hljs-meta {
+	 color: #444 !important;
+}
+ .hljs-built_in, .hljs-builtin-name {
+	 color: #0086b3 !important;
+}
+ .hljs-title, .hljs-section, .hljs-selector-id {
+	 color: #bfa01c !important;
+}
+/* Editor Highlight Theme */
+ .cm-s-default .cm-comment {
+	 color: #a2a2a2 6e !important;
+	 font-weight: 400;
+}
+ .cm-s-default .cm-def {
+	 color: #9ebdb1;
+}
+ .cm-s-default .cm-builtin {
+	 color: #0086b3 !important;
+}
+/* semantic */
+ .ui.attached.segment {
+	 border: 0;
+}
+ .ui.segment {
+	 background: #6a491c;
+}
+ .ui.buttons > .ui.dropdown:last-child .menu, .ui.menu .right.dropdown.item .menu, .ui.menu .right.menu .dropdown:last-child .menu {
+	 background-color: #6a491c !important;
+}
+ .ui.menu .ui.dropdown .menu > .item {
+	 color: #9b3838 !important;
+	 background-color: #6a491c !important;
+}
+ .ui.dropdown .menu > .item:hover {
+	 color: rgba(196, 45, 45, 0.95);
+}
+ .ui.menu {
+	 background: #6a491c;
+}
+ .ui.menu .ui.dropdown .menu > .selected.item {
+	 color: rgba(196, 45, 45, 0.95) !important;
+}
+ .ui.menu .ui.dropdown .menu > .active.item {
+	 color: rgba(196, 45, 45, 0.95) !important;
+}
+ .ui.selection.dropdown .menu > .item {
+	 border-top: none;
+}
+ .ui.checkbox input:focus ~ label {
+	 color: #f8ca9b;
+}
+ .ui.checkbox.checked label:focus {
+	 color: #f8ca9b;
+}
+ .ui.checkbox label {
+	 color: #f8ca9b;
+}
+ .ui.checkbox.checked label {
+	 color: #f8ca9b;
+}
+ .ui.checkbox label.focus {
+	 color: #f8ca9b;
+}
+ .ui.checkbox.checked label.focus {
+	 color: #f8ca9b;
+}
+ .ui.checkbox label:hover {
+	 color: #f8ca9b;
+}
+ .ui.checkbox.checked label:hover {
+	 color: #f8ca9b;
+}
+ .ui.checkbox label, .ui.checkbox + label {
+	 color: #f8ca9b;
+}
+ .ui.checkbox label:hover, .ui.checkbox + label:hover {
+	 color: #f8ca9b;
+}
+ .ui.checkbox.checked label, .ui.checkbox.checked + label {
+	 color: #f8ca9b;
+}
+ .ui.checkbox.checked label:hover, .ui.checkbox.cheked + label:hover {
+	 color: #f8ca9b;
+}
+ .ui.link.menu .item, .ui.menu .dropdown.item, .ui.menu .link.item, .ui.menu a.item {
+	 color: #f8ca9b;
+}
+ .ui.link.menu .item:hover, .ui.menu .dropdown.item:hover, .ui.menu .link.item:hover, .ui.menu a.item:hover {
+	 color: rgba(196, 45, 45, 0.95);
+}
+ .ui .dropdown .menu {
+	 background-color: #6a491c;
+}
+ .ui .dropdown .menu a:hover {
+	 color: #9b3838 !important;
+}
+ .ui.basic.button, .ui.basic.buttons .button {
+	 background: #9b3838 !important;
+	 color: #f8ca9b;
+}
+ .ui.vertical.menu {
+	 background: #6a491c;
+	 color: #9b3838;
+}
+ .ui.dropdown .menu .selected.item, .ui.dropdown.selected {
+	 background: #6a491c;
+	 color: #f8ca9b;
+}
+ .ui.vertical.menu .item {
+	 color: #f8ca9b;
+}
+ .ui.menu .item {
+	 font-weight: 600;
+	 color: #f8ca9b;
+}
+ .ui.menu .item .active {
+	 font-weight: 600;
+}
+ .ui.menu .item > .input input {
+	 background-color: #2d2214;
+}
+ .ui.menu .item:hover, .ui.vertical.menu .item:hover {
+	 color: #f8ca9b;
+}
+ .ui.vertical.menu .active.item {
+	 background: #f8ca9b 99;
+}
+ .ui.menu .active.item {
+	 font-weight: 600;
+}
+ .ui.menu .active.item:hover, .ui.vertical.menu .active.item:hover {
+	 color: inherit;
+}
+ .ui.dropdown .menu > .item {
+	 color: #f8ca9b;
+}
+ .ui.menu.three.item .item {
+	 background-color: #6a491c;
+	 color: steelblue;
+}
+ .ui.card > .extra a:not(.ui), .ui.cards > .card > .extra a:not(.ui) {
+	 color: #f8ca9b;
+}
+ .ui.table {
+	 background-color: #0a2948 78;
+	 background-image: url("/img/grad1.png");
+	 background-size: contain;
+	 background-attachment: fixed;
+	 background-blend-mode: color-burn;
+	 color: #f8ca9b;
+}
+ .ui.button {
+	 background: #122940 !important;
+	 color: #f8ca9b !important;
+}
+ .ui.button:hover {
+	 color: rgba(196, 45, 45, 0.95);
+}
+ .ui.dropdown .menu > .header {
+	 color: #f8ca9b;
+}
+ .ui.menu .ui.dropdown .menu > .item:hover {
+	 color: rgba(196, 45, 45, 0.95) !important;
+	 background-color: #6a491c !important;
+}
+ .ui.dropdown .menu {
+	 border: 1px solid #6a491c;
+}
+ .ui.dropdown .menu > .input:not(.transparent) input {
+	 background-color: #2d2214;
+	 color: #f8ca9b 88;
+}
+ .ui.form input:not([type]), .ui.form input[type="date"], .ui.form input[type="datetime-local"], .ui.form input[type="email"], .ui.form input[type="file"], .ui.form input[type="number"], .ui.form input[type="password"], .ui.form input[type="search"], .ui.form input[type="tel"], .ui.form input[type="text"], .ui.form input[type="time"], .ui.form input[type="url"] {
+	 background: #2d2214;
+	 color: #f8ca9b;
+	 font-weight: 600;
+}
+ .ui.form input:not([type]):focus, .ui.form input[type="date"]:focus, .ui.form input[type="datetime-local"]:focus, .ui.form input[type="email"]:focus, .ui.form input[type="file"]:focus, .ui.form input[type="number"]:focus, .ui.form input[type="password"]:focus, .ui.form input[type="search"]:focus, .ui.form input[type="tel"]:focus, .ui.form input[type="text"]:focus, .ui.form input[type="time"]:focus, .ui.form input[type="url"]:focus {
+	 color: #f8ca9b;
+	 border-color: transparent;
+	 border-radius: 0;
+	 background: #2d2214;
+	 -webkit-box-shadow: 0px 0px 8px 2px #807b54;
+	 box-shadow: 0px 0px 8px 2px #807b54;
+}
+ .ui.action.input:not([class*="left action"]) > input:focus {
+	 border-right-color: transparent !important;
+}
+ .ui.form .field > label {
+	 color: #f8ca9b;
+}
+ .ui.attached.header {
+	 background: #2d2214;
+}
+ .ui .text.black:hover {
+	 color: #f8ca9b 88;
+}
+ .ui.tabular.menu .active.item {
+	 background: none #6a491c;
+	 border-color: #6a491c;
+	 color: rgba(196, 45, 45, 0.95);
+}
+ .ui.basic.button, .ui.basic.buttons .button {
+	 background: #6a491c none !important;
+	 color: #f8ca9b !important;
+	 font-weight: 600;
+	 -webkit-box-shadow: 0 0 8px 0px #807b54;
+	 box-shadow: 0 0 8px 0px #807b54;
+}
+ .ui.basic.button:hover, .ui.basic.buttons .button:hover {
+	 background: #6a491c !important;
+	 color: rgba(196, 45, 45, 0.95) !important;
+	 -webkit-box-shadow: 0 0 8px 0px #807b54;
+	 box-shadow: 0 0 8px 0px #807b54;
+}
+ .ui.label {
+	 background-color: #4682b4 85;
+	 color: #fff;
+}
+ .ui.basic.label {
+	 background: none #9b3838;
+}
+ .ui.breadcrumb a {
+	 color: #f8ca9b;
+}
+ .ui.list .list > .item .description, .ui.list > .item .description {
+	 color: #f8ca9b;
+}
+ .ui.user.list .item .description a {
+	 color: #f8ca9b;
+}
+ .ui.user.list .item .description a:hover {
+	 text-decoration: underline;
+	 color: #9b3838;
+}
+ .ui.buttons:not(.basic):not(.inverted) > .button, .ui.buttons > .ui.button:not(.basic):not(.inverted) {
+	 -webkit-box-shadow: 0 0 8px 0px #807b54;
+	 box-shadow: 0 0 8px 0px #807b54;
+}
+ .ui.button {
+	 background: transparent;
+}
+ .ui.button:hover {
+	 background-color: #6a491c;
+	 color: rgba(196, 45, 45, 0.95) !important;
+}
+ .ui.card, .ui.cards > .card {
+	 box-shadow: 0px -1px 22px 10px #151124;
+}
+ a {
+	 color: #f8ca9b;
+}
+ a:hover {
+	 color: rgba(196, 45, 45, 0.95);
+}
+ footer {
+	 border-top: 1px solid #2d2214;
+}
+ 

+ 1105 - 0
gogs_lamdwiki.scss

@@ -0,0 +1,1105 @@
+$mainBgColor        : #2d2214;
+$secondBgColor      : #4a3413;
+$mainTextColor      : #f8ca9b;
+$secondTextColor    : #f8ca9b88;
+$accentBase         : #f8ca9b;
+$accentBase2        : #f8ca9b88;
+$accentBase-dark    : #f8ca9b66;
+$active             : #ffe1c1;
+$link               : #f8ca9b;
+$btnColor           : #6a491c;
+$btnShadow          : #000000ee;
+$userCardShadow     : #151124;
+$RepoFilesBg        : #4a3413;
+$codeBg             : #4a3413;
+$codeColor          : #f8ca9b; 
+$codeSelected       : #2d2214;
+$codeColorSelected  : #f8ca9b;
+$codeDifOld         : #6f3a3a;
+$privRepoBg         : #f9f8f8;
+$highlightBg        : #804a06;
+/**** Syntax Highlighting ****/
+
+$comment            : #d5f1a5ba;
+$selector           : #e18614; 
+
+$doctag             : #bdee51;
+$attribute          : #1c7100;
+$meta               : #f8ca9b66;
+
+html{
+    font-size:80%;
+}
+
+strong{ font-weight: normal; }
+
+.repository.view.issue .comment-list::before,
+.repository .comment.form .content .form::after, .repository .comment.form .content .form::before,
+.repository.view.issue .comment-list .comment .content .header::after, .repository.view.issue .comment-list .comment .content .header::before{
+    display:none !important;
+}
+
+.ui .info.segment.top{
+    background-color: $highlightBg !important;
+}
+
+.ui.button:not(.label), .ui.header, .ui.input input, .ui.menu, h1, h2, h3, h4, h5{
+    font-family: 'Noto Serif CJK SC','Times New Roman','SimSun', Georgia, serif !important;
+}
+
+body:not(.full-width) {
+    background-color: $mainBgColor;
+    background-size: contain;
+    background-attachment: fixed;
+    color: $secondTextColor !important;
+    font-family: 'Noto Serif CJK SC','Times New Roman','SimSun', Georgia, serif !important;
+}
+
+.following.bar.light {
+    background-color: $secondBgColor !important;
+    border-bottom: 1px solid $mainBgColor;
+    color: $secondTextColor !important;
+    position: fixed;
+    top: 0;
+}
+
+.ui.container:not(.fluid) {
+    width: 70% !important;
+}
+
+.tabular .item {
+    /* Repo tabs menu */
+    color: $accentBase !important;
+}
+
+h1,
+h2,
+h3,
+h4,
+h5,
+.ui.header,
+.ui.menu,
+.ui.input input,
+.ui.button:not(.label) {
+    color: $accentBase;
+}
+
+#file-content h1,
+#file-content h2,
+#file-content h3,
+#file-content h4,
+#file-content h5 {
+    color: $mainTextColor;
+}
+
+pre.raw,
+code.raw {
+    background-color: $secondBgColor;
+    border: 1px solid $mainBgColor;
+}
+
+.following.bar {
+    color: $accentBase !important;
+}
+
+.overflow.menu .items .item {
+    color: $mainTextColor;
+}
+
+.following.bar .top.menu a.item.brand {
+    color: $accentBase !important;
+}
+
+.following.bar .top.menu a.item:hover,
+.following.bar .top.menu .dropdown.item:hover,
+.following.bar .top.menu .dropdown.item.active {
+    color: $active !important;
+}
+
+.following.bar .top.menu a.item:hover {
+    color: $active !important;
+}
+
+.following.bar .top.menu .menu {
+    color: $accentBase !important;
+}
+
+.following.bar .head.link.item {
+    color: $accentBase !important;
+}
+
+.home {
+    padding-top: 80px;
+}
+
+.ui.left {
+    padding-left: 5px;
+}
+
+.ui.right {
+    padding-right: 5px;
+}
+
+.ui .header>i+.content {
+    background-color: $mainBgColor !important;
+}
+
+footer {
+    color: $mainTextColor !important;
+    background-color: $mainBgColor;
+}
+
+.markdown:not(code) {
+    background-color: $secondBgColor;
+    color: $mainTextColor;
+    font-family: 'Noto Serif CJK SC','Times New Roman','SimSun', Georgia, serif !important;
+}
+
+.markdown:not(code) blockquote {
+    border-left: 4px solid $secondBgColor;
+}
+
+.markdown:not(code) table th,
+.markdown:not(code) table td {
+    border: 0px solid $accentBase !important;
+    background-color: $codeBg;
+    color:$accentBase;
+}
+
+.markdown:not(code) span.frame>span {
+    border: 1px solid $mainBgColor;
+}
+
+.markdown:not(code) pre code,
+.markdown:not(code) pre tt {
+    color: $mainTextColor;
+}
+
+.markdown:not(code) .highlight pre,
+.markdown:not(code) pre {
+    background-color: $secondBgColor;
+}
+
+.home .stackable {
+    padding-top: 40px;
+}
+
+.ui.attached.header {
+    background: $secondBgColor !important;
+    color: $accentBase !important;
+    border: none !important;
+}
+
+.repository .header-wrapper {
+    color: $accentBase !important;
+    background-color: transparent;
+    padding-top: 80px;
+}
+
+.repository {
+    padding-top: 80px;
+}
+
+.repository #clone-panel {
+    width: 30vw !important;
+}
+
+.repository #clone-panel input {
+    max-width: 100%;
+    background-color: $secondBgColor;
+}
+
+.repository.file.list #git-stats {
+    background-color: transparent;
+}
+
+.repository.file.list #file-content .view-raw {
+    border: 0 !important;
+}
+
+.repository.file.list #file-content .code-view * {
+    font-size: 14px;
+    line-height: 19px!important;
+}
+
+.repository.file.list #file-content .code-view table {
+    border: 0 !important;
+}
+
+.repository.file.list #file-content .code-view .lines-num {
+    background-color: $secondBgColor !important;
+}
+
+.repository.file.list #file-content .code-view .lines-num pre li,
+.repository.file.list #file-content .code-view .lines-code pre li,
+.repository.file.list #file-content .code-view .lines-num ol li,
+.repository.file.list #file-content .code-view .lines-code ol li,
+.repository.file.list #file-content .code-view .lines-num .hljs li,
+.repository.file.list #file-content .code-view .lines-code .hljs li {
+    font-size: 16px;
+}
+
+.repository.file.list #file-content .code-view .lines-num pre li.active,
+.repository.file.list #file-content .code-view .lines-code pre li.active,
+.repository.file.list #file-content .code-view .lines-num ol li.active,
+.repository.file.list #file-content .code-view .lines-code ol li.active,
+.repository.file.list #file-content .code-view .lines-num .hljs li.active,
+.repository.file.list #file-content .code-view .lines-code .hljs li.active,
+.hljs-meta .hljs-attribute .hljs-name .hljs-tag .hljs-string .hljs-doctag .hljs-subst .hljs-selector-tag .hljs-keyword .hljs-quote .hljs-comment {
+    background: $codeSelected;
+    color: $codeColorSelected !important;
+}
+.hljs-number, .hljs-literal, .hljs-variable, .hljs-template-variable, .hljs-tag .hljs-attr{
+    color: #f7cc24 !important;
+}
+
+.hljs{ color: $mainTextColor !important; }
+
+.repository .diff-file-box .file-body.file-code .lines-num {
+    background-color: $secondBgColor !important;
+    color: $secondTextColor !important;
+}
+
+.repository .diff-box .file{
+    color: $mainTextColor !important;
+}
+
+.repository .diff-file-box .code-diff tbody tr.del-code td {
+    background-color: #6f3a3a88 !important;
+    border-color: #000 !important;
+    color: $mainTextColor !important;
+}
+
+.repository .diff-file-box .code-diff tbody tr.add-code td {
+    background-color: #467b6055 !important;
+    border-color: #c1e9c1 !important;
+    color: $active !important;
+}
+.repository .diff-file-box .code-diff tbody tr .removed-code{
+    background-color:#f997 !important;
+}
+.repository .diff-file-box .code-diff tbody tr .added-code{
+    background-color:#9f95 !important;
+}
+
+.repository .diff-file-box .code-diff tbody tr.del-code td.add-code pre {
+    background-color: #4c296a !important;
+    color: #fff !important;
+}
+
+.repository .diff-file-box .code-diff tbody tr.del-code td.add-code {
+    background-color: #4c296a !important;
+    color: #fff !important;
+}
+
+.repository.file.editor .commit-form-wrapper .commit-form {
+    border: 1px solid $mainBgColor;
+}
+
+.repository .diff-detail-box ol li {
+    border-bottom: 1px dashed $mainBgColor;
+}
+
+.repository .diff-file-box .file-body.file-code .lines-num-old {
+    border-right: 1px solid $mainBgColor;
+}
+
+.repository .head .mega-octicon {
+    color: $accentBase;
+}
+
+.mega-octicon.octicon-repo {
+    color: $accentBase2 !important;
+}
+
+#repo-clone-url {
+    color: $accentBase !important;
+}
+
+#repo-clone-url::-moz-selection {
+    color: $active !important;
+    background-color: $secondBgColor;
+}
+
+#repo-clone-url::selection {
+    color: $active !important;
+    background-color: $secondBgColor;
+}
+
+.repository.release #release-list {
+    border-top: 1px solid $mainBgColor;
+}
+
+.repository.release #release-list>li .detail {
+    border-left: 1px solid $mainBgColor;
+}
+
+.repository.forks .list .item {
+    border-bottom: 1px solid $mainBgColor;
+}
+
+.repository.settings.collaboration .collaborator.list>.item:not(:last-child) {
+    border-bottom: 1px solid $mainBgColor;
+}
+
+#search-user-box .results .item {
+    border-bottom: 1px solid $mainBgColor;
+}
+
+.ui.vertical.menu .header.item {
+    background: $secondBgColor;
+    color: $mainTextColor;
+    text-align: center;
+    padding-left: 0;
+}
+
+.organization {
+    padding-top: 80px;
+}
+
+.organization.profile #org-avatar {
+    width: 90px;
+    height: 70px;
+    margin-right: 5px;
+}
+
+.organization.teams .repositories .item:not(:last-child),
+.organization.teams .members .item:not(:last-child) {
+    border-bottom: 1px solid $mainBgColor;
+}
+
+.user:not(.icon) {
+    padding-top: 80px;
+}
+
+.ui.card {
+    background-color: transparent !important;
+    color: $mainTextColor !important;
+    border: 0 !important;
+}
+
+.ui.card > .extra a:not(.ui):hover, .ui.cards > .card > .extra a:not(.ui):hover{
+    color: unset; text-decoration: none;
+}
+
+.user.profile .ui.card .extra.content ul li:not(:last-child) {
+    border-bottom: 1px solid $mainBgColor;
+}
+
+.dashboard {
+    padding-top: 80px;
+}
+
+.feeds .list ul li:not(:last-child) {
+    border-bottom: none;
+}
+
+.feeds .list ul li a .octicon {
+    color: $accentBase2;
+}
+
+.explore {
+    padding-top: 80px;
+}
+
+.ui.repository.list .item .ui.header {
+    color: $mainTextColor;
+}
+
+.ui.repository.list .item .ui.header .name {
+    color: $mainTextColor;
+}
+
+.ui.repository.list .item .ui.header .name:hover {
+    color: $active;
+}
+
+.ui.repository.list .item:not(:first-child) {
+    border-top: none;
+}
+
+.ui.header .sub.header {
+    color: $accentBase !important;
+}
+
+.ui.header {
+    color: $accentBase !important;
+}
+
+.ui.form .inline.field>label,
+.ui.form .inline.field>p,
+.ui.form .inline.fields .field>label,
+.ui.form .inline.fields .field>p,
+.ui.form .inline.fields>label {
+    color: $mainTextColor;
+}
+
+.secondary {
+    background-color: transparent !important;
+    color: $accentBase !important;
+}
+
+.secondary .item {
+    color: $mainTextColor !important;
+}
+
+.secondary .item:hover {
+    color: $mainTextColor;
+}
+
+.ui.secondary.menu .item{
+    border-radius: 0;
+}
+.ui.secondary.menu .dropdown.item > .menu, .ui.text.menu .dropdown.item > .menu{
+    border-radius: 0;
+}
+.ui.menu .dropdown.item .menu, .ui.dropdown .menu{
+    border-radius: 0;
+}
+
+.repository.file.list #file-content .code-view .lines-num pre,
+.repository.file.list #file-content .code-view .lines-code pre,
+.repository.file.list #file-content .code-view .lines-num ol,
+.repository.file.list #file-content .code-view .lines-code ol,
+.repository.file.list #file-content .code-view .lines-num .hljs,
+.repository.file.list #file-content .code-view .lines-code .hljs {
+    background-color: $codeBg !important;
+    color: $codeColor;
+    margin: 0;
+    padding: 0 !important;
+    border: 0 !important;
+}
+
+.feeds .news>.ui.grid {
+    margin-left: auto;
+    margin-right: auto;
+}
+
+#ActionButtons {
+    color: $mainTextColor !important;
+    background-color: inherit !important;
+    border: 0.5px solid $mainBgColor;
+}
+
+.CodeMirror-code {
+    background-color: $codeBg !important;
+    color: $codeColor !important;
+}
+
+.CodeMirror-gutters {
+    background-color: $codeBg;
+    color: $accentBase;
+}
+
+.CodeMirror {
+    /* file edditor */
+    background: $codeBg;
+    color: $codeColor !important;
+}
+
+#repo-desc {
+    text-align: center;
+    font-weight: normal;
+    color: $mainTextColor;
+}
+
+#repo-clone-ssh {
+    color: $mainTextColor;
+}
+
+#repo-clone-https {
+    color: $mainTextColor;
+}
+
+#danger_zone_edited {
+    background: $secondBgColor;
+    border-color: $mainBgColor !important;
+}
+
+#basic_sett,
+#adv_sett,
+#adv_sett_form,
+#admin_sec_panel,
+#admin_small_panel {
+    background: $secondBgColor;
+    border-color: $mainBgColor !important;
+}
+
+#danger_zone_edited_header {
+    background: $accentBase !important;
+}
+
+#commits-table-head tr th {
+    color: $mainTextColor !important;
+}
+
+.ui.table thead th {
+    background: $secondBgColor;
+    color: $mainTextColor;
+}
+
+.ui.table thead tr:hover th {
+    background: $mainBgColor;
+}
+
+.ui.table tr td {
+    border-top: none;
+}
+.repository.file.list #repo-files-table tr:hover {
+    background-color: $highlightBg;
+}
+
+tr:hover a {
+    color: $active; text-decoration:none !important;
+}
+
+#repo-files-table tr:hover {
+    color: $active;
+}
+
+#repo-files-table tr:hover span {
+    /* repo time last changed */
+    color: $active !important;
+}
+
+.ui.repository.list .item .time{
+    color: $secondTextColor
+}
+
+.feeds .list ul li.private {
+    background-color: $secondBgColor;
+}
+
+.feeds .list ul {
+    background-color: $mainBgColor;
+    background-image: url("/img/grad1.png");
+    background-size: contain;
+    background-attachment: fixed;
+    background-blend-mode: difference;
+}
+
+.repository .diff-file-box .code-diff tbody tr.tag-code td {
+    background-color: $secondBgColor !important;
+}
+
+.repository .diff-file-box .header {
+    background-color: $accentBase;
+}
+
+/* File Editor */
+.editor-toolbar a {
+    color: $link !important; text-decoration:underline !important;
+}
+
+.editor-toolbar a.active,
+.editor-toolbar a:hover {
+    background: $btnShadow;
+    color: $mainTextColor;
+    border-color: $btnShadow;
+    text-decoration:none;
+}
+
+.CodeMirror {
+    border: 1px solid $mainBgColor;
+}
+
+.CodeMirror-gutters {
+    background-color: $secondBgColor;
+    border-right: none;
+    border-left: none;
+}
+
+/*#########################           Syntax Higlight Theme #####################*/
+
+.hljs-comment,
+.hljs-quote {
+    color: $comment !important;
+    font-weight: normal;
+}
+
+.hljs-keyword,
+.hljs-selector-tag,
+.hljs-subst {
+    color: $selector !important;
+}
+
+.hljs-string,
+.hljs-doctag {
+    color: $doctag !important;
+}
+
+.hljs-tag,
+.hljs-name,
+.hljs-attribute {
+    color: $attribute !important;
+}
+
+.hljs-meta {
+    color: $meta !important;
+}
+
+.hljs-built_in,
+.hljs-builtin-name {
+    color: $selector !important;
+}
+
+.hljs-title,
+.hljs-section,
+.hljs-selector-id {
+    color: #bfa01c !important;
+}
+
+
+/* Editor Highlight Theme */
+
+.cm-s-default .cm-comment {
+    color: $comment !important;
+    font-weight: normal;
+}
+
+.cm-s-default .cm-def {
+    color: $codeColor;
+}
+
+.cm-s-default .cm-builtin {
+    color: $selector !important;
+}
+
+/* semantic */
+
+.ui.attached.segment {
+    border: 0;
+}
+
+.ui.segment {
+    background: $secondBgColor;
+}
+
+.ui.buttons>.ui.dropdown:last-child .menu,
+.ui.menu .right.dropdown.item .menu,
+.ui.menu .right.menu .dropdown:last-child .menu {
+    background-color: $secondBgColor !important;
+}
+
+.ui.menu .ui.dropdown .menu>.item {
+    color: $accentBase !important;
+    background-color: $secondBgColor !important;
+}
+
+.ui.dropdown .menu>.item:hover {
+    color: $active;
+}
+
+.ui.menu {
+    background: $secondBgColor;
+    border-radius:0;
+}
+
+.ui.menu .ui.dropdown .menu>.selected.item {
+    color: $active !important;
+}
+
+.ui.menu .ui.dropdown .menu>.active.item {
+    color: $active !important;
+}
+
+.ui.selection.dropdown .menu>.item {
+    border-top: none;
+    background-color: $highlightBg;
+}
+.ui.selection.active.dropdown .menu {
+    border-color: $highlightBg;
+    color: $mainTextColor;
+}
+.repository.view.issue .title .index{
+    color: $secondTextColor;
+}
+.ui.pointing.menu .item::after{
+    border-bottom: 3px solid $mainTextColor;
+}
+
+.ui.form .dropzone{
+    border-color: $secondTextColor;
+}
+
+.ui.input > input,
+.ui.input > input:focus,
+.ui.form textarea:focus,
+.ui.form textarea{
+    background-color: $mainBgColor;
+    color:$mainTextColor;
+}
+
+.ui.checkbox input:focus~label {
+    color: $mainTextColor;
+}
+
+.ui.checkbox.checked label:focus {
+    color: $mainTextColor;
+}
+
+.ui.checkbox label {
+    color: $mainTextColor;
+}
+
+.ui.checkbox.checked label {
+    color: $mainTextColor;
+}
+
+.ui.checkbox label.focus {
+    color: $mainTextColor;
+}
+
+.ui.checkbox.checked label.focus {
+    color: $mainTextColor;
+}
+
+.ui.checkbox label:hover {
+    color: $mainTextColor;
+}
+
+.ui.checkbox.checked label:hover {
+    color: $mainTextColor;
+}
+
+.ui.checkbox label,
+.ui.checkbox+label {
+    color: $mainTextColor;
+}
+
+.ui.checkbox label:hover,
+.ui.checkbox+label:hover {
+    color: $mainTextColor;
+}
+
+.ui.checkbox.checked label,
+.ui.checkbox.checked+label {
+    color: $mainTextColor;
+}
+
+.ui.checkbox.checked label:hover,
+.ui.checkbox.cheked+label:hover {
+    color: $mainTextColor;
+}
+
+.ui.link.menu .item,
+.ui.menu .dropdown.item,
+.ui.menu .link.item,
+.ui.menu a.item {
+    color: $mainTextColor;
+}
+
+.ui.link.menu .item:hover,
+.ui.menu .dropdown.item:hover,
+.ui.menu .link.item:hover,
+.ui.menu a.item:hover {
+    color: $active;
+}
+
+.ui .dropdown .menu {
+    background-color: $highlightBg;
+}
+
+.ui .dropdown .menu a:hover {
+    color: $accentBase !important;
+}
+
+.ui.basic.button,
+.ui.basic.buttons .button {
+    background: $accentBase !important;
+    color: $mainTextColor;
+}
+
+.ui.vertical.menu {
+    background: $secondBgColor;
+    color: $accentBase;
+}
+
+.ui.dropdown .menu .selected.item,
+.ui.dropdown.selected {
+    background: $secondBgColor;
+    color: $mainTextColor;
+}
+
+.ui.vertical.menu .item {
+    color: $mainTextColor;
+}
+
+.ui.menu .item {
+    font-weight: normal;
+    color: $mainTextColor;
+}
+
+.ui.menu .item .active {
+    font-weight: normal;
+}
+
+.ui.menu .item>.input input {
+    background-color: $mainBgColor;
+}
+
+.ui.menu .item:hover,
+.ui.vertical.menu .item:hover {
+    color: $mainTextColor;
+}
+
+.ui.user.list .item:not(:first-child){
+    border-top:none;
+}
+
+.ui.vertical.menu .active.item {
+    background: $highlightBg;
+}
+.ui.input > input{
+    border-bottom: 1px solid $secondTextColor;
+}
+
+.ui.menu .active.item {
+    font-weight: normal;
+    border-bottom: 3px solid $mainTextColor;
+    color: $mainTextColor;
+}
+
+.ui.menu .active.item:hover,
+.ui.vertical.menu .active.item:hover {
+    color: inherit;
+}
+
+.ui.dropdown .menu>.item {
+    color: $mainTextColor;
+}
+
+.ui.menu.three.item .item {
+    background-color: $secondBgColor;
+    color: $accentBase2;
+}
+
+.ui.card>.extra a:not(.ui),
+.ui.cards>.card>.extra a:not(.ui) {
+    color: $mainTextColor;
+}
+
+.ui.table {
+    background-color: $RepoFilesBg;
+    background-image: url("/img/grad1.png");
+    background-size: contain;
+    background-attachment: fixed;
+    background-blend-mode: color-burn;
+    color: $mainTextColor;
+}
+
+.ui.button {
+    background: $btnColor !important;
+    color: $mainTextColor !important;
+}
+
+.ui.button:hover {
+    color: $active;
+}
+
+.ui.dropdown .menu>.header {
+    color: $mainTextColor;
+}
+
+.ui.menu .ui.dropdown .menu>.item:hover {
+    color: $active !important;
+    background-color: $secondBgColor !important;
+}
+
+.ui.dropdown .menu {
+    border: 3px solid $highlightBg;
+}
+
+.ui.dropdown .menu>.input:not(.transparent) input {
+    background-color: $mainBgColor;
+    color: $secondTextColor;
+}
+
+.ui.form input:not([type]),
+.ui.form input[type="date"],
+.ui.form input[type="datetime-local"],
+.ui.form input[type="email"],
+.ui.form input[type="file"],
+.ui.form input[type="number"],
+.ui.form input[type="password"],
+.ui.form input[type="search"],
+.ui.form input[type="tel"],
+.ui.form input[type="text"],
+.ui.form input[type="time"],
+.ui.form input[type="url"] {
+    background: $mainBgColor;
+    color: $mainTextColor;
+    font-weight: normal;
+}
+
+.ui.form input:not([type]):focus,
+.ui.form input[type="date"]:focus,
+.ui.form input[type="datetime-local"]:focus,
+.ui.form input[type="email"]:focus,
+.ui.form input[type="file"]:focus,
+.ui.form input[type="number"]:focus,
+.ui.form input[type="password"]:focus,
+.ui.form input[type="search"]:focus,
+.ui.form input[type="tel"]:focus,
+.ui.form input[type="text"]:focus,
+.ui.form input[type="time"]:focus,
+.ui.form input[type="url"]:focus {
+    color: $mainTextColor;
+    border-color: transparent;
+    border-radius: 0;
+    background: $mainBgColor;
+    -webkit-box-shadow: 0px 0px 8px 2px $btnShadow;
+    box-shadow: 0px 0px 8px 2px $btnShadow;
+}
+ui.tabular.menu .active.item{
+    border-radius: .28571429rem !important; }
+
+.ui.action.input:not([class*="left action"])>input:focus {
+    border-right-color: transparent !important;
+}
+
+.ui.form .field>label {
+    color: $mainTextColor;
+}
+
+.ui.attached.header {
+    background: $mainBgColor;
+}
+
+.ui .text.grey {
+    color: $secondTextColor !important;
+}
+.ui .text.grey a {
+    color: $accentBase-dark !important; text-decoration:underline !important;
+}
+.ui .text.grey a:hover {
+    color: $mainTextColor !important; text-decoration:none !important;
+}
+.ui .text.light.grey{
+    color: $accentBase-dark !important;
+}
+.ui .text.black {
+    color: $secondTextColor !important;
+}
+.ui .text.black:hover {
+    color: $mainTextColor !important;
+}
+.repository.view.issue .comment-list .comment .tag{
+    color: $secondTextColor !important;
+    border: 1px solid $secondTextColor !important;
+}
+
+.ui.tabular.menu .active.item {
+    background: none;
+    border: none;
+    color: $active;
+    border-bottom: 4px solid $mainTextColor;
+    border-radius: 0;
+}
+
+.ui.tabular.menu{
+    border-bottom: 2px solid $mainTextColor;
+}
+
+.ui.basic.button,
+.ui.basic.buttons .button {
+    background: $secondBgColor none !important;
+    color: $mainTextColor!important;
+    font-weight: normal;
+    -webkit-box-shadow: 0 0 8px 0px $btnShadow;
+    box-shadow: 0 0 8px 0px $btnShadow;
+}
+
+.ui.basic.button:hover,
+.ui.basic.buttons .button:hover {
+    background: $secondBgColor !important;
+    color: $active !important;
+    -webkit-box-shadow: 0 0 8px 0px $btnShadow;
+    box-shadow: 0 0 8px 0px $btnShadow;
+}
+
+.ui.basic.grey.button, .ui.basic.grey.buttons .button{
+    box-shadow: 0 0 0px 1px $secondTextColor inset !important;
+    color: $secondTextColor !important;
+}
+
+.ui.label {
+    background-color: $accentBase-dark;
+    color: #fff;
+}
+
+.ui.basic.label {
+    background: none $accentBase;
+}
+
+.ui.breadcrumb a:hover {
+    color: $secondTextColor;
+}
+.ui.breadcrumb a {
+    color: $mainTextColor;
+}
+.ui.breadcrumb .divider{
+    color: $secondTextColor;
+}
+
+.ui.list .list>.item .description,
+.ui.list>.item .description {
+    color: $mainTextColor;
+}
+
+.ui.user.list .item .description a {
+    text-decoration: underline !important;
+    color: $mainTextColor;
+}
+
+.ui.user.list .item .description a:hover {
+    text-decoration: none;
+    color: $accentBase;
+}
+
+.ui.buttons:not(.basic):not(.inverted)>.button,
+.ui.buttons>.ui.button:not(.basic):not(.inverted) {
+    -webkit-box-shadow: 0 0 8px 0px $btnShadow;
+    box-shadow: 0 0 8px 0px $btnShadow;
+}
+
+.ui.button {
+    background: transparent;
+}
+
+.ui.button:hover {
+    background-color: $secondBgColor;
+    color: $active !important;
+}
+
+.ui.card,
+.ui.cards>.card {
+    box-shadow: 0px -1px 22px 10px $userCardShadow;
+}
+
+.repository.file.list #repo-files-table tbody .octicon {
+    color: $secondTextColor !important;
+}
+
+a {
+    color: $mainTextColor;
+}
+
+a:hover {
+    color: $active;
+}
+
+footer {
+    border-top: 1px solid $mainBgColor;
+}
+
+hr{border: 2px solid #f8ca9b88 !important;
+height: 0px !important;
+background: none !important;}
+
+.issue.list > .item .title{
+    color: $secondTextColor !important;
+}
+.issue.list > .item .title:hover {
+    color: $mainTextColor !important;
+}

二进制
images/20210929145345.jpg


二进制
images/202109291453450.jpg


二进制
images/20210930111723.jpg


二进制
images/20211008070157.jpg


二进制
images/20211012145706.jpg


二进制
images/20211012150250.jpg


二进制
images/20211021103942.jpg


二进制
images/20211024105235.jpg


二进制
images/20211028081604.jpg


二进制
images/20211109091920.jpg


二进制
images/20211109092233.jpg


二进制
images/20211109092304.jpg


二进制
images/20211109092751.jpg


二进制
images/20211109155406.jpg


二进制
images/20211109155529.jpg


二进制
images/20211109155604.jpg


二进制
images/20211109155655.jpg


二进制
images/20211109155722.jpg


二进制
images/20211109155940.jpg


二进制
images/20211109160020.jpg


二进制
images/20211109160058.jpg


二进制
images/20211109160245.jpg


二进制
images/20211109160323.jpg


二进制
images/20211109160348.jpg


二进制
images/20211109163015.jpg


二进制
images/20211224061904.jpg


二进制
images/20220322141509.jpg


二进制
images/20220322141612.jpg


二进制
images/20220322141653.jpg


二进制
images/20220322141700.jpg


二进制
images/20220322141832.jpg


二进制
images/20220322141841.jpg


二进制
images/20220624090942.mp4


部分文件因为文件数量过多而无法显示