diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..efb220b9cfdd0ce05b9772945d4f76faf8d56eae
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+/vendor/
+.idea
+/Dist/*
+.DS_Store
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..89e08fb002e48e22363b7b3789a5470ffe71fea1
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ ' . $this->markLine($start, $end) . htmlspecialchars(implode("\n", $lines)) . '';
+ $index = 1;
+
+ while ($val = array_shift($this->_footnotes)) {
+ if (is_string($val)) {
+ $val .= " ↩";
+ } else {
+ $val[count($val) - 1] .= " ↩";
+ $val = count($val) > 1 ? $this->parse(implode("\n", $val)) : $this->parseInline($val[0]);
+ }
+
+ $html .= "
' . htmlspecialchars($matches[3]) . '
'
+ );
+ },
+ $text
+ );
+
+ // mathjax
+ $text = preg_replace_callback(
+ "/(^|[^\\\])(\\$+)(.+?)\\2/",
+ function ($matches) use ($self) {
+ return $matches[1] . $self->makeHolder(
+ $matches[2] . htmlspecialchars($matches[3]) . $matches[2]
+ );
+ },
+ $text
+ );
+
+ // escape
+ $text = preg_replace_callback(
+ "/\\\(.)/u",
+ function ($matches) use ($self) {
+ $prefix = preg_match("/^[-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]$/", $matches[1]) ? '' : '\\';
+ $escaped = htmlspecialchars($matches[1]);
+ $escaped = str_replace('$', '$', $escaped);
+ return $self->makeHolder($prefix . $escaped);
+ },
+ $text
+ );
+
+ // link
+ $text = preg_replace_callback(
+ "/<(https?:\/\/.+)>/i",
+ function ($matches) use ($self) {
+ $url = $self->cleanUrl($matches[1]);
+ $link = $self->call('parseLink', $matches[1]);
+
+ return $self->makeHolder(
+ "{$link}"
+ );
+ },
+ $text
+ );
+
+ // encode unsafe tags
+ $text = preg_replace_callback(
+ "/<(\/?)([a-z0-9-]+)(\s+[^>]*)?>/i",
+ function ($matches) use ($self, $whiteList) {
+ if ($self->_html || false !== stripos(
+ '|' . $self->_commonWhiteList . '|' . $whiteList . '|', '|' . $matches[2] . '|'
+ )) {
+ return $self->makeHolder($matches[0]);
+ } else {
+ return $self->makeHolder(htmlspecialchars($matches[0]));
+ }
+ },
+ $text
+ );
+
+ if ($this->_html) {
+ $text = preg_replace_callback("//", function ($matches) use ($self) {
+ return $self->makeHolder($matches[0]);
+ }, $text);
+ }
+
+ $text = str_replace(array('<', '>'), array('<', '>'), $text);
+
+ // footnote
+ $text = preg_replace_callback(
+ "/\[\^((?:[^\]]|\\\\\]|\\\\\[)+?)\]/",
+ function ($matches) use ($self) {
+ $id = array_search($matches[1], $self->_footnotes);
+
+ if (false === $id) {
+ $id = count($self->_footnotes) + 1;
+ $self->_footnotes[$id] = $self->parseInline($matches[1], '', false);
+ }
+
+ return $self->makeHolder(
+ "{$id}"
+ );
+ },
+ $text
+ );
+
+ // image
+ $text = preg_replace_callback(
+ "/!\[((?:[^\]]|\\\\\]|\\\\\[)*?)\]\(((?:[^\)]|\\\\\)|\\\\\()+?)\)/",
+ function ($matches) use ($self) {
+ $escaped = htmlspecialchars($self->escapeBracket($matches[1]));
+ $url = $self->escapeBracket($matches[2]);
+ $url = $self->cleanUrl($url);
+ return $self->makeHolder(
+ ""
+ );
+ },
+ $text
+ );
+
+ $text = preg_replace_callback(
+ "/!\[((?:[^\]]|\\\\\]|\\\\\[)*?)\]\[((?:[^\]]|\\\\\]|\\\\\[)+?)\]/",
+ function ($matches) use ($self) {
+ $escaped = htmlspecialchars($self->escapeBracket($matches[1]));
+
+ $result = isset( $self->_definitions[$matches[2]] ) ?
+ "
_definitions[$matches[2]]}\" alt=\"{$escaped}\" title=\"{$escaped}\">"
+ : $escaped;
+
+ return $self->makeHolder($result);
+ },
+ $text
+ );
+
+ // link
+ $text = preg_replace_callback(
+ "/\[((?:[^\]]|\\\\\]|\\\\\[)+?)\]\(((?:[^\)]|\\\\\)|\\\\\()+?)\)/",
+ function ($matches) use ($self) {
+ $escaped = $self->parseInline(
+ $self->escapeBracket($matches[1]), '', false, false
+ );
+ $url = $self->escapeBracket($matches[2]);
+ $url = $self->cleanUrl($url);
+ return $self->makeHolder("{$escaped}");
+ },
+ $text
+ );
+
+ $text = preg_replace_callback(
+ "/\[((?:[^\]]|\\\\\]|\\\\\[)+?)\]\[((?:[^\]]|\\\\\]|\\\\\[)+?)\]/",
+ function ($matches) use ($self) {
+ $escaped = $self->parseInline(
+ $self->escapeBracket($matches[1]), '', false
+ );
+ $result = isset( $self->_definitions[$matches[2]] ) ?
+ "_definitions[$matches[2]]}\">{$escaped}"
+ : $escaped;
+
+ return $self->makeHolder($result);
+ },
+ $text
+ );
+
+ // strong and em and some fuck
+ $text = $this->parseInlineCallback($text);
+ $text = preg_replace(
+ "/<([_a-z0-9-\.\+]+@[^@]+\.[a-z]{2,})>/i",
+ "\\1",
+ $text
+ );
+
+ // autolink url
+ if ($enableAutoLink) {
+ $text = preg_replace_callback(
+ "/(^|[^\"])((https?):[\p{L}_0-9-\.\/%#!@\?\+=~\|\,&\(\)]+)($|[^\"])/iu",
+ function ($matches) use ($self) {
+ $link = $self->call('parseLink', $matches[2]);
+ return "{$matches[1]}{$link}{$matches[4]}";
+ },
+ $text
+ );
+ }
+
+ $text = $this->call('afterParseInlineBeforeRelease', $text);
+ $text = $this->releaseHolder($text, $clearHolders);
+
+ $text = $this->call('afterParseInline', $text);
+
+ return $text;
+ }
+
+ /**
+ * @param $text
+ * @return mixed
+ */
+ public function parseInlineCallback($text)
+ {
+ $self = $this;
+
+ $text = preg_replace_callback(
+ "/(\*{3})(.+?)\\1/",
+ function ($matches) use ($self) {
+ return '' .
+ $self->parseInlineCallback($matches[2]) .
+ '';
+ },
+ $text
+ );
+
+ $text = preg_replace_callback(
+ "/(\*{2})(.+?)\\1/",
+ function ($matches) use ($self) {
+ return '' .
+ $self->parseInlineCallback($matches[2]) .
+ '';
+ },
+ $text
+ );
+
+ $text = preg_replace_callback(
+ "/(\*)(.+?)\\1/",
+ function ($matches) use ($self) {
+ return '' .
+ $self->parseInlineCallback($matches[2]) .
+ '';
+ },
+ $text
+ );
+
+ $text = preg_replace_callback(
+ "/(\s+|^)(_{3})(.+?)\\2(\s+|$)/",
+ function ($matches) use ($self) {
+ return $matches[1] . '' .
+ $self->parseInlineCallback($matches[3]) .
+ '' . $matches[4];
+ },
+ $text
+ );
+
+ $text = preg_replace_callback(
+ "/(\s+|^)(_{2})(.+?)\\2(\s+|$)/",
+ function ($matches) use ($self) {
+ return $matches[1] . '' .
+ $self->parseInlineCallback($matches[3]) .
+ '' . $matches[4];
+ },
+ $text
+ );
+
+ $text = preg_replace_callback(
+ "/(\s+|^)(_)(.+?)\\2(\s+|$)/",
+ function ($matches) use ($self) {
+ return $matches[1] . '' .
+ $self->parseInlineCallback($matches[3]) .
+ '' . $matches[4];
+ },
+ $text
+ );
+
+ $text = preg_replace_callback(
+ "/(~{2})(.+?)\\1/",
+ function ($matches) use ($self) {
+ return '
' .
+ $self->parseInlineCallback($matches[2]) .
+ '';
+ },
+ $text
+ );
+
+ return $text;
+ }
+
+ /**
+ * parseBlock
+ *
+ * @param string $text
+ * @param array $lines
+ * @return array
+ */
+ private function parseBlock($text, &$lines)
+ {
+ $lines = explode("\n", $text);
+ $this->_blocks = array();
+ $this->_current = 'normal';
+ $this->_pos = -1;
+
+ $state = array(
+ 'special' => implode("|", array_keys($this->_specialWhiteList)),
+ 'empty' => 0,
+ 'html' => false
+ );
+
+ // analyze by line
+ foreach ($lines as $key => $line) {
+ $block = $this->getBlock();
+ $args = array($block, $key, $line, &$state, $lines);
+
+ if ($this->_current != 'normal') {
+ $pass = call_user_func_array($this->_parsers[$this->_current], $args);
+
+ if (!$pass) {
+ continue;
+ }
+ }
+
+ foreach ($this->_parsers as $name => $parser) {
+ if ($name != $this->_current) {
+ $pass = call_user_func_array($parser, $args);
+
+ if (!$pass) {
+ break;
+ }
+ }
+ }
+ }
+
+ return $this->optimizeBlocks($this->_blocks, $lines);
+ }
+
+ /**
+ * @param $block
+ * @param $key
+ * @param $line
+ * @param $state
+ * @return bool
+ */
+ private function parseBlockList($block, $key, $line, &$state)
+ {
+ if ($this->isBlock('list') && !preg_match("/^\s*\[((?:[^\]]|\\]|\\[)+?)\]:\s*(.+)$/", $line)) {
+ if ($state['empty'] <= 1
+ && preg_match("/^(\s+)/", $line, $matches)
+ && strlen($matches[1]) > $block[3]) {
+
+ $state['empty'] = 0;
+ $this->setBlock($key);
+ return false;
+ } else if (preg_match("/^(\s*)$/", $line) && $state['empty'] == 0) {
+ $state['empty'] ++;
+ $this->setBlock($key);
+ return false;
+ }
+ }
+
+ if (preg_match("/^(\s*)((?:[0-9]+\.)|\-|\+|\*)\s+/i", $line, $matches)) {
+ $space = strlen($matches[1]);
+ $state['empty'] = 0;
+
+ // opened
+ if ($this->isBlock('list')) {
+ $this->setBlock($key, $space);
+ } else {
+ $this->startBlock('list', $key, $space);
+ }
+
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * @param $block
+ * @param $key
+ * @param $line
+ * @return bool
+ */
+ private function parseBlockCode($block, $key, $line)
+ {
+ if (preg_match("/^(\s*)(~{3,}|`{3,})([^`~]*)$/i", $line, $matches)) {
+ if ($this->isBlock('code')) {
+ $isAfterList = $block[3][2];
+
+ if ($isAfterList) {
+ $this->combineBlock()
+ ->setBlock($key);
+ } else {
+ $this->setBlock($key)
+ ->endBlock();
+ }
+ } else {
+ $isAfterList = false;
+
+ if ($this->isBlock('list')) {
+ $space = $block[3];
+
+ $isAfterList = ($space > 0 && strlen($matches[1]) >= $space)
+ || strlen($matches[1]) > $space;
+ }
+
+ $this->startBlock('code', $key, array(
+ $matches[1], $matches[3], $isAfterList
+ ));
+ }
+
+ return false;
+ } else if ($this->isBlock('code')) {
+ $this->setBlock($key);
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * @param $block
+ * @param $key
+ * @param $line
+ * @param $state
+ * @return bool
+ */
+ private function parseBlockShtml($block, $key, $line, &$state)
+ {
+ if ($this->_html) {
+ if (preg_match("/^(\s*)!!!(\s*)$/", $line, $matches)) {
+ if ($this->isBlock('shtml')) {
+ $this->setBlock($key)->endBlock();
+ } else {
+ $this->startBlock('shtml', $key);
+ }
+
+ return false;
+ } else if ($this->isBlock('shtml')) {
+ $this->setBlock($key);
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * @param $block
+ * @param $key
+ * @param $line
+ * @param $state
+ * @return bool
+ */
+ private function parseBlockAhtml($block, $key, $line, &$state)
+ {
+ if ($this->_html) {
+ if (preg_match("/^\s*<({$this->_blockHtmlTags})(\s+[^>]*)?>/i", $line, $matches)) {
+ if ($this->isBlock('ahtml')) {
+ $this->setBlock($key);
+ return false;
+ } else if (empty($matches[2]) || $matches[2] != '/') {
+ $this->startBlock('ahtml', $key);
+ preg_match_all("/<({$this->_blockHtmlTags})(\s+[^>]*)?>/i", $line, $allMatches);
+ $lastMatch = $allMatches[1][count($allMatches[0]) - 1];
+
+ if (strpos($line, "{$lastMatch}>") !== false) {
+ $this->endBlock();
+ } else {
+ $state['html'] = $lastMatch;
+ }
+ return false;
+ }
+ } else if (!!$state['html'] && strpos($line, "{$state['html']}>") !== false) {
+ $this->setBlock($key)->endBlock();
+ $state['html'] = false;
+ return false;
+ } else if ($this->isBlock('ahtml')) {
+ $this->setBlock($key);
+ return false;
+ } else if (preg_match("/^\s*\s*$/", $line, $matches)) {
+ $this->startBlock('ahtml', $key)->endBlock();
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * @param $block
+ * @param $key
+ * @param $line
+ * @return bool
+ */
+ private function parseBlockMath($block, $key, $line)
+ {
+ if (preg_match("/^(\s*)\\$\\$(\s*)$/", $line, $matches)) {
+ if ($this->isBlock('math')) {
+ $this->setBlock($key)->endBlock();
+ } else {
+ $this->startBlock('math', $key);
+ }
+
+ return false;
+ } else if ($this->isBlock('math')) {
+ $this->setBlock($key);
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * @param $block
+ * @param $key
+ * @param $line
+ * @param $state
+ * @return bool
+ */
+ private function parseBlockPre($block, $key, $line, &$state)
+ {
+ if (preg_match("/^ {4}/", $line)) {
+ if ($this->isBlock('pre')) {
+ $this->setBlock($key);
+ } else {
+ $this->startBlock('pre', $key);
+ }
+
+ return false;
+ } else if ($this->isBlock('pre') && preg_match("/^\s*$/", $line)) {
+ $this->setBlock($key);
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * @param $block
+ * @param $key
+ * @param $line
+ * @param $state
+ * @return bool
+ */
+ private function parseBlockHtml($block, $key, $line, &$state)
+ {
+ if (preg_match("/^\s*<({$state['special']})(\s+[^>]*)?>/i", $line, $matches)) {
+ $tag = strtolower($matches[1]);
+ if (!$this->isBlock('html', $tag) && !$this->isBlock('pre')) {
+ $this->startBlock('html', $key, $tag);
+ }
+
+ return false;
+ } else if (preg_match("/<\/({$state['special']})>\s*$/i", $line, $matches)) {
+ $tag = strtolower($matches[1]);
+
+ if ($this->isBlock('html', $tag)) {
+ $this->setBlock($key)
+ ->endBlock();
+ }
+
+ return false;
+ } else if ($this->isBlock('html')) {
+ $this->setBlock($key);
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * @param $block
+ * @param $key
+ * @param $line
+ * @return bool
+ */
+ private function parseBlockFootnote($block, $key, $line)
+ {
+ if (preg_match("/^\[\^((?:[^\]]|\\]|\\[)+?)\]:/", $line, $matches)) {
+ $space = strlen($matches[0]) - 1;
+ $this->startBlock('footnote', $key, array(
+ $space, $matches[1]
+ ));
+
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * @param $block
+ * @param $key
+ * @param $line
+ * @return bool
+ */
+ private function parseBlockDefinition($block, $key, $line)
+ {
+ if (preg_match("/^\s*\[((?:[^\]]|\\]|\\[)+?)\]:\s*(.+)$/", $line, $matches)) {
+ $this->_definitions[$matches[1]] = $this->cleanUrl($matches[2]);
+ $this->startBlock('definition', $key)
+ ->endBlock();
+
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * @param $block
+ * @param $key
+ * @param $line
+ * @return bool
+ */
+ private function parseBlockQuote($block, $key, $line)
+ {
+ if (preg_match("/^(\s*)>/", $line, $matches)) {
+ if ($this->isBlock('list') && strlen($matches[1]) > 0) {
+ $this->setBlock($key);
+ } else if ($this->isBlock('quote')) {
+ $this->setBlock($key);
+ } else {
+ $this->startBlock('quote', $key);
+ }
+
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * @param $block
+ * @param $key
+ * @param $line
+ * @param $state
+ * @param $lines
+ * @return bool
+ */
+ private function parseBlockTable($block, $key, $line, &$state, $lines)
+ {
+ if (preg_match("/^((?:(?:(?:\||\+)(?:[ :]*\-+[ :]*)(?:\||\+))|(?:(?:[ :]*\-+[ :]*)(?:\||\+)(?:[ :]*\-+[ :]*))|(?:(?:[ :]*\-+[ :]*)(?:\||\+))|(?:(?:\||\+)(?:[ :]*\-+[ :]*)))+)$/", $line, $matches)) {
+ if ($this->isBlock('table')) {
+ $block[3][0][] = $block[3][2];
+ $block[3][2]++;
+ $this->setBlock($key, $block[3]);
+ } else {
+ $head = 0;
+
+ if (empty($block) ||
+ $block[0] != 'normal' ||
+ preg_match("/^\s*$/", $lines[$block[2]])) {
+ $this->startBlock('table', $key);
+ } else {
+ $head = 1;
+ $this->backBlock(1, 'table');
+ }
+
+ if ($matches[1][0] == '|') {
+ $matches[1] = substr($matches[1], 1);
+
+ if ($matches[1][strlen($matches[1]) - 1] == '|') {
+ $matches[1] = substr($matches[1], 0, -1);
+ }
+ }
+
+ $rows = preg_split("/(\+|\|)/", $matches[1]);
+ $aligns = array();
+ foreach ($rows as $row) {
+ $align = 'none';
+
+ if (preg_match("/^\s*(:?)\-+(:?)\s*$/", $row, $matches)) {
+ if (!empty($matches[1]) && !empty($matches[2])) {
+ $align = 'center';
+ } else if (!empty($matches[1])) {
+ $align = 'left';
+ } else if (!empty($matches[2])) {
+ $align = 'right';
+ }
+ }
+
+ $aligns[] = $align;
+ }
+
+ $this->setBlock($key, array(array($head), $aligns, $head + 1));
+ }
+
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * @param $block
+ * @param $key
+ * @param $line
+ * @return bool
+ */
+ private function parseBlockSh($block, $key, $line)
+ {
+ if (preg_match("/^(#+)(.*)$/", $line, $matches)) {
+ $num = min(strlen($matches[1]), 6);
+ $this->startBlock('sh', $key, $num)
+ ->endBlock();
+
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * @param $block
+ * @param $key
+ * @param $line
+ * @param $state
+ * @param $lines
+ * @return bool
+ */
+ private function parseBlockMh($block, $key, $line, &$state, $lines)
+ {
+ if (preg_match("/^\s*((=|-){2,})\s*$/", $line, $matches)
+ && ($block && $block[0] == "normal" && !preg_match("/^\s*$/", $lines[$block[2]]))) { // check if last line isn't empty
+ if ($this->isBlock('normal')) {
+ $this->backBlock(1, 'mh', $matches[1][0] == '=' ? 1 : 2)
+ ->setBlock($key)
+ ->endBlock();
+ } else {
+ $this->startBlock('normal', $key);
+ }
+
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * @param $block
+ * @param $key
+ * @param $line
+ * @return bool
+ */
+ private function parseBlockShr($block, $key, $line)
+ {
+ if (preg_match("/^(\* *){3,}\s*$/", $line)) {
+ $this->startBlock('hr', $key)
+ ->endBlock();
+
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * @param $block
+ * @param $key
+ * @param $line
+ * @return bool
+ */
+ private function parseBlockDhr($block, $key, $line)
+ {
+ if (preg_match("/^(- *){3,}\s*$/", $line)) {
+ $this->startBlock('hr', $key)
+ ->endBlock();
+
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * @param $block
+ * @param $key
+ * @param $line
+ * @param $state
+ * @return bool
+ */
+ private function parseBlockDefault($block, $key, $line, &$state)
+ {
+ if ($this->isBlock('footnote')) {
+ preg_match("/^(\s*)/", $line, $matches);
+ if (strlen($matches[1]) >= $block[3][0]) {
+ $this->setBlock($key);
+ } else {
+ $this->startBlock('normal', $key);
+ }
+ } else if ($this->isBlock('table')) {
+ if (false !== strpos($line, '|')) {
+ $block[3][2] ++;
+ $this->setBlock($key, $block[3]);
+ } else {
+ $this->startBlock('normal', $key);
+ }
+ } else if ($this->isBlock('quote')) {
+ if (!preg_match("/^(\s*)$/", $line)) { // empty line
+ $this->setBlock($key);
+ } else {
+ $this->startBlock('normal', $key);
+ }
+ } else {
+ if (empty($block) || $block[0] != 'normal') {
+ $this->startBlock('normal', $key);
+ } else {
+ $this->setBlock($key);
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * @param array $blocks
+ * @param array $lines
+ * @return array
+ */
+ private function optimizeBlocks(array $blocks, array $lines)
+ {
+ $blocks = $this->call('beforeOptimizeBlocks', $blocks, $lines);
+
+ $key = 0;
+ while (isset($blocks[$key])) {
+ $moved = false;
+
+ $block = &$blocks[$key];
+ $prevBlock = isset($blocks[$key - 1]) ? $blocks[$key - 1] : NULL;
+ $nextBlock = isset($blocks[$key + 1]) ? $blocks[$key + 1] : NULL;
+
+ list ($type, $from, $to) = $block;
+
+ if ('pre' == $type) {
+ $isEmpty = array_reduce(
+ array_slice($lines, $block[1], $block[2] - $block[1] + 1),
+ function ($result, $line) {
+ return preg_match("/^\s*$/", $line) && $result;
+ },
+ true
+ );
+
+ if ($isEmpty) {
+ $block[0] = $type = 'normal';
+ }
+ }
+
+ if ('normal' == $type) {
+ // combine two blocks
+ $types = array('list', 'quote');
+
+ if ($from == $to && preg_match("/^\s*$/", $lines[$from])
+ && !empty($prevBlock) && !empty($nextBlock)) {
+ if ($prevBlock[0] == $nextBlock[0] && in_array($prevBlock[0], $types)) {
+ // combine 3 blocks
+ $blocks[$key - 1] = array(
+ $prevBlock[0], $prevBlock[1], $nextBlock[2], NULL
+ );
+ array_splice($blocks, $key, 2);
+
+ // do not move
+ $moved = true;
+ }
+ }
+ }
+
+ if (!$moved) {
+ $key ++;
+ }
+ }
+
+ return $this->call('afterOptimizeBlocks', $blocks, $lines);
+ }
+
+ /**
+ * parseCode
+ *
+ * @param array $lines
+ * @param array $parts
+ * @param int $start
+ * @return string
+ */
+ private function parseCode(array $lines, array $parts, $start)
+ {
+ list ($blank, $lang) = $parts;
+ $lang = trim($lang);
+ $count = strlen($blank);
+
+ if (!preg_match("/^[_a-z0-9-\+\#\:\.]+$/i", $lang)) {
+ $lang = NULL;
+ } else {
+ $parts = explode(':', $lang);
+ if (count($parts) > 1) {
+ list ($lang, $rel) = $parts;
+ $lang = trim($lang);
+ $rel = trim($rel);
+ }
+ }
+
+ $isEmpty = true;
+
+ $lines = array_map(function ($line) use ($count, &$isEmpty) {
+ $line = preg_replace("/^[ ]{{$count}}/", '', $line);
+ if ($isEmpty && !preg_match("/^\s*$/", $line)) {
+ $isEmpty = false;
+ }
+
+ return htmlspecialchars($line);
+ }, array_slice($lines, 1, -1));
+ $str = implode("\n", $this->markLines($lines, $start + 1));
+
+ return $isEmpty ? '' :
+ '
';
+ }
+
+ /**
+ * parsePre
+ *
+ * @param array $lines
+ * @param mixed $value
+ * @param int $start
+ * @return string
+ */
+ private function parsePre(array $lines, $value, $start)
+ {
+ foreach ($lines as &$line) {
+ $line = htmlspecialchars(substr($line, 4));
+ }
+
+ $str = implode("\n", $this->markLines($lines, $start));
+ return preg_match("/^\s*$/", $str) ? '' : ''
+ . $str . '
';
+ }
+
+ /**
+ * parseAhtml
+ *
+ * @param array $lines
+ * @param mixed $value
+ * @param int $start
+ * @return string
+ */
+ private function parseAhtml(array $lines, $value, $start)
+ {
+ return trim(implode("\n", $this->markLines($lines, $start)));
+ }
+
+ /**
+ * parseShtml
+ *
+ * @param array $lines
+ * @param mixed $value
+ * @param int $start
+ * @return string
+ */
+ private function parseShtml(array $lines, $value, $start)
+ {
+ return trim(implode("\n", $this->markLines(array_slice($lines, 1, -1), $start + 1)));
+ }
+
+ /**
+ * parseMath
+ *
+ * @param array $lines
+ * @param mixed $value
+ * @param int $start
+ * @param int $end
+ * @return string
+ */
+ private function parseMath(array $lines, $value, $start, $end)
+ {
+ return '' . $str . '
' . $this->parse($str, true, $start) . '
';
+ }
+
+ /**
+ * parseList
+ *
+ * @param array $lines
+ * @param mixed $value
+ * @param int $start
+ * @return string
+ */
+ private function parseList(array $lines, $value, $start)
+ {
+ $html = '';
+ $minSpace = 99999;
+ $secondMinSpace = 99999;
+ $found = false;
+ $secondFound = false;
+ $rows = array();
+
+ // count levels
+ foreach ($lines as $key => $line) {
+ if (preg_match("/^(\s*)((?:[0-9]+\.?)|\-|\+|\*)(\s+)(.*)$/i", $line, $matches)) {
+ $space = strlen($matches[1]);
+ $type = false !== strpos('+-*', $matches[2]) ? 'ul' : 'ol';
+ $minSpace = min($space, $minSpace);
+ $found = true;
+
+ if ($space > 0) {
+ $secondMinSpace = min($space, $secondMinSpace);
+ $secondFound = true;
+ }
+
+ $rows[] = array($space, $type, $line, $matches[4]);
+ } else {
+ $rows[] = $line;
+
+ if (preg_match("/^(\s*)/", $line, $matches)) {
+ $space = strlen($matches[1]);
+
+ if ($space > 0) {
+ $secondMinSpace = min($space, $secondMinSpace);
+ $secondFound = true;
+ }
+ }
+ }
+ }
+
+ $minSpace = $found ? $minSpace : 0;
+ $secondMinSpace = $secondFound ? $secondMinSpace : $minSpace;
+
+ $lastType = '';
+ $leftLines = array();
+ $leftStart = 0;
+
+ foreach ($rows as $key => $row) {
+ if (is_array($row)) {
+ list ($space, $type, $line, $text) = $row;
+
+ if ($space != $minSpace) {
+ $leftLines[] = preg_replace("/^\s{" . $secondMinSpace . "}/", '', $line);
+ } else {
+ if (!empty($leftLines)) {
+ $html .= "';
+ $body = $head ? NULL : true;
+ $output = false;
+
+ foreach ($lines as $key => $line) {
+ if (in_array($key, $ignores)) {
+ if ($head && $output) {
+ $head = false;
+ $body = true;
+ }
+
+ continue;
+ }
+
+ $line = trim($line);
+ $output = true;
+
+ if ($line[0] == '|') {
+ $line = substr($line, 1);
+
+ if ($line[strlen($line) - 1] == '|') {
+ $line = substr($line, 0, -1);
+ }
+ }
+
+
+ $rows = array_map(function ($row) {
+ if (preg_match("/^\s*$/", $row)) {
+ return ' ';
+ } else {
+ return trim($row);
+ }
+ }, explode('|', $line));
+ $columns = array();
+ $last = -1;
+
+ foreach ($rows as $row) {
+ if (strlen($row) > 0) {
+ $last ++;
+ $columns[$last] = array(
+ isset($columns[$last]) ? $columns[$last][0] + 1 : 1, $row
+ );
+ } else if (isset($columns[$last])) {
+ $columns[$last][0] ++;
+ } else {
+ $columns[0] = array(1, $row);
+ }
+ }
+
+ if ($head) {
+ $html .= '';
+ } else if ($body) {
+ $html .= '';
+ }
+
+ $html .= '
';
+ return $html;
+ }
+
+ /**
+ * parseHr
+ *
+ * @param array $lines
+ * @param array $value
+ * @param int $start
+ * @return string
+ */
+ private function parseHr($lines, $value, $start)
+ {
+ return $this->_line ? '_line ? ' class="line" data-start="'
+ . ($start + $key) . '" data-end="' . ($start + $key)
+ . '" data-id="' . $this->_uniqid . '"' : '') . '>';
+
+ foreach ($columns as $key => $column) {
+ list ($num, $text) = $column;
+ $tag = $head ? 'th' : 'td';
+
+ $html .= "<{$tag}";
+ if ($num > 1) {
+ $html .= " colspan=\"{$num}\"";
+ }
+
+ if (isset($aligns[$key]) && $aligns[$key] != 'none') {
+ $html .= " align=\"{$aligns[$key]}\"";
+ }
+
+ $html .= '>' . $this->parseInline($text) . "{$tag}>";
+ }
+
+ $html .= ' ';
+
+ if ($head) {
+ $html .= '';
+ } else if ($body) {
+ $body = false;
+ }
+ }
+
+ if ($body !== NULL) {
+ $html .= '';
+ }
+
+ $html .= '
' : '
';
+ }
+
+ /**
+ * parseNormal
+ *
+ * @param array $lines
+ * @param bool $inline
+ * @param int $start
+ * @return string
+ */
+ private function parseNormal(array $lines, $inline = false, $start)
+ {
+ foreach ($lines as $key => &$line) {
+ $line = $this->parseInline($line);
+
+ if (!preg_match("/^\s*$/", $line)) {
+ $line = $this->markLine($start + $key) . $line;
+ }
+ }
+
+ $str = trim(implode("\n", $lines));
+ $str = preg_replace("/(\n\s*){2,}/", "
", $str);
+ $str = preg_replace("/\n/", "
", $str);
+
+ return preg_match("/^\s*$/", $str) ? '' : ($inline ? $str : "
{$str}
"); + } + + /** + * parseFootnote + * + * @param array $lines + * @param array $value + * @return string + */ + private function parseFootnote(array $lines, array $value) + { + list($space, $note) = $value; + $index = array_search($note, $this->_footnotes); + + if (false !== $index) { + $lines[0] = preg_replace("/^\[\^((?:[^\]]|\\]|\\[)+?)\]:/", '', $lines[0]); + $this->_footnotes[$index] = $lines; + } + + return ''; + } + + /** + * parseDefine + * + * @return string + */ + private function parseDefinition() + { + return ''; + } + + /** + * parseHtml + * + * @param array $lines + * @param string $type + * @param int $start + * @return string + */ + private function parseHtml(array $lines, $type, $start) + { + foreach ($lines as &$line) { + $line = $this->parseInline($line, + isset($this->_specialWhiteList[$type]) ? $this->_specialWhiteList[$type] : ''); + } + + return implode("\n", $this->markLines($lines, $start)); + } + + /** + * @param $url + * @return string + */ + public function cleanUrl($url) + { + if (preg_match("/^\s*((http|https|ftp|mailto):[\p{L}_a-z0-9-:\.\*\/%#;!@\?\+=~\|\,&\(\)]+)/iu", $url, $matches)) { + return $matches[1]; + } else if (preg_match("/^\s*([\p{L}_a-z0-9-:\.\*\/%#!@\?\+=~\|\,&]+)/iu", $url, $matches)) { + return $matches[1]; + } else { + return '#'; + } + } + + /** + * @param $str + * @return mixed + */ + public function escapeBracket($str) + { + return str_replace( + array('\[', '\]', '\(', '\)'), array('[', ']', '(', ')'), $str + ); + } + + /** + * startBlock + * + * @param mixed $type + * @param mixed $start + * @param mixed $value + * @return $this + */ + private function startBlock($type, $start, $value = NULL) + { + $this->_pos ++; + $this->_current = $type; + + $this->_blocks[$this->_pos] = array($type, $start, $start, $value); + + return $this; + } + + /** + * endBlock + * + * @return $this + */ + private function endBlock() + { + $this->_current = 'normal'; + return $this; + } + + /** + * isBlock + * + * @param mixed $type + * @param mixed $value + * @return bool + */ + private function isBlock($type, $value = NULL) + { + return $this->_current == $type + && (NULL === $value ? true : $this->_blocks[$this->_pos][3] == $value); + } + + /** + * getBlock + * + * @return array + */ + private function getBlock() + { + return isset($this->_blocks[$this->_pos]) ? $this->_blocks[$this->_pos] : NULL; + } + + /** + * setBlock + * + * @param mixed $to + * @param mixed $value + * @return $this + */ + private function setBlock($to = NULL, $value = NULL) + { + if (NULL !== $to) { + $this->_blocks[$this->_pos][2] = $to; + } + + if (NULL !== $value) { + $this->_blocks[$this->_pos][3] = $value; + } + + return $this; + } + + /** + * backBlock + * + * @param mixed $step + * @param mixed $type + * @param mixed $value + * @return $this + */ + private function backBlock($step, $type, $value = NULL) + { + if ($this->_pos < 0) { + return $this->startBlock($type, 0, $value); + } + + $last = $this->_blocks[$this->_pos][2]; + $this->_blocks[$this->_pos][2] = $last - $step; + + if ($this->_blocks[$this->_pos][1] <= $this->_blocks[$this->_pos][2]) { + $this->_pos ++; + } + + $this->_current = $type; + $this->_blocks[$this->_pos] = array( + $type, $last - $step + 1, $last, $value + ); + + return $this; + } + + /** + * @return $this + */ + private function combineBlock() + { + if ($this->_pos < 1) { + return $this; + } + + $prev = $this->_blocks[$this->_pos - 1]; + $current = $this->_blocks[$this->_pos]; + + $prev[2] = $current[2]; + $this->_blocks[$this->_pos - 1] = $prev; + $this->_current = $prev[0]; + unset($this->_blocks[$this->_pos]); + $this->_pos --; + + return $this; + } +} diff --git a/Lib/echoLib.php b/Lib/echoLib.php new file mode 100644 index 0000000000000000000000000000000000000000..d879faffca2342ccdf87bee401627081b74650d1 --- /dev/null +++ b/Lib/echoLib.php @@ -0,0 +1,11 @@ + $v) { + $contents = str_replace("{{{$k}}}", $v, $contents); + } + } + return $contents; +} \ No newline at end of file diff --git a/Lib/main.php b/Lib/main.php new file mode 100644 index 0000000000000000000000000000000000000000..db3083fbc032fad56b9742eb8b992e0bea09ba0f --- /dev/null +++ b/Lib/main.php @@ -0,0 +1,16 @@ + $v) { + foreach ($v as $vv) { + $_json[$k][] = $vv; + } + } + $jsonRes = json_encode($_json); + file_put_contents($dbFilePath, $jsonRes); + } +} \ No newline at end of file diff --git a/README.en.md b/README.en.md deleted file mode 100644 index 3bbb1d5c70eba0b518cb0e9351f8c282c8449e83..0000000000000000000000000000000000000000 --- a/README.en.md +++ /dev/null @@ -1,36 +0,0 @@ -# meoBlog - -#### Description -一个喵呜的博客系统(不知道能不能写出来) - -#### Software Architecture -Software architecture description - -#### Installation - -1. xxxx -2. xxxx -3. xxxx - -#### Instructions - -1. xxxx -2. xxxx -3. xxxx - -#### Contribution - -1. Fork the repository -2. Create Feat_xxx branch -3. Commit your code -4. Create Pull Request - - -#### Gitee Feature - -1. You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md -2. Gitee blog [blog.gitee.com](https://blog.gitee.com) -3. Explore open source project [https://gitee.com/explore](https://gitee.com/explore) -4. The most valuable open source project [GVP](https://gitee.com/gvp) -5. The manual of Gitee [https://gitee.com/help](https://gitee.com/help) -6. The most popular members [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) diff --git a/README.md b/README.md index e851e880eee9146495ed29c8cfda698e9765411c..a39b4fa34bc2175579804d4a2790d4cf3101b9e4 100644 --- a/README.md +++ b/README.md @@ -1,37 +1,228 @@ # meoBlog -#### 介绍 -一个喵呜的博客系统(不知道能不能写出来) +是一个会meo meo叫的博客系统。 -#### 软件架构 -软件架构说明 +``` + _ _ + _ __ ___ ___ ___ | |__ | | ___ __ _ +| '_ ` _ \ / _ \/ _ \ | '_ \| |/ _ \ / _` | +| | | | | | \__/ (_) | | |_) | | (_) | (_| | +|_| |_| |_|\___|\___/ |_.__/|_|\___/ \__, | + |___/ +``` +## 基础使用 -#### 安装教程 +### 安装并快速开始 -1. xxxx -2. xxxx -3. xxxx +在安装前,你需要先准备好必要的环境。 -#### 使用说明 +> php 7.4 + +> +>composer 1.9.1 + -1. xxxx -2. xxxx -3. xxxx +接下来开始安装项目。 -#### 参与贡献 +- 访问[github](https://github.com/xtzero/meoBlog)的`main`分支,或[gitee](https://gitee.com/xtzero/meoBlog)的`master`分支,克隆项目到本地。 -1. Fork 本仓库 -2. 新建 Feat_xxx 分支 -3. 提交代码 -4. 新建 Pull Request +```shell script +$ git clone https://gitee.com/xtzero/meoBlog.git +//或 git clone https://github.com/xtzero/meoBlog.git +``` +- 使用composer安装依赖。 -#### 特技 +```shell script +$ composer install -vvv +``` -1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md -2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com) -3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目 -4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目 -5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help) -6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) +- 使用`meo`命令生成页面,开启服务。 + +```shell script +$ php meo s + +[Sun Jan 17 20:18:29 2021] PHP 7.4.1 Development Server (http://localhost:8888) started +``` + +- 点击控制台显示的地址,访问页面。 + +http://localhost:8888 + +> 文档约定 +> +>在本文档中,使用`/Volumes/proj/meoBlog`当做项目的根目录。 +> +>在本文档中,`$`表示终端提示符,`$`后是执行的命令,后面的行则是命令执行之后的回显。 +> +>在本文档中,会使用`[name]`来包含一个可选参数,使用`