packages feed

Blogdown (empty) → 0.1.0

raw patch · 22 files changed

+2082/−0 lines, 22 filesdep +MissingHdep +basedep +containerssetup-changed

Dependencies added: MissingH, base, containers, parsec

Files

+ Blogdown.cabal view
@@ -0,0 +1,47 @@+name:                Blogdown+version:             0.1.0+synopsis:            A markdown-like markup language designed for blog posts+description:         A library and executable that implement a modified, extended version of Markdown designed for writing blog posts.+license:             AGPL-3+license-file:        LICENSE+author:              Alex Becker+maintainer:          acbecker@uchicago.edu+copyright:           (c) 2017 Alex Becker+category:            Web+build-type:          Simple+extra-source-files:+    Readme.html+    ChangeLog.md+    Readme.md+data-files:          assets/footnotes.css, assets/footnotes.js+cabal-version:       >=1.10++library+  exposed-modules:     Parsing.Parse, Rendering.Render, Rendering.RenderOptions+  other-modules:       AST, Parsing.ParseBlock, Parsing.ParseInline, Parsing.Text, Parsing.Utils, Parsing.ParseFootnotes, Parsing.ParseHtml, Parsing.State, Parsing.TextUtils, Paths_Blogdown+  other-extensions:    CPP, DeriveGeneric+  build-depends:       base >=4.9 && <4.10, parsec >=3.1 && <3.2, containers >=0.5 && <0.6, MissingH >=1.4 && <1.5+  hs-source-dirs:      src+  default-language:    Haskell2010+  cpp-options:         -DCABAL++executable Blogdown+  main-is:             Blogdown.hs+  other-extensions:    CPP, DeriveGeneric+  build-depends:       base >=4.9 && <4.10, parsec >=3.1 && <3.2, containers >=0.5 && <0.6, MissingH >=1.4 && <1.5+  hs-source-dirs:      src+  default-language:    Haskell2010+  cpp-options:         -DCABAL++test-suite Test+  type:                exitcode-stdio-1.0+  main-is:             Test.hs+  other-extensions:    CPP, DeriveGeneric+  build-depends:       base >=4.9 && <4.10, parsec >=3.1 && <3.2, containers >=0.5 && <0.6, MissingH >=1.4 && <1.5+  hs-source-dirs:      src, test+  default-language:    Haskell2010+  cpp-options:         -DCABAL++source-repository Head+  type:                git+  location:            https://github.com/alexbecker/blogdown
+ ChangeLog.md view
@@ -0,0 +1,5 @@+# Revision history for Blogdown++## 0.1.0  -- 2017-04-02++* Released as a Cabal package.
+ LICENSE view
@@ -0,0 +1,661 @@+                    GNU AFFERO GENERAL PUBLIC LICENSE+                       Version 3, 19 November 2007++ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>+ Everyone is permitted to copy and distribute verbatim copies+ of this license document, but changing it is not allowed.++                            Preamble++  The GNU Affero General Public License is a free, copyleft license for+software and other kinds of works, specifically designed to ensure+cooperation with the community in the case of network server software.++  The licenses for most software and other practical works are designed+to take away your freedom to share and change the works.  By contrast,+our General Public Licenses are intended to guarantee your freedom to+share and change all versions of a program--to make sure it remains free+software for all its users.++  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+them 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.++  Developers that use our General Public Licenses protect your rights+with two steps: (1) assert copyright on the software, and (2) offer+you this License which gives you legal permission to copy, distribute+and/or modify the software.++  A secondary benefit of defending all users' freedom is that+improvements made in alternate versions of the program, if they+receive widespread use, become available for other developers to+incorporate.  Many developers of free software are heartened and+encouraged by the resulting cooperation.  However, in the case of+software used on network servers, this result may fail to come about.+The GNU General Public License permits making a modified version and+letting the public access it on a server without ever releasing its+source code to the public.++  The GNU Affero General Public License is designed specifically to+ensure that, in such cases, the modified source code becomes available+to the community.  It requires the operator of a network server to+provide the source code of the modified version running there to the+users of that server.  Therefore, public use of a modified version, on+a publicly accessible server, gives the public access to the source+code of the modified version.++  An older license, called the Affero General Public License and+published by Affero, was designed to accomplish similar goals.  This is+a different license, not a version of the Affero GPL, but Affero has+released a new version of the Affero GPL which permits relicensing under+this license.++  The precise terms and conditions for copying, distribution and+modification follow.++                       TERMS AND CONDITIONS++  0. Definitions.++  "This License" refers to version 3 of the GNU Affero General Public License.++  "Copyright" also means copyright-like laws that apply to other kinds of+works, such as semiconductor masks.++  "The Program" refers to any copyrightable work licensed under this+License.  Each licensee is addressed as "you".  "Licensees" and+"recipients" may be individuals or organizations.++  To "modify" a work means to copy from or adapt all or part of the work+in a fashion requiring copyright permission, other than the making of an+exact copy.  The resulting work is called a "modified version" of the+earlier work or a work "based on" the earlier work.++  A "covered work" means either the unmodified Program or a work based+on the Program.++  To "propagate" a work means to do anything with it that, without+permission, would make you directly or secondarily liable for+infringement under applicable copyright law, except executing it on a+computer or modifying a private copy.  Propagation includes copying,+distribution (with or without modification), making available to the+public, and in some countries other activities as well.++  To "convey" a work means any kind of propagation that enables other+parties to make or receive copies.  Mere interaction with a user through+a computer network, with no transfer of a copy, is not conveying.++  An interactive user interface displays "Appropriate Legal Notices"+to the extent that it includes a convenient and prominently visible+feature that (1) displays an appropriate copyright notice, and (2)+tells the user that there is no warranty for the work (except to the+extent that warranties are provided), that licensees may convey the+work under this License, and how to view a copy of this License.  If+the interface presents a list of user commands or options, such as a+menu, a prominent item in the list meets this criterion.++  1. Source Code.++  The "source code" for a work means the preferred form of the work+for making modifications to it.  "Object code" means any non-source+form of a work.++  A "Standard Interface" means an interface that either is an official+standard defined by a recognized standards body, or, in the case of+interfaces specified for a particular programming language, one that+is widely used among developers working in that language.++  The "System Libraries" of an executable work include anything, other+than the work as a whole, that (a) is included in the normal form of+packaging a Major Component, but which is not part of that Major+Component, and (b) serves only to enable use of the work with that+Major Component, or to implement a Standard Interface for which an+implementation is available to the public in source code form.  A+"Major Component", in this context, means a major essential component+(kernel, window system, and so on) of the specific operating system+(if any) on which the executable work runs, or a compiler used to+produce the work, or an object code interpreter used to run it.++  The "Corresponding Source" for a work in object code form means all+the source code needed to generate, install, and (for an executable+work) run the object code and to modify the work, including scripts to+control those activities.  However, it does not include the work's+System Libraries, or general-purpose tools or generally available free+programs which are used unmodified in performing those activities but+which are not part of the work.  For example, Corresponding Source+includes interface definition files associated with source files for+the work, and the source code for shared libraries and dynamically+linked subprograms that the work is specifically designed to require,+such as by intimate data communication or control flow between those+subprograms and other parts of the work.++  The Corresponding Source need not include anything that users+can regenerate automatically from other parts of the Corresponding+Source.++  The Corresponding Source for a work in source code form is that+same work.++  2. Basic Permissions.++  All rights granted under this License are granted for the term of+copyright on the Program, and are irrevocable provided the stated+conditions are met.  This License explicitly affirms your unlimited+permission to run the unmodified Program.  The output from running a+covered work is covered by this License only if the output, given its+content, constitutes a covered work.  This License acknowledges your+rights of fair use or other equivalent, as provided by copyright law.++  You may make, run and propagate covered works that you do not+convey, without conditions so long as your license otherwise remains+in force.  You may convey covered works to others for the sole purpose+of having them make modifications exclusively for you, or provide you+with facilities for running those works, provided that you comply with+the terms of this License in conveying all material for which you do+not control copyright.  Those thus making or running the covered works+for you must do so exclusively on your behalf, under your direction+and control, on terms that prohibit them from making any copies of+your copyrighted material outside their relationship with you.++  Conveying under any other circumstances is permitted solely under+the conditions stated below.  Sublicensing is not allowed; section 10+makes it unnecessary.++  3. Protecting Users' Legal Rights From Anti-Circumvention Law.++  No covered work shall be deemed part of an effective technological+measure under any applicable law fulfilling obligations under article+11 of the WIPO copyright treaty adopted on 20 December 1996, or+similar laws prohibiting or restricting circumvention of such+measures.++  When you convey a covered work, you waive any legal power to forbid+circumvention of technological measures to the extent such circumvention+is effected by exercising rights under this License with respect to+the covered work, and you disclaim any intention to limit operation or+modification of the work as a means of enforcing, against the work's+users, your or third parties' legal rights to forbid circumvention of+technological measures.++  4. Conveying Verbatim Copies.++  You may convey 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;+keep intact all notices stating that this License and any+non-permissive terms added in accord with section 7 apply to the code;+keep intact all notices of the absence of any warranty; and give all+recipients a copy of this License along with the Program.++  You may charge any price or no price for each copy that you convey,+and you may offer support or warranty protection for a fee.++  5. Conveying Modified Source Versions.++  You may convey a work based on the Program, or the modifications to+produce it from the Program, in the form of source code under the+terms of section 4, provided that you also meet all of these conditions:++    a) The work must carry prominent notices stating that you modified+    it, and giving a relevant date.++    b) The work must carry prominent notices stating that it is+    released under this License and any conditions added under section+    7.  This requirement modifies the requirement in section 4 to+    "keep intact all notices".++    c) You must license the entire work, as a whole, under this+    License to anyone who comes into possession of a copy.  This+    License will therefore apply, along with any applicable section 7+    additional terms, to the whole of the work, and all its parts,+    regardless of how they are packaged.  This License gives no+    permission to license the work in any other way, but it does not+    invalidate such permission if you have separately received it.++    d) If the work has interactive user interfaces, each must display+    Appropriate Legal Notices; however, if the Program has interactive+    interfaces that do not display Appropriate Legal Notices, your+    work need not make them do so.++  A compilation of a covered work with other separate and independent+works, which are not by their nature extensions of the covered work,+and which are not combined with it such as to form a larger program,+in or on a volume of a storage or distribution medium, is called an+"aggregate" if the compilation and its resulting copyright are not+used to limit the access or legal rights of the compilation's users+beyond what the individual works permit.  Inclusion of a covered work+in an aggregate does not cause this License to apply to the other+parts of the aggregate.++  6. Conveying Non-Source Forms.++  You may convey a covered work in object code form under the terms+of sections 4 and 5, provided that you also convey the+machine-readable Corresponding Source under the terms of this License,+in one of these ways:++    a) Convey the object code in, or embodied in, a physical product+    (including a physical distribution medium), accompanied by the+    Corresponding Source fixed on a durable physical medium+    customarily used for software interchange.++    b) Convey the object code in, or embodied in, a physical product+    (including a physical distribution medium), accompanied by a+    written offer, valid for at least three years and valid for as+    long as you offer spare parts or customer support for that product+    model, to give anyone who possesses the object code either (1) a+    copy of the Corresponding Source for all the software in the+    product that is covered by this License, on a durable physical+    medium customarily used for software interchange, for a price no+    more than your reasonable cost of physically performing this+    conveying of source, or (2) access to copy the+    Corresponding Source from a network server at no charge.++    c) Convey individual copies of the object code with a copy of the+    written offer to provide the Corresponding Source.  This+    alternative is allowed only occasionally and noncommercially, and+    only if you received the object code with such an offer, in accord+    with subsection 6b.++    d) Convey the object code by offering access from a designated+    place (gratis or for a charge), and offer equivalent access to the+    Corresponding Source in the same way through the same place at no+    further charge.  You need not require recipients to copy the+    Corresponding Source along with the object code.  If the place to+    copy the object code is a network server, the Corresponding Source+    may be on a different server (operated by you or a third party)+    that supports equivalent copying facilities, provided you maintain+    clear directions next to the object code saying where to find the+    Corresponding Source.  Regardless of what server hosts the+    Corresponding Source, you remain obligated to ensure that it is+    available for as long as needed to satisfy these requirements.++    e) Convey the object code using peer-to-peer transmission, provided+    you inform other peers where the object code and Corresponding+    Source of the work are being offered to the general public at no+    charge under subsection 6d.++  A separable portion of the object code, whose source code is excluded+from the Corresponding Source as a System Library, need not be+included in conveying the object code work.++  A "User Product" is either (1) a "consumer product", which means any+tangible personal property which is normally used for personal, family,+or household purposes, or (2) anything designed or sold for incorporation+into a dwelling.  In determining whether a product is a consumer product,+doubtful cases shall be resolved in favor of coverage.  For a particular+product received by a particular user, "normally used" refers to a+typical or common use of that class of product, regardless of the status+of the particular user or of the way in which the particular user+actually uses, or expects or is expected to use, the product.  A product+is a consumer product regardless of whether the product has substantial+commercial, industrial or non-consumer uses, unless such uses represent+the only significant mode of use of the product.++  "Installation Information" for a User Product means any methods,+procedures, authorization keys, or other information required to install+and execute modified versions of a covered work in that User Product from+a modified version of its Corresponding Source.  The information must+suffice to ensure that the continued functioning of the modified object+code is in no case prevented or interfered with solely because+modification has been made.++  If you convey an object code work under this section in, or with, or+specifically for use in, a User Product, and the conveying occurs as+part of a transaction in which the right of possession and use of the+User Product is transferred to the recipient in perpetuity or for a+fixed term (regardless of how the transaction is characterized), the+Corresponding Source conveyed under this section must be accompanied+by the Installation Information.  But this requirement does not apply+if neither you nor any third party retains the ability to install+modified object code on the User Product (for example, the work has+been installed in ROM).++  The requirement to provide Installation Information does not include a+requirement to continue to provide support service, warranty, or updates+for a work that has been modified or installed by the recipient, or for+the User Product in which it has been modified or installed.  Access to a+network may be denied when the modification itself materially and+adversely affects the operation of the network or violates the rules and+protocols for communication across the network.++  Corresponding Source conveyed, and Installation Information provided,+in accord with this section must be in a format that is publicly+documented (and with an implementation available to the public in+source code form), and must require no special password or key for+unpacking, reading or copying.++  7. Additional Terms.++  "Additional permissions" are terms that supplement the terms of this+License by making exceptions from one or more of its conditions.+Additional permissions that are applicable to the entire Program shall+be treated as though they were included in this License, to the extent+that they are valid under applicable law.  If additional permissions+apply only to part of the Program, that part may be used separately+under those permissions, but the entire Program remains governed by+this License without regard to the additional permissions.++  When you convey a copy of a covered work, you may at your option+remove any additional permissions from that copy, or from any part of+it.  (Additional permissions may be written to require their own+removal in certain cases when you modify the work.)  You may place+additional permissions on material, added by you to a covered work,+for which you have or can give appropriate copyright permission.++  Notwithstanding any other provision of this License, for material you+add to a covered work, you may (if authorized by the copyright holders of+that material) supplement the terms of this License with terms:++    a) Disclaiming warranty or limiting liability differently from the+    terms of sections 15 and 16 of this License; or++    b) Requiring preservation of specified reasonable legal notices or+    author attributions in that material or in the Appropriate Legal+    Notices displayed by works containing it; or++    c) Prohibiting misrepresentation of the origin of that material, or+    requiring that modified versions of such material be marked in+    reasonable ways as different from the original version; or++    d) Limiting the use for publicity purposes of names of licensors or+    authors of the material; or++    e) Declining to grant rights under trademark law for use of some+    trade names, trademarks, or service marks; or++    f) Requiring indemnification of licensors and authors of that+    material by anyone who conveys the material (or modified versions of+    it) with contractual assumptions of liability to the recipient, for+    any liability that these contractual assumptions directly impose on+    those licensors and authors.++  All other non-permissive additional terms are considered "further+restrictions" within the meaning of section 10.  If the Program as you+received it, or any part of it, contains a notice stating that it is+governed by this License along with a term that is a further+restriction, you may remove that term.  If a license document contains+a further restriction but permits relicensing or conveying under this+License, you may add to a covered work material governed by the terms+of that license document, provided that the further restriction does+not survive such relicensing or conveying.++  If you add terms to a covered work in accord with this section, you+must place, in the relevant source files, a statement of the+additional terms that apply to those files, or a notice indicating+where to find the applicable terms.++  Additional terms, permissive or non-permissive, may be stated in the+form of a separately written license, or stated as exceptions;+the above requirements apply either way.++  8. Termination.++  You may not propagate or modify a covered work except as expressly+provided under this License.  Any attempt otherwise to propagate or+modify it is void, and will automatically terminate your rights under+this License (including any patent licenses granted under the third+paragraph of section 11).++  However, if you cease all violation of this License, then your+license from a particular copyright holder is reinstated (a)+provisionally, unless and until the copyright holder explicitly and+finally terminates your license, and (b) permanently, if the copyright+holder fails to notify you of the violation by some reasonable means+prior to 60 days after the cessation.++  Moreover, your license from a particular copyright holder is+reinstated permanently if the copyright holder notifies you of the+violation by some reasonable means, this is the first time you have+received notice of violation of this License (for any work) from that+copyright holder, and you cure the violation prior to 30 days after+your receipt of the notice.++  Termination of your rights under this section does not terminate the+licenses of parties who have received copies or rights from you under+this License.  If your rights have been terminated and not permanently+reinstated, you do not qualify to receive new licenses for the same+material under section 10.++  9. Acceptance Not Required for Having Copies.++  You are not required to accept this License in order to receive or+run a copy of the Program.  Ancillary propagation of a covered work+occurring solely as a consequence of using peer-to-peer transmission+to receive a copy likewise does not require acceptance.  However,+nothing other than this License grants you permission to propagate or+modify any covered work.  These actions infringe copyright if you do+not accept this License.  Therefore, by modifying or propagating a+covered work, you indicate your acceptance of this License to do so.++  10. Automatic Licensing of Downstream Recipients.++  Each time you convey a covered work, the recipient automatically+receives a license from the original licensors, to run, modify and+propagate that work, subject to this License.  You are not responsible+for enforcing compliance by third parties with this License.++  An "entity transaction" is a transaction transferring control of an+organization, or substantially all assets of one, or subdividing an+organization, or merging organizations.  If propagation of a covered+work results from an entity transaction, each party to that+transaction who receives a copy of the work also receives whatever+licenses to the work the party's predecessor in interest had or could+give under the previous paragraph, plus a right to possession of the+Corresponding Source of the work from the predecessor in interest, if+the predecessor has it or can get it with reasonable efforts.++  You may not impose any further restrictions on the exercise of the+rights granted or affirmed under this License.  For example, you may+not impose a license fee, royalty, or other charge for exercise of+rights granted under this License, and you may not initiate litigation+(including a cross-claim or counterclaim in a lawsuit) alleging that+any patent claim is infringed by making, using, selling, offering for+sale, or importing the Program or any portion of it.++  11. Patents.++  A "contributor" is a copyright holder who authorizes use under this+License of the Program or a work on which the Program is based.  The+work thus licensed is called the contributor's "contributor version".++  A contributor's "essential patent claims" are all patent claims+owned or controlled by the contributor, whether already acquired or+hereafter acquired, that would be infringed by some manner, permitted+by this License, of making, using, or selling its contributor version,+but do not include claims that would be infringed only as a+consequence of further modification of the contributor version.  For+purposes of this definition, "control" includes the right to grant+patent sublicenses in a manner consistent with the requirements of+this License.++  Each contributor grants you a non-exclusive, worldwide, royalty-free+patent license under the contributor's essential patent claims, to+make, use, sell, offer for sale, import and otherwise run, modify and+propagate the contents of its contributor version.++  In the following three paragraphs, a "patent license" is any express+agreement or commitment, however denominated, not to enforce a patent+(such as an express permission to practice a patent or covenant not to+sue for patent infringement).  To "grant" such a patent license to a+party means to make such an agreement or commitment not to enforce a+patent against the party.++  If you convey a covered work, knowingly relying on a patent license,+and the Corresponding Source of the work is not available for anyone+to copy, free of charge and under the terms of this License, through a+publicly available network server or other readily accessible means,+then you must either (1) cause the Corresponding Source to be so+available, or (2) arrange to deprive yourself of the benefit of the+patent license for this particular work, or (3) arrange, in a manner+consistent with the requirements of this License, to extend the patent+license to downstream recipients.  "Knowingly relying" means you have+actual knowledge that, but for the patent license, your conveying the+covered work in a country, or your recipient's use of the covered work+in a country, would infringe one or more identifiable patents in that+country that you have reason to believe are valid.++  If, pursuant to or in connection with a single transaction or+arrangement, you convey, or propagate by procuring conveyance of, a+covered work, and grant a patent license to some of the parties+receiving the covered work authorizing them to use, propagate, modify+or convey a specific copy of the covered work, then the patent license+you grant is automatically extended to all recipients of the covered+work and works based on it.++  A patent license is "discriminatory" if it does not include within+the scope of its coverage, prohibits the exercise of, or is+conditioned on the non-exercise of one or more of the rights that are+specifically granted under this License.  You may not convey a covered+work if you are a party to an arrangement with a third party that is+in the business of distributing software, under which you make payment+to the third party based on the extent of your activity of conveying+the work, and under which the third party grants, to any of the+parties who would receive the covered work from you, a discriminatory+patent license (a) in connection with copies of the covered work+conveyed by you (or copies made from those copies), or (b) primarily+for and in connection with specific products or compilations that+contain the covered work, unless you entered into that arrangement,+or that patent license was granted, prior to 28 March 2007.++  Nothing in this License shall be construed as excluding or limiting+any implied license or other defenses to infringement that may+otherwise be available to you under applicable patent law.++  12. No Surrender of Others' Freedom.++  If 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 convey a+covered work so as to satisfy simultaneously your obligations under this+License and any other pertinent obligations, then as a consequence you may+not convey it at all.  For example, if you agree to terms that obligate you+to collect a royalty for further conveying from those to whom you convey+the Program, the only way you could satisfy both those terms and this+License would be to refrain entirely from conveying the Program.++  13. Remote Network Interaction; Use with the GNU General Public License.++  Notwithstanding any other provision of this License, if you modify the+Program, your modified version must prominently offer all users+interacting with it remotely through a computer network (if your version+supports such interaction) an opportunity to receive the Corresponding+Source of your version by providing access to the Corresponding Source+from a network server at no charge, through some standard or customary+means of facilitating copying of software.  This Corresponding Source+shall include the Corresponding Source for any work covered by version 3+of the GNU General Public License that is incorporated pursuant to the+following paragraph.++  Notwithstanding any other provision of this License, you have+permission to link or combine any covered work with a work licensed+under version 3 of the GNU General Public License into a single+combined work, and to convey the resulting work.  The terms of this+License will continue to apply to the part which is the covered work,+but the work with which it is combined will remain governed by version+3 of the GNU General Public License.++  14. Revised Versions of this License.++  The Free Software Foundation may publish revised and/or new versions of+the GNU Affero 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 that a certain numbered version of the GNU Affero General+Public License "or any later version" applies to it, you have the+option of following the terms and conditions either of that numbered+version or of any later version published by the Free Software+Foundation.  If the Program does not specify a version number of the+GNU Affero General Public License, you may choose any version ever published+by the Free Software Foundation.++  If the Program specifies that a proxy can decide which future+versions of the GNU Affero General Public License can be used, that proxy's+public statement of acceptance of a version permanently authorizes you+to choose that version for the Program.++  Later license versions may give you additional or different+permissions.  However, no additional obligations are imposed on any+author or copyright holder as a result of your choosing to follow a+later version.++  15. Disclaimer of Warranty.++  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.++  16. Limitation of Liability.++  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS+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.++  17. Interpretation of Sections 15 and 16.++  If the disclaimer of warranty and limitation of liability provided+above cannot be given local legal effect according to their terms,+reviewing courts shall apply local law that most closely approximates+an absolute waiver of all civil liability in connection with the+Program, unless a warranty or assumption of liability accompanies a+copy of the Program in return for a fee.++                     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+state the exclusion of warranty; and each file should have at least+the "copyright" line and a pointer to where the full notice is found.++    <one line to give the program's name and a brief idea of what it does.>+    Copyright (C) <year>  <name of author>++    This program is free software: you can redistribute it and/or modify+    it under the terms of the GNU Affero General Public License as published by+    the Free Software Foundation, either version 3 of the License, or+    (at your option) any later version.++    This program is distributed in the hope that it will be useful,+    but WITHOUT ANY WARRANTY; without even the implied warranty of+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+    GNU Affero General Public License for more details.++    You should have received a copy of the GNU Affero General Public License+    along with this program.  If not, see <http://www.gnu.org/licenses/>.++Also add information on how to contact you by electronic and paper mail.++  If your software can interact with users remotely through a computer+network, you should also make sure that it provides a way for users to+get its source.  For example, if your program is a web application, its+interface could display a "Source" link that leads users to an archive+of the code.  There are many ways you could offer source, and different+solutions will be better for different programs; see section 13 for the+specific requirements.++  You should also get your employer (if you work as a programmer) or school,+if any, to sign a "copyright disclaimer" for the program, if necessary.+For more information on this, and how to apply and follow the GNU AGPL, see+<http://www.gnu.org/licenses/>.
+ Readme.html view
@@ -0,0 +1,189 @@+<p><i>Note: If you are viewing this Readme on GitHub, its Blogdown-specific features will not render correctly.+The Blogdown-formatted output is in Readme.html.</i></p>+<h1>Blogdown</h1>+<p>Blogdown is a markup language based on Markdown, designed for writing blog posts.+Blogdown's goals are:</p>+<ul><li>Clean syntax for common blog post features absent from Markdown.</li>+<li>Ability to handle untrusted input.</li>+<li>Simple syntax, with no surprises.</li>+<li>Debuggability.</li>+<li>Near compatibility with common Markdown implementations.</li>+</ul>+<p>Because there is no Markdown standard and existing Markdown implementations+<a href="http://johnmacfarlane.net/babelmark2/?text=Hello+world%0A*+this+is+a+list%0A%3E+this+is+a+quote">disagree wildly even on simple cases</a>,+Blogdown cannot be 100% compatible with even a majority of Markdown implementations.+While there have been attemps to create a common Markdown standard&mdash;most notably <a href="http://commonmark.org/">CommonMark</a>&mdash;they+are necessarily quite complex. The primary cause of this complexity is that Markdown insists on rendering <i>something</i> for every input,+no matter how malformed. Blogdown is considerably simpler, and hopefully easier for authors to debug, because it fails on malformed inputs.+With full compatability out of the window, I have chosen to make some other small improvements on Markdown syntax.</p>+<h2>Installation</h2>+<h3>With Cabal or Stack</h3>+<p>The recommended way to install any Haskell project is using <a href="https://www.haskell.org/cabal/">Cabal</a> or <a href="https://docs.haskellstack.org/en/stable/README/">Stack</a>.+With these tools, you can simply run <code>cabal install</code> or <code>stack build</code> respectively.</p>+<h3>Without Haskell Tooling</h3>+<p>Because configuring Cabal and Stack can be tricky for Haskell beginners, Blogdown supports installation without any Haskell tooling.</p>+<p>First, install <a href="https://www.haskell.org/ghc/">GHC</a>, <a href="https://hackage.haskell.org/package/parsec">Parsec</a> and <a href="https://hackage.haskell.org/package/MissingH">MissingH</a>,+all of which are available through common Linux package managers.+Then run <code>ghc -isrc -o Blogdown src/Blogdown.hs</code> in the repository's base directory.</p>+<h2>Usage</h2>+<p>The <code>Blogdown</code> binary reads from <code>stdin</code> and writes to <code>stdout</code>. Typical usage looks like:</p>+<pre><code>cat blogpost.md | ./Blogdown &gt; blogpost.html+</code></pre>+<h3>Optional Styling and Scripts</h3>+<p>It is recommended to include <code>footnotes.css</code> and <code>footnotes.js</code> on any pages which make use of Blogdown-generated footnotes,+which improve the appearance of footnotes and allow them to be shown inline.+These can be inlined using the <code>--inline-css</code> and <code>--inline-js</code> flags respectively<sup><a href="#-footnote-0" id="a--footnote-0">[0]</a></sup>.</p>+<h3>Optional Flags</h3>+<p><code>Blogdown</code> accepts the following long-style flags:</p>+<ul><li><code>--footnote-prefix</code>: Defines a prefix for the <code>id</code>s of footnotes. Recommended if multiple output files are included in a single HTML page, to avoid <code>id</code> collisions.</li>+<li><code>--footnote-index-from</code>: The index from which footnotes are numbered. Defaults to 0.</li>+<li><code>--footnote-backlinks</code>: If this flag is passed, footnotes will be preceded by a caret linking back to the point where the footnote is referenced.</li>+<li><code>--em-dashes</code>: If this flag is passed, <code>--</code> will be replaced with "&mdash;" in text.</li>+<li><code>--inline-css</code>: If this flag is passed, the recommended CSS will be inlined at the end of the output document.</li>+<li><code>--inline-js</code>: If this flag is passed, the recommended JS will be inlined at the end of the output document.</li>+</ul>+<h2>Syntax</h2>+<h3>Differences from Markdown</h3>+<p>Most of the syntax of Blogdown should be familiar to Markdown users, but some new syntax has been added, and some existing syntax has changed.</p>+<h4>New Features</h4>+<p>Blogdown adds footnote support to Markdown.+Footnotes can be referenced inline with ^[<i>footnote-name</i>], which will render as a superscript link to a <i>footnote-definition</i>+at the end of the document, which is defined by ~[<i>footnote-name</i>] followed by the footnote contents.</p>+<h4>Markdown Incompatibilities</h4>+<p>Blogdown does not support the Markdown syntax of underlining text with <code>=</code> or <code>-</code> characters to define a header,+as this comes at a large cost in the parser implementation<sup><a href="#-footnote-1" id="a--footnote-1">[1]</a></sup>.+The <code>#</code> syntax for headers is supported instead.</p>+<p>It also does not support using multiple trailing spaces to force a breakpoint at the end of a line.+The <code>&lt;br/&gt;</code> tag is supported instead.</p>+<p>The <code>~</code> and <code>^</code> characters are now special, and must be escaped to be used in text.+Additionally, while most Markdown implementations do not require escaping many special characters when their special meaning would+not be valid, Blogdown always requires they be escaped.</p>+<h3>Formal Description</h3>+<p>The body of a Blogdown document consists of a sequence of <i>block nodes</i>, which in turn consist of <i>inline nodes</i>.</p>+<h4>Block Nodes</h4>+<p>Block nodes can contain any sequence of inline nodes, with the exception of code and HTML blocks, whose contents are rendered verbatim.+Block nodes can span multiple lines and are terminated by a blank line, the beginning of another type of block, or the end of the document.</p>+<p>The following block node types are supported:</p>+<ul><li><b>Paragraph</b>: The default block type; any content not in another block is part of a paragraph.+Paragraphs must be separated by a blank line.+Can contain arbitrary inline nodes.</li>+<li><b>Header</b>: 1-6 <code>#</code> characters at the beginning of a line begins a header, with the number of <code>#</code> characters determining the header level.+Can contain arbitrary inline nodes.</li>+<li><b>Ordered Lists</b>: A <code> - </code> begins an ordered list item, which itself is a block.+Sequential ordered list items form an ordered list.+Can contain arbitrary inline nodes.</li>+<li><b>Unordered Lists</b>: A <code> * </code> begins an unordered list item, which itself is a block.+Sequential unordered list items form an unordered list.+Can contain arbitrary inline nodes.</li>+<li><b>Blockquote</b>: Lines beginning with <code>&gt; </code> define a blockquote.+Can contain arbitrary inline nodes.+Note that the first line not beginning with <code>&gt; </code> will start a new block.</li>+<li><b>Code Block</b>: Lines indented with 4+ spaces or a tab define a code block.+Code blocks are rendered verbatim, ignoring special characters.+Note that the first un-indented line will start a new block.</li>+<li><b>HTML Block</b>: An HTML tag at the beginning of a line starts an HTML block.+Its contents must be valid HTML, and it is ended by the corresponding closing tag.+HTML blocks are rendered verbatim, unless HTML bleaching is enabled.</li>+<li><b>Hard Rule</b>: A line consisting of 3+ <code>-</code> defines a hard rule.</li>+<li><b>Table</b>: A <code>|</code>character at the beginning of a line begins a table row, consisting of table cells separated by <code>|</code> characters.+The cells are themselves blocks, and as such can contain newlines. The rows are terminated by a <code>|</code> followed by a newline.+By default the table has only a body, but if rows are separated by an alternating string of <code>+</code> and multiple <code>-</code> characters,+e.g. <code>+---+---+</code>, then every row above the separator will be in the header and every row below will be in the body.+Optionally the table may start and end with such a separator as well.</li>+</ul>+<h4>Inline Nodes</h4>+<p>Inline nodes can generally contain a sequence of other inline nodes, but cannot contain nodes of the same type.+Despite the name, inline nodes can span multiple lines, e.g. to accomodate line length limits.</p>+<p>The following inline node types are supported:</p>+<ul><li><b>Plaintext</b>: The default inline type; any text not in another inline node is plaintext. Rendered verbatim.</li>+<li><b>Italic</b>: Surrounding text with <code>*</code> <i>italicizes</i> it. Italic nodes can contain any other type of inline node.</li>+<li><b>Bold</b>: Surrounding text with <code>**</code> <b>bolds</b> it. Bold nodes can contain any other type of inline node.</li>+<li><b>Code</b>: Surrounding text with <code>`</code> renders it as <code>code</code>. The content is rendered verbatim.</li>+<li><b>Link</b>: A <a href="#">link</a> is written as [<i>text</i>](<i>href</i>). The <i>text</i> portion can contain any other type of inline node.+The <i>href</i> portion is the link destination, and is parsed verbatim except that any literal <code>(</code> or <code>)</code> must be escaped.</li>+<li><b>Footnote Reference</b>: Writing ^[<i>footnote-name</i>] defines a footnote reference.+It is rendered as a superscript footnote number<sup><a href="#-footnote-2" id="a--footnote-2">[2]</a></sup>, and links to the footnote named <i>footnote-name</i>+if it is present in the footer.</li>+</ul>+<h4>Footer</h4>+<p>A Blogdown document may optionally include a footer after the body.+The footer consists of a sequence of <i>footnote definitions</i>, each of which begins on a new line with ~[<i>footnote-name</i>] and consists of an arbitrary sequence of blocks.+A footnote definition is only terminated by another footnote definition or the end of the document.</p>+<h3>Escaping</h3>+<p>Any character (special or not) can be escaped with <code>\</code>. For a literal backslash, use <code>\\</code>.+A backslash before a newline acts as a continuation character.</p>+<h2>Planned improvements</h2>+<ul><li>Nested lists</li>+<li>Better error messages on parse failures</li>+<li>Windows support</li>+<li>Better tests</li>+<li>Document building & running tests</li>+<li>Comments</li>+</ul>+<hr/>+<ol start="0" class="footnotes"><li id="-footnote-0"><p>Inlining CSS and JS is not recommended if you will be rendering multiple Blogdown documents on a single page, e.g. multiple blog posts on a blog.+Doing so will degrade network and browser performance slightly.</p></li>+<li id="-footnote-1"><p>Supporting underlines for headers requires the parser to look-ahead arbitrarily far, resulting in quadratic time complexity.</p></li>+<li id="-footnote-2"><p>Footnotes are auto-numbered in order of appearance, starting from 0 by default (this can be changed by passing the <code>--footnote-index-from</code> flag).</p></li>+</ol>+<style>.footnotes > li {+    margin: 15px 0;+}++/* Keeps the back-link on the same line as the first block. */+.footnotes > li > *:nth-child(2) {+    display: inline;+}++.inline-footnote {+    margin: .5em;+    padding: .5em;+    border-left: 2px solid #555;+}++.inline-footnote > p {+    margin: 0;+}+</style>+<script>function inlineFootnote(footnote) {+    var ref = document.getElementById("a-" + footnote.id);+    var inline = document.createElement("div");+    for (var i=0; i<footnote.children.length;) {+        var child = footnote.children[i];+        if (child.tagName == "A" && child.getAttribute("href") == "#" + ref.id) {+            i++;+            continue;+        }+        inline.appendChild(child);+    }+    inline.className = "inline-footnote";+    inline.style.display = "none";+    ref.parentNode.parentNode.insertBefore(inline, ref.parentNode.nextSibling);+    ref.origIndex = ref.textContent;+    ref.expanded = false;+    ref.addEventListener("click", function (e) {+        e.preventDefault();+        if (ref.expanded) {+            inline.style.display = "none";+            ref.textContent = ref.origIndex;+        } else {+            inline.style.display = "block";+            ref.textContent = "X";+        }+        ref.expanded = !ref.expanded;+    });+}++function inlineFootnotes() {+    var footnoteLists = document.getElementsByClassName("footnotes");+    while (footnoteLists.length > 0) {+        var footnotes = footnoteLists[0].children;+        for (var j=0; j<footnotes.length; j++) {+            inlineFootnote(footnotes[j]);+        }+        footnoteLists[0].remove();+    }+}++inlineFootnotes()+</script>
+ Readme.md view
@@ -0,0 +1,163 @@+*Note: If you are viewing this Readme on GitHub, its Blogdown-specific features will not render correctly.+The Blogdown-formatted output is in Readme.html.*++# Blogdown++Blogdown is a markup language based on Markdown, designed for writing blog posts.+Blogdown's goals are:+ * Clean syntax for common blog post features absent from Markdown.+ * Ability to handle untrusted input.+ * Simple syntax, with no surprises.+ * Debuggability.+ * Near compatibility with common Markdown implementations.++Because there is no Markdown standard and existing Markdown implementations+[disagree wildly even on simple cases](http://johnmacfarlane.net/babelmark2/?text=Hello+world%0A*+this+is+a+list%0A%3E+this+is+a+quote),+Blogdown cannot be 100% compatible with even a majority of Markdown implementations.+While there have been attemps to create a common Markdown standard--most notably [CommonMark](http://commonmark.org/)--they+are necessarily quite complex. The primary cause of this complexity is that Markdown insists on rendering *something* for every input,+no matter how malformed. Blogdown is considerably simpler, and hopefully easier for authors to debug, because it fails on malformed inputs.+With full compatability out of the window, I have chosen to make some other small improvements on Markdown syntax.++## Installation++### With Cabal or Stack++The recommended way to install any Haskell project is using [Cabal](https://www.haskell.org/cabal/) or [Stack](https://docs.haskellstack.org/en/stable/README/).+With these tools, you can simply run `cabal install` or `stack build` respectively.++### Without Haskell Tooling++Because configuring Cabal and Stack can be tricky for Haskell beginners, Blogdown supports installation without any Haskell tooling.++First, install [GHC](https://www.haskell.org/ghc/), [Parsec](https://hackage.haskell.org/package/parsec) and [MissingH](https://hackage.haskell.org/package/MissingH),+all of which are available through common Linux package managers.+Then run `ghc -isrc -o Blogdown src/Blogdown.hs` in the repository's base directory.++## Usage++The `Blogdown` binary reads from `stdin` and writes to `stdout`. Typical usage looks like:++    cat blogpost.md | ./Blogdown > blogpost.html++### Optional Styling and Scripts++It is recommended to include `footnotes.css` and `footnotes.js` on any pages which make use of Blogdown-generated footnotes,+which improve the appearance of footnotes and allow them to be shown inline.+These can be inlined using the `--inline-css` and `--inline-js` flags respectively^[inline].++### Optional Flags++`Blogdown` accepts the following long-style flags:+ * `--footnote-prefix`: Defines a prefix for the `id`s of footnotes. Recommended if multiple output files are included in a single HTML page, to avoid `id` collisions.+ * `--footnote-index-from`: The index from which footnotes are numbered. Defaults to 0.+ * `--footnote-backlinks`: If this flag is passed, footnotes will be preceded by a caret linking back to the point where the footnote is referenced.+ * `--em-dashes`: If this flag is passed, `--` will be replaced with "&mdash;" in text.+ * `--inline-css`: If this flag is passed, the recommended CSS will be inlined at the end of the output document.+ * `--inline-js`: If this flag is passed, the recommended JS will be inlined at the end of the output document.++## Syntax++### Differences from Markdown++Most of the syntax of Blogdown should be familiar to Markdown users, but some new syntax has been added, and some existing syntax has changed.++#### New Features++Blogdown adds footnote support to Markdown.+Footnotes can be referenced inline with \^\[*footnote-name*\], which will render as a superscript link to a *footnote-definition*+at the end of the document, which is defined by \~\[*footnote-name*\] followed by the footnote contents.++#### Markdown Incompatibilities++Blogdown does not support the Markdown syntax of underlining text with `=` or `-` characters to define a header,+as this comes at a large cost in the parser implementation^[underline-parser-complexity].+The `#` syntax for headers is supported instead.++It also does not support using multiple trailing spaces to force a breakpoint at the end of a line.+The `<br/>` tag is supported instead.++The `\~` and `\^` characters are now special, and must be escaped to be used in text.+Additionally, while most Markdown implementations do not require escaping many special characters when their special meaning would+not be valid, Blogdown always requires they be escaped.++### Formal Description++The body of a Blogdown document consists of a sequence of *block nodes*, which in turn consist of *inline nodes*.++#### Block Nodes++Block nodes can contain any sequence of inline nodes, with the exception of code and HTML blocks, whose contents are rendered verbatim.+Block nodes can span multiple lines and are terminated by a blank line, the beginning of another type of block, or the end of the document.++The following block node types are supported:+ * **Paragraph**: The default block type; any content not in another block is part of a paragraph.+Paragraphs must be separated by a blank line.+Can contain arbitrary inline nodes.+ * **Header**: 1-6 `#` characters at the beginning of a line begins a header, with the number of `#` characters determining the header level.+Can contain arbitrary inline nodes.+ * **Ordered Lists**: A ` - ` begins an ordered list item, which itself is a block.+Sequential ordered list items form an ordered list.+Can contain arbitrary inline nodes.+ * **Unordered Lists**: A ` * ` begins an unordered list item, which itself is a block.+Sequential unordered list items form an unordered list.+Can contain arbitrary inline nodes.+ * **Blockquote**: Lines beginning with `> ` define a blockquote.+Can contain arbitrary inline nodes.+Note that the first line not beginning with `> ` will start a new block.+ * **Code Block**: Lines indented with 4+ spaces or a tab define a code block.+Code blocks are rendered verbatim, ignoring special characters.+Note that the first un-indented line will start a new block.+ * **HTML Block**: An HTML tag at the beginning of a line starts an HTML block.+Its contents must be valid HTML, and it is ended by the corresponding closing tag.+HTML blocks are rendered verbatim, unless HTML bleaching is enabled.+ * **Hard Rule**: A line consisting of 3+ `-` defines a hard rule.+ * **Table**: A `|`character at the beginning of a line begins a table row, consisting of table cells separated by `|` characters.+The cells are themselves blocks, and as such can contain newlines. The rows are terminated by a `|` followed by a newline.+By default the table has only a body, but if rows are separated by an alternating string of `+` and multiple `-` characters,+e.g. `+---+---+`, then every row above the separator will be in the header and every row below will be in the body.+Optionally the table may start and end with such a separator as well.++#### Inline Nodes++Inline nodes can generally contain a sequence of other inline nodes, but cannot contain nodes of the same type.+Despite the name, inline nodes can span multiple lines, e.g. to accomodate line length limits.++The following inline node types are supported:+ * **Plaintext**: The default inline type; any text not in another inline node is plaintext. Rendered verbatim.+ * **Italic**: Surrounding text with `*` *italicizes* it. Italic nodes can contain any other type of inline node.+ * **Bold**: Surrounding text with `**` **bolds** it. Bold nodes can contain any other type of inline node.+ * **Code**: Surrounding text with `\`` renders it as `code`. The content is rendered verbatim.+ * **Link**: A [link](#) is written as \[*text*\]\(*href*\). The *text* portion can contain any other type of inline node.+The *href* portion is the link destination, and is parsed verbatim except that any literal `(` or `)` must be escaped.+ * **Footnote Reference**: Writing \^\[*footnote-name*\] defines a footnote reference.+It is rendered as a superscript footnote number^[footnote-numbering], and links to the footnote named *footnote-name*+if it is present in the footer.++#### Footer++A Blogdown document may optionally include a footer after the body.+The footer consists of a sequence of *footnote definitions*, each of which begins on a new line with \~\[*footnote-name*\] and consists of an arbitrary sequence of blocks.+A footnote definition is only terminated by another footnote definition or the end of the document.++### Escaping++Any character (special or not) can be escaped with `\\`. For a literal backslash, use `\\\\`.+A backslash before a newline acts as a continuation character.++## Planned improvements+ * Nested lists+ * Better error messages on parse failures+ * Windows support+ * Better tests+ * Document building & running tests+ * Comments++---++~[inline] Inlining CSS and JS is not recommended if you will be rendering multiple Blogdown documents on a single page, e.g. multiple blog posts on a blog.+Doing so will degrade network and browser performance slightly.++~[underline-parser-complexity] Supporting underlines for headers requires the parser to look-ahead arbitrarily far, resulting in quadratic time complexity.++~[footnote-numbering] Footnotes are auto-numbered in order of appearance, starting from 0 by default (this can be changed by passing the `--footnote-index-from` flag).
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ assets/footnotes.css view
@@ -0,0 +1,18 @@+.footnotes > li {+    margin: 15px 0;+}++/* Keeps the back-link on the same line as the first block. */+.footnotes > li > *:nth-child(2) {+    display: inline;+}++.inline-footnote {+    margin: .5em;+    padding: .5em;+    border-left: 2px solid #555;+}++.inline-footnote > p {+    margin: 0;+}
+ assets/footnotes.js view
@@ -0,0 +1,41 @@+function inlineFootnote(footnote) {+    var ref = document.getElementById("a-" + footnote.id);+    var inline = document.createElement("div");+    for (var i=0; i<footnote.children.length;) {+        var child = footnote.children[i];+        if (child.tagName == "A" && child.getAttribute("href") == "#" + ref.id) {+            i++;+            continue;+        }+        inline.appendChild(child);+    }+    inline.className = "inline-footnote";+    inline.style.display = "none";+    ref.parentNode.parentNode.insertBefore(inline, ref.parentNode.nextSibling);+    ref.origIndex = ref.textContent;+    ref.expanded = false;+    ref.addEventListener("click", function (e) {+        e.preventDefault();+        if (ref.expanded) {+            inline.style.display = "none";+            ref.textContent = ref.origIndex;+        } else {+            inline.style.display = "block";+            ref.textContent = "X";+        }+        ref.expanded = !ref.expanded;+    });+}++function inlineFootnotes() {+    var footnoteLists = document.getElementsByClassName("footnotes");+    while (footnoteLists.length > 0) {+        var footnotes = footnoteLists[0].children;+        for (var j=0; j<footnotes.length; j++) {+            inlineFootnote(footnotes[j]);+        }+        footnoteLists[0].remove();+    }+}++inlineFootnotes()
+ src/AST.hs view
@@ -0,0 +1,65 @@+module AST where++data AST = AST [Block] (Maybe FootnoteDefs)+    deriving (Show)++data FootnoteDefs = FootnoteDefs [FootnoteDef]+    deriving (Show)++data FootnoteDef = FootnoteDef {+    index :: Int,+    content :: [Block]+}+    deriving (Show)++data ListItem = ListItem Bool [Inline]+    deriving (Show)++data TableCell = TableHeaderCell [Inline]+               | TableBodyCell [Inline]+    deriving (Show)++data TableRow = TableRow [TableCell]+    deriving (Show)++data Block = HardRule+           | Paragraph [Inline]+           | Header Int [Inline]+           | OrderedList [ListItem]+           | UnorderedList [ListItem]+           | BlockQuote [Inline]+           | BlockCode String+           | BlockHtml Html+           | Table {+    thead :: Maybe [TableRow],+    body :: [TableRow]+}+    deriving (Show)++data Inline = Italics [Inline]+            | Bold [Inline]+            | Code String+            | FootnoteRef Int+            | Plaintext String+            | InlineHtml Html+            | Link {+    text :: [Inline],+    href :: String+}+    deriving (Show)++data HtmlTagType = Open | Close | SelfClosing+    deriving (Show, Eq)++data HtmlTag = HtmlTag {+    tagname :: String,+    attrs :: [Attr]+}+    deriving (Show)++data Attr = Attr String String+    deriving (Show)++data Html = PairTag HtmlTag [Either String Html]+          | SingleTag HtmlTag+    deriving (Show)
+ src/Blogdown.hs view
@@ -0,0 +1,11 @@+module Main where++import Parsing.Parse+import Rendering.Render+import Rendering.RenderOptions++main :: IO ()+main = do+    renderOptions <- getRenderOptions+    input <- getContents+    either (putStrLn . show) (putStr . toHtml renderOptions) $ parse input
+ src/Parsing/Parse.hs view
@@ -0,0 +1,18 @@+module Parsing.Parse where++import Text.Parsec hiding (parse)++import AST+import Parsing.State+import Parsing.ParseBlock+import Parsing.ParseFootnotes++ast :: Parser AST+ast = do+    blocks <- many block+    footnotes <- optionMaybe footnoteDefs+    eof+    return $ AST blocks footnotes++parse :: String -> Either ParseError AST+parse = runParser ast initialState ""
+ src/Parsing/ParseBlock.hs view
@@ -0,0 +1,83 @@+module Parsing.ParseBlock where++import Text.Parsec++import AST+import Parsing.State+import Parsing.TextUtils+import Parsing.Utils+import Parsing.ParseHtml+import Parsing.ParseInline++hardRule :: Parser Block+hardRule = do+    try (string "---") <?> "\"---\" (hard rule)"+    many (char '-')+    many1 (char '\n')+    return HardRule++paragraph :: Parser Block+paragraph = fmap Paragraph $ many1 inline++header :: Parser Block+header = do+    hashes <- (many1 $ char' '#') <?> "\"#\" (header)"+    many1 $ oneOf " \t"+    text <- many1 inline+    return $ Header (length hashes) text++listItem :: Bool -> Parser ListItem+listItem ordered = fmap (ListItem ordered) $ do+    let identifier = if ordered then " - " else " * "+    (try $ string' identifier) <?> ("\"" ++ identifier ++ "\" (list item)")+    many1 $ inline++orderedList :: Parser Block+orderedList = fmap OrderedList $ many1 $ listItem True++unorderedList :: Parser Block+unorderedList = fmap UnorderedList $ many1 $ listItem False++blockQuoteLineStart :: Parser String+blockQuoteLineStart = try (string "> ") <?> "\"> \" (blockquote)"++blockQuote :: Parser Block+blockQuote = fmap BlockQuote $ do+    blockQuoteLineStart+    withModifiedState (many1 inline) $ \s -> s {prevCharIsNewline=False, skipPrefix=(blockQuoteLineStart >> many (char ' '))}++blockCodeLineStart :: Parser String+blockCodeLineStart = try (string "\t" <|> string "    ") <?> "\"    \" or tab (code block)"++blockCode :: Parser Block+blockCode = fmap (BlockCode . unlines) $ many1 $ blockCodeLineStart >> manyTill (noneOf "\n") (char' '\n')++blockHtml :: Parser Block+blockHtml = fmap BlockHtml html++-- Does not return TableRow because we don't know what type the cells are until the whole table is parsed.+tableRow :: Parser [[Inline]]+tableRow = manyTill ((char' '|' <?> "\"|\" (table cell)") >> many1 inline) (try $ string' "|\n")++tableSeparator :: Parser ()+tableSeparator = optional $ do+    sepBy1 (char '+' <?> "\"+\" (table)") (optionMaybe (char ' ') >> many1 (char '-') >> optionMaybe (char ' '))+    char' '\n'++table :: Parser Block+table = do+    tableSeparator+    rows <- many1 $ tableRow+    tableSeparator+    rows2 <- many $ tableRow+    let headerRows = if null rows2+        then Nothing+        else Just $ map (TableRow . map TableHeaderCell) rows+    let bodyRows = map (TableRow . map TableBodyCell) $ if null rows2 then rows else rows2+    if null rows2+        then return ()+        else tableSeparator+    return $ Table headerRows bodyRows++block :: Parser Block+block = (many $ char '\n') >> choice [blockHtml, hardRule, header, orderedList, unorderedList, blockQuote, table, blockCode, paragraph]
+ src/Parsing/ParseFootnotes.hs view
@@ -0,0 +1,27 @@+module Parsing.ParseFootnotes where++import Data.Maybe+import Text.Parsec+import qualified Data.Map.Strict as M++import AST+import Parsing.State+import Parsing.TextUtils+import Parsing.ParseBlock++footnoteDef :: Parser FootnoteDef+footnoteDef = do+    many $ char '\n'+    identifier <- betweenWithErrors' "~[" "]" "footnote definition" $ many1 $ escapableNoneOf "[]"+    state <- getState+    let maybeIndex = M.lookup identifier $ footnoteIndices state+    index <- if isNothing maybeIndex+        then fail $ "unreferenced footnote identifier: " ++ identifier+        else return $ fromJust maybeIndex+    many1 $ oneOf " \t"+    modifyState (\s -> s {prevCharIsNewline=False})+    content <- many1 $ try block+    return $ FootnoteDef index content++footnoteDefs :: Parser FootnoteDefs+footnoteDefs = fmap FootnoteDefs $ many1 footnoteDef
+ src/Parsing/ParseHtml.hs view
@@ -0,0 +1,51 @@+module Parsing.ParseHtml (html) where++import Text.Parsec++import AST+import Parsing.State+import Parsing.Utils++htmlTag :: HtmlTagType -> Parser HtmlTag+htmlTag tagType = do+    if tagType == Close+        then try (string "</") <?> "\"</\" (closing html tag)"+        else string "<" <?> "\"<\" (html tag)"+    spaces+    tagname <- many1 (letter <?> "rest of tag name") <?> "html tag name"+    attrs <- many attr+    if tagType == SelfClosing+        then try (string "/>") <?> "closing \"/>\" (self-closing html tag)"+        else string ">" <?> "closing \">\" (html tag)"+    return $ HtmlTag {tagname=tagname, attrs=attrs}++htmlContent :: Parser (Either String Html)+htmlContent = fmap Left (many1 $ noneOf "<") <|> fmap Right html++pairTag :: Parser Html+pairTag = do+    open <- htmlTag Open+    content <- option [] $ many $ try htmlContent+    close <- htmlTag Close+    if tagname open /= tagname close+        then fail $ "mismatched tags: '" ++ tagname open ++ "' and '" ++ tagname close ++ "'"+        else return $ PairTag open content++singleTag :: Parser Html+singleTag = fmap SingleTag $ htmlTag SelfClosing++-- TODO: consider eliminating arbitrary-length backtracking on [singleTag]+-- by limiting tag name length and checking against self-closing tag names.+html :: Parser Html+html = try singleTag <|> pairTag++attr :: Parser Attr+attr = do+    space >> spaces+    name <- many1 letter <?> "html attribute name"+    char '='+    val <- attrVal+    return $ Attr name val++attrVal :: Parser String+attrVal = betweenWithErrors "\"" "\"" "html attribute value" (many $ noneOf "\"")
+ src/Parsing/ParseInline.hs view
@@ -0,0 +1,71 @@+module Parsing.ParseInline (inline) where++import Data.Maybe+import Text.Parsec+import qualified Data.Map.Strict as M++import AST+import Parsing.State+import Parsing.Text+import Parsing.TextUtils+import Parsing.Utils+import Parsing.ParseHtml++nestedBold :: Parser Inline+nestedBold = suppressErr (try (string "****")) >> fail "cannot have empty or nested bold nodes"++bold :: Parser Inline+bold = do+    state <- getState+    let currentParserStack = inlineParserStack state+    failIf $ elem "bold" currentParserStack+    s <- betweenWithErrors' "**" "**" "bold"+        $ withModifiedState (many1 inline) $ \s -> s {inlineParserStack=("bold" : currentParserStack)}+    return $ Bold s++-- The bold and italics parsers are tricky because they both use the same special character.+italics :: Parser Inline+italics = do+    state <- getState+    let currentParserStack = inlineParserStack state+    failIf $ elem "italics" currentParserStack+    s <- between+        (try ((char' '*' <?> "\"*\" (italics)") >> suppressErr (notFollowedBy (char '*'))))+        (char' '*' <?> "closing \"*\" (italics)")+        ((withModifiedState (many1 inline) $ \s -> s {inlineParserStack=("italics" : currentParserStack)}) <?>+            if elem "bold" currentParserStack+                then "content in italics node or extra \"*\" to close bold node"+                else "content in italics node")+    return $ Italics s++code :: Parser Inline+code = fmap Code $ betweenWithErrors' "`" "`" "code" $ many1 $ escapableNoneOf "`"++footnoteRef :: Parser Inline+footnoteRef = do+    identifier <- betweenWithErrors' "^[" "]" "footnote reference" $ many1 $ escapableNoneOf "[]"+    state <- getState+    let f = footnoteIndices state+    let maybeIndex = M.lookup identifier f+    if isJust maybeIndex+        then fail $ "repeated footnote identifier: " ++ (show $ fromJust maybeIndex)+        else return ()+    let index = M.size f+    let f' = M.insert identifier index f+    putState $ state {footnoteIndices=f'}+    return $ FootnoteRef index++link :: Parser Inline+link = do+    state <- getState+    let currentParserStack = inlineParserStack state+    lookAhead (char '[' <?> "\"[\" (link text)")+    if elem "link" currentParserStack+        then fail "links cannot be nested"+        else return ()+    text <- betweenWithErrors' "[" "]" "link text" $ withModifiedState (many1 inline) $ \s -> s {inlineParserStack=("link" : currentParserStack)}+    href <- betweenWithErrors' "(" ")" "link href" $ many $ escapableNoneOf "()"+    return $ Link {text=text, href=href}++inline :: Parser Inline+inline = choice [nestedBold, bold, italics, code, footnoteRef, link, fmap InlineHtml html, fmap Plaintext plaintext]
+ src/Parsing/State.hs view
@@ -0,0 +1,19 @@+module Parsing.State where++import Text.Parsec+import qualified Data.Map.Strict as M++data ParserState = ParserState {+    prevCharIsNewline :: Bool,+    skipPrefix :: Parser String,+    inlineParserStack :: [String],+    footnoteIndices :: M.Map String Int+}+type Parser = Parsec String ParserState++initialState = ParserState{+    prevCharIsNewline=False,+    skipPrefix=string "",+    inlineParserStack=[],+    footnoteIndices=M.fromList []+}
+ src/Parsing/Text.hs view
@@ -0,0 +1,49 @@+module Parsing.Text (plaintext) where++import Data.Maybe+import Text.Parsec++import Parsing.State+import Parsing.Utils++specials = "*`^<>[]|\\"+firstCharSpecials = " \t#~+\n" ++ specials++-- Parse a single non-special character, allowing for escaping and continuation.+nonSpecial :: String -> Parser Char+nonSpecial blacklist = do+    escape <- suppressErr (optionMaybe $ char '\\')+    if isJust escape+        then do+            optional $ char '\n'    -- continuation+            anyChar+        else noneOf blacklist++-- Parse one or more non-special characters, allowing for escaping and incorporating the+-- rules for special characters at the start of a line. Note that this does not consume+-- as many non-special characters as possible, for ease of implementation.+nonSpecials :: Parser String+nonSpecials = do+    state <- getState+    str <- if prevCharIsNewline state+        then do+            s <- skipPrefix state+            if null s+                then do+                    c <- nonSpecial firstCharSpecials+                    return [c]+                else return s+        else do+            s <- many $ nonSpecial ('\n' : specials)+            c <- suppressErr (optionMaybe $ char '\n')+            if isJust c+                then return (s ++ "\n")+                else if null s+                    then fail ""    -- Don't succeed without consuming any input.+                    else return s+    putState $ state {prevCharIsNewline=(last str == '\n')}+    return str++-- Parse as many non-special characters as possible.+plaintext :: Parser String+plaintext = fmap concat $ many1 nonSpecials
+ src/Parsing/TextUtils.hs view
@@ -0,0 +1,40 @@+module Parsing.TextUtils where++import Data.Maybe+import Text.Parsec++import Parsing.State+import Parsing.Utils++-- Parse a single character and update parsing state accordingly.+char' :: Char -> Parser Char+char' c = if c == '\n'+    then do+        newline <|> (eof >> return '\n')+        modifyState $ \s -> s {prevCharIsNewline=True}+        return '\n'+    else do+        char c+        modifyState $ \s -> s {prevCharIsNewline=False}+        return c++-- Parse a string of characters and update parsing state accordingly.+string' :: String -> Parser String+string' = mapM char'++-- Parse a string of characters not in [blacklist], unless they are escaped,+-- and update state like [string'].+escapableNoneOf :: String -> Parser Char+escapableNoneOf blacklist = do+    escape <- suppressErr (optionMaybe $ char '\\')+    c <- if isJust escape+        then anyChar+        else noneOf blacklist+    modifyState $ \s -> s {prevCharIsNewline=(c == '\n')}+    return c++-- Like [betweenWithErrors], but updates state when parsing the start and end strings.+betweenWithErrors' :: String -> String -> String -> Parser a -> Parser a+betweenWithErrors' open close name = between+    (try (string' open) <?> "\"" ++ open ++ "\" (" ++ name ++ ")")+    (try (string' close) <?> "closing \"" ++ close ++ "\" (" ++ name ++ ")")
+ src/Parsing/Utils.hs view
@@ -0,0 +1,26 @@+module Parsing.Utils where++import Text.Parsec++suppressErr :: Parsec s u a -> Parsec s u a+suppressErr p = p <?> ""++failWithIf :: String -> Bool -> Parsec s u ()+failWithIf msg cond = if cond then fail msg else return ()++failIf :: Bool -> Parsec s u ()+failIf = failWithIf ""++withModifiedState :: Parsec s u a -> (u -> u) -> Parsec s u a+withModifiedState p modifier = do+    state <- getState+    putState $ modifier state+    result <- p+    putState state+    return result++-- Like [between], but with more helpful error messages on failure.+betweenWithErrors :: String -> String -> String -> Parsec String u a -> Parsec String u a+betweenWithErrors open close name = between+    (try (string open) <?> "\"" ++ open ++ "\" (" ++ name ++ ")")+    (try (string close) <?> "closing \"" ++ close ++ "\" (" ++ name ++ ")")
+ src/Rendering/Render.hs view
@@ -0,0 +1,132 @@+{-# Language CPP #-}++module Rendering.Render (ToHtml, toHtml) where++import Data.List+import Data.List.Utils+import System.IO.Unsafe++import AST+#ifdef CABAL+import Paths_Blogdown+#endif+import Rendering.RenderOptions++class ToHtml a where+    toHtml :: RenderOptions -> a -> String++dataFileContents :: FilePath -> String+dataFileContents relPath = unsafePerformIO $ do+#ifdef CABAL+    newPath <- getDataFileName relPath+    readFile newPath+#else+    readFile relPath+#endif++optionalJS :: RenderOptions -> String+optionalJS r = if (inlineJS r)+    then "<script>" ++ dataFileContents "assets/footnotes.js" ++ "</script>\n"+    else ""++optionalCSS :: RenderOptions -> String+optionalCSS r = if (inlineCSS r)+    then "<style>" ++ dataFileContents "assets/footnotes.css" ++ "</style>\n"+    else ""++instance ToHtml AST where+    toHtml r (AST bs Nothing) = (unlines $ map (toHtml r) bs) ++ optionalCSS r ++ optionalJS r+    toHtml r (AST bs (Just f)) = body ++ footnotes ++ "\n" ++ optionalCSS r ++ optionalJS r where+        body = unlines $ map (toHtml r) bs+        footnotes = toHtml r f++withTag :: String -> String -> String+withTag tag content = "<" ++ tag ++ ">" ++ content ++ "</" ++ tag ++ ">"++showAttrs :: [(String, String)] -> String+showAttrs attrs = unwords $ map (\(name, val) -> name ++ "=\"" ++ val ++ "\"") attrs++withTagAttrs :: String -> [(String, String)] -> String -> String+withTagAttrs tag attrs content = "<" ++ tag ++ " " ++ showAttrs attrs ++ ">" ++ content ++ "</" ++ tag ++ ">"++fancyUnlines :: [String] -> String+fancyUnlines = concat . intersperse "\n"++escapeHtml :: String -> String+escapeHtml = concatMap escapeChar where+    escapeChar '<' = "&lt;"+    escapeChar '>' = "&gt;"+    escapeChar c = [c]++instance ToHtml FootnoteDefs where+    toHtml r (FootnoteDefs fs) = withTagAttrs "ol"+        [("start", show $ footnoteIndexFrom r), ("class", "footnotes")]+        $ unlines $ map (toHtml r) $ sortOn index fs++instance ToHtml FootnoteDef where+    toHtml r (FootnoteDef index ls) = withTagAttrs "li" [("id", (footnotePrefix r) ++ "-footnote-" ++ show index)] content' where+        content = fancyUnlines $ map (toHtml r) ls+        content' = if footnoteBacklinks r+            then withTagAttrs "a" [("href", "#a-" ++ (footnotePrefix r) ++ "-footnote-" ++ show index)] "^" ++ "\n" ++ content+            else content++stripEndingNewline :: String -> String+stripEndingNewline s = if last s == '\n'+    then init s+    else s++instance ToHtml Block where+    toHtml _ HardRule = "<hr/>"+    toHtml r (Paragraph ls) = withTag "p" $ stripEndingNewline $ concatMap (toHtml r) ls+    toHtml r (Header level text) = withTag ("h" ++ show level) $ stripEndingNewline $ concatMap (toHtml r) text+    toHtml r (OrderedList ls) = withTag "ol" $ unlines $ map (toHtml r) ls+    toHtml r (UnorderedList ls) = withTag "ul" $ unlines $ map (toHtml r) ls+    toHtml r (BlockQuote ls) = withTag "blockquote" $ stripEndingNewline $ concatMap (toHtml r) ls+    toHtml _ (BlockCode s) = withTag "pre" $ withTag "code" $ escapeHtml s+    toHtml r (BlockHtml h) = toHtml r h+    toHtml r (Table Nothing trs) = withTag "table" $ withTag "tbody" body where+        body = unlines $ map (toHtml r) trs+    toHtml r (Table (Just ths) trs) = withTag "table" $ (withTag "thead" header) ++ (withTag "tbody" body) where+        header = unlines $ map (toHtml r) ths+        body = unlines $ map (toHtml r) trs++instance ToHtml ListItem where+    toHtml r (ListItem _ ls) = withTag "li" $ stripEndingNewline $ concatMap (toHtml r) ls++instance ToHtml TableCell where+    toHtml r (TableHeaderCell tds) = withTag "th" $ concatMap (toHtml r) tds+    toHtml r (TableBodyCell tds) = withTag "td" $ concatMap (toHtml r) tds++instance ToHtml TableRow where+    toHtml r (TableRow tcs) = withTag "tr" $ unlines $ map (toHtml r) tcs++instance ToHtml Inline where+    toHtml r (Italics ls) = withTag "i" $ concatMap (toHtml r) ls+    toHtml r (Bold ls) = withTag "b" $ concatMap (toHtml r) ls+    toHtml _ (Code s) = withTag "code" $ escapeHtml s+    toHtml r (FootnoteRef index) = withTag "sup" $ withTagAttrs "a"+        [("href", "#" ++ (footnotePrefix r) ++ "-footnote-" ++ show index),+         ("id", "a-" ++ (footnotePrefix r) ++ "-footnote-" ++ show index)]+        ("[" ++ show (index + footnoteIndexFrom r) ++ "]")+    toHtml r (Plaintext s) = escapeHtml $ if emDashes r+        then replace "--" "&mdash;" s+        else s+    toHtml r (InlineHtml h) = toHtml r h+    toHtml r (Link text href) = withTagAttrs "a" [("href", href)] $ concatMap (toHtml r) text++showAttr :: Attr -> String+showAttr (Attr s t) = s ++ "=\"" ++ t ++ "\""++showInnerTag :: HtmlTag -> String+showInnerTag t = unwords (tagname t : (map showAttr $ attrs t))++showHtmlContent :: Either String Html -> String+showHtmlContent (Left s) = s+showHtmlContent (Right h) = showHtml h++showHtml :: Html -> String+showHtml (PairTag open content) = "<" ++ showInnerTag open ++ ">" ++ concatMap showHtmlContent content ++ "</" ++ tagname open ++ ">"+showHtml (SingleTag tag) = "<" ++ showInnerTag tag ++ "/>"++instance ToHtml Html where+    toHtml _ = showHtml
+ src/Rendering/RenderOptions.hs view
@@ -0,0 +1,37 @@+{-# LANGUAGE DeriveGeneric #-}++module Rendering.RenderOptions where++import GHC.Generics (Generic)+import System.Environment++data RenderOptions = RenderOptions {+    footnotePrefix :: String,+    footnoteIndexFrom :: Int,+    footnoteBacklinks :: Bool,+    emDashes :: Bool,+    inlineCSS :: Bool,+    inlineJS :: Bool+} deriving (Generic)++defaultRenderOptions = RenderOptions {+    footnotePrefix = "",+    footnoteIndexFrom = 0,+    footnoteBacklinks = False,+    emDashes = False,+    inlineCSS = False,+    inlineJS = False+}++renderOptions :: [String] -> RenderOptions+renderOptions [] = defaultRenderOptions+renderOptions (key : ls) = case key of+    "--footnote-prefix" -> (renderOptions $ tail ls) {footnotePrefix=head ls}+    "--footnote-index-from" -> (renderOptions $ tail ls) {footnoteIndexFrom=read $ head ls}+    "--footnote-backlinks" -> (renderOptions ls) {footnoteBacklinks=True}+    "--em-dashes" -> (renderOptions ls) {emDashes=True}+    "--inline-css" -> (renderOptions ls) {inlineCSS=True}+    "--inline-js" -> (renderOptions ls) {inlineJS=True}++getRenderOptions :: IO RenderOptions+getRenderOptions = fmap renderOptions getArgs
+ test/Test.hs view
@@ -0,0 +1,327 @@+module Main where++import Data.String.Utils+import Text.Parsec+import System.Exit++import Parsing.Parse+import Parsing.ParseBlock+import Parsing.ParseHtml+import Parsing.ParseInline+import Parsing.State+import Rendering.Render+import Rendering.RenderOptions++printExpectedSuccess :: (ToHtml a) => String -> String -> String -> a -> IO Bool+printExpectedSuccess name input expected parsed = if output == expected+    then do+        putStrLn $ "PASS: " ++ name+        return True+    else do+        putStrLn $ "FAIL: " ++ name+        putStrLn "in:"+        putStrLn input+        putStrLn "out:"+        putStrLn output+        putStrLn "expect:"+        putStrLn expected+        return False+    where+        output = toHtml defaultRenderOptions parsed++expectSuccess :: (ToHtml a) => String -> (Parser a) -> String -> String -> IO Bool+expectSuccess name p input expected = either+    (\err -> do+        putStrLn $ "FAIL: " ++ name+        putStrLn $ show err+        return False)+    (printExpectedSuccess name input expected)+    $ runParser p initialState name input++testItalics = expectSuccess "italics" inline "*abc*" "<i>abc</i>"+testBold = expectSuccess "bold" inline "**abc**" "<b>abc</b>"+testBoldItalics = expectSuccess "bold italics" inline "***abc***" "<b><i>abc</i></b>"+testCode = expectSuccess "code" inline "`abc`" "<code>abc</code>"+testInlineHtml = expectSuccess "inline html" html+    "<abbr title=\"\">SQL</abbr>"+    "<abbr title=\"\">SQL</abbr>"+testMultipleAttrs = expectSuccess "html with multiple attrs" html+    "<abbr title=\"a\" id=\"x\">SQL</abbr>"+    "<abbr title=\"a\" id=\"x\">SQL</abbr>"+testFootnoteRef = expectSuccess "footnote reference" inline+    "^[x]"+    "<sup><a href=\"#-footnote-0\" id=\"a--footnote-0\">[0]</a></sup>"+testLink = expectSuccess "link" inline+    "[Google](https://google.com)"+    "<a href=\"https://google.com\">Google</a>"+testLinkWithContents = expectSuccess "link with styling inside" inline+    "[*Whence* `he` **came**](https://google.com)"+    "<a href=\"https://google.com\"><i>Whence</i> <code>he</code> <b>came</b></a>"+testH1 = expectSuccess "h1" header "# hello" "<h1>hello</h1>"+testH6 = expectSuccess "h6" header "###### hello" "<h6>hello</h6>"+testHardRule = expectSuccess "hard rule" hardRule "---\n" "<hr/>"+testHardRuleLong = expectSuccess "hard rule with extra dashes" hardRule "----\n" "<hr/>"+testParagraph = expectSuccess "paragraph" paragraph+    "This is a paragraph\n\+    \of text.\n"+    "<p>This is a paragraph\n\+    \of text.</p>"+testEscapeCharacters = expectSuccess "paragraph with escaped special characters" paragraph+    "These are special: \\~, \\*, \\[, \\], \\^\n"+    "<p>These are special: ~, *, [, ], ^</p>"+testHashInParagraph = expectSuccess "paragraph containing literal '#'" paragraph+    "This is a paragraph\n\+    \containing '#'\n"+    "<p>This is a paragraph\n\+    \containing '#'</p>"+testOrderedList = expectSuccess "ol" orderedList+    " - point 1\n\+    \ - point 2\n"+    "<ol><li>point 1</li>\n\+    \<li>point 2</li>\n</ol>"+testUnorderedList = expectSuccess "ul" unorderedList+    " * point 1\n\+    \ * point 2\n"+    "<ul><li>point 1</li>\n\+    \<li>point 2</li>\n</ul>"+testBlockQuote = expectSuccess "blockquote" blockQuote+    "> The politician said that\n\+    \> he would fix the economy.\n"+    "<blockquote>The politician said that\n\+    \he would fix the economy.</blockquote>"+testBlockQuotePreFormatted = expectSuccess "blockquote pre-formatted" blockQuote+    ">     \"This is it... this is where I belong...\"\n\+    \>     I know everyone here... even if I've never met them, never talked to\n\+    \> them, may never hear from them again... I know you all...\n"+    "<blockquote>    \"This is it... this is where I belong...\"\n\+    \    I know everyone here... even if I've never met them, never talked to\n\+    \them, may never hear from them again... I know you all...</blockquote>"+testBlockCode = expectSuccess "block code" blockCode+    "    var x = 0;\n\+    \    alert(x);\n"+    "<pre><code>var x = 0;\n\+    \alert(x);\n\+    \</code></pre>"+testBlockCodeWhitespace = expectSuccess "block code handles starting whitespace correctly" blockCode+    "    def f(x):\n\+    \        return x\n"+    "<pre><code>def f(x):\n\+    \    return x\n\+    \</code></pre>"+testBlockCodeSpecialChars = expectSuccess "any line beginning with four spaces should be a block of code, regardless of the first non-whitespace character" block+    "    > print(1)\n\+    \    * 1"+    "<pre><code>&gt; print(1)\n\+    \* 1\n\+    \</code></pre>"+testBlockHtml = expectSuccess "block html" blockHtml+    "<div class=\"class\">\n\+    \    <span></span>\n\+    \</div>"+    "<div class=\"class\">\n\+    \    <span></span>\n\+    \</div>"+testTable = expectSuccess "table" table+    "+---+---+\n\+    \| a | b |\n\+    \| c | d |\n\+    \+---+---+\n"+    "<table><tbody><tr><td> a </td>\n\+    \<td> b </td>\n\+    \</tr>\n\+    \<tr><td> c </td>\n\+    \<td> d </td>\n\+    \</tr>\n\+    \</tbody></table>"+testTableHeader = expectSuccess "table with header" table+    "+---+---+\n\+    \| a | b |\n\+    \+---+---+\n\+    \| c | d |\n\+    \| e | f |\n\+    \+---+---+\n"+    "<table><thead><tr><th> a </th>\n\+    \<th> b </th>\n\+    \</tr>\n\+    \</thead><tbody><tr><td> c </td>\n\+    \<td> d </td>\n\+    \</tr>\n\+    \<tr><td> e </td>\n\+    \<td> f </td>\n\+    \</tr>\n\+    \</tbody></table>"+testTableMinimal = expectSuccess "minimal table" table+    "| a | b |\n\+    \| c | d |\n"+    "<table><tbody><tr><td> a </td>\n\+    \<td> b </td>\n\+    \</tr>\n\+    \<tr><td> c </td>\n\+    \<td> d </td>\n\+    \</tr>\n\+    \</tbody></table>"+testTableHeaderMinimal = expectSuccess "minimal table with header" table+    "| a | b |\n\+    \+\n\+    \| c | d |\n\+    \| e | f |\n"+    "<table><thead><tr><th> a </th>\n\+    \<th> b </th>\n\+    \</tr>\n\+    \</thead><tbody><tr><td> c </td>\n\+    \<td> d </td>\n\+    \</tr>\n\+    \<tr><td> e </td>\n\+    \<td> f </td>\n\+    \</tr>\n\+    \</tbody></table>"+testFootnoteDef = expectSuccess "footnote definition" ast+    "^[x]\n\+    \~[x] This is a single list item\n\+    \of footnote.\n"+    "<p><sup><a href=\"#-footnote-0\" id=\"a--footnote-0\">[0]</a></sup></p>\n\+    \<ol start=\"0\" class=\"footnotes\"><li id=\"-footnote-0\"><p>This is a single list item\n\+    \of footnote.</p></li>\n\+    \</ol>\n"+testFootnoteDefs = expectSuccess "multiple footnote definitions" ast+    "^[0]^[1]^[2]\n\+    \~[0] a\n\+    \~[1] b\n\+    \\n\+    \~[2] c\n"+    "<p><sup><a href=\"#-footnote-0\" id=\"a--footnote-0\">[0]</a></sup><sup><a href=\"#-footnote-1\" id=\"a--footnote-1\">[1]</a></sup><sup><a href=\"#-footnote-2\" id=\"a--footnote-2\">[2]</a></sup></p>\n\+    \<ol start=\"0\" class=\"footnotes\"><li id=\"-footnote-0\"><p>a</p></li>\n\+    \<li id=\"-footnote-1\"><p>b</p></li>\n\+    \<li id=\"-footnote-2\"><p>c</p></li>\n\+    \</ol>\n"+testFootnoteOrdering = expectSuccess "footnotes should be sorted by first reference" ast+    "^[0]^[1]\n\+    \~[1] b\n\+    \~[0] a\n"+    "<p><sup><a href=\"#-footnote-0\" id=\"a--footnote-0\">[0]</a></sup><sup><a href=\"#-footnote-1\" id=\"a--footnote-1\">[1]</a></sup></p>\n\+    \<ol start=\"0\" class=\"footnotes\"><li id=\"-footnote-0\"><p>a</p></li>\n\+    \<li id=\"-footnote-1\"><p>b</p></li>\n\+    \</ol>\n"+testContinuation = expectSuccess "continuation character" paragraph+    "a\\\nb"+    "<p>ab</p>"++expectFailure :: String -> (Parser a) -> String -> String -> IO Bool+expectFailure name p input expectedErr = either+    (\err -> if endswith expectedErr $ show err+        then do+            putStrLn $ "PASS: " ++ name+            return True+        else do+            putStrLn $ "FAIL: " ++ name+            putStrLn "in:"+            putStrLn input+            putStrLn "out:"+            putStrLn $ show err+            putStrLn "expect:"+            putStrLn expectedErr+            return False)+    (\_ -> do+        putStrLn $ "FAIL: " ++ name+        return False)+    $ runParser p initialState name input++testNestedBold = expectFailure "bold tags cannot be nested" inline+    "****abc****"+    "cannot have empty or nested bold nodes"+testMismatchedBoldItalics = expectFailure "bold opening tag closed with italics tag" inline+    "**a*"+    "expecting content in italics node or extra \"*\" to close bold node"+testSwappedItalicsBold = expectFailure "italics and bold closing tags swapped" paragraph+    "*a**b*c**"+    "unexpected \"c\"\n\+    \expecting closing \"**\" (bold)"+testNestedLink = expectFailure "links cannot be nested" inline+    "[[a](https://a.com)](https://b.com)"+    "unexpected \"[\"\n\+    \expecting \"**\" (bold), \"*\" (italics), \"`\" (code), \"^[\" (footnote reference) or \"<\" (html tag)\n\+    \links cannot be nested"+testUnclosedOpeningTag = expectFailure "unclosed opening tag should fail to parse" html+    "<div"+    "unexpected end of input\n\+    \expecting rest of tag name, space or closing \">\" (html tag)"+testUnclosedTag = expectFailure "unclosed tag should fail to parse" html+    "<p>hello"+    "unexpected end of input\n\+    \expecting \"<\" (html tag) or \"</\" (closing html tag)"+testMismatchedTags = expectFailure "mismatched tags should fail to parse" html+    "<a>hello</b>"+    "mismatched tags: 'a' and 'b'"++goldenTest :: FilePath -> FilePath -> String -> IO Bool+goldenTest inFilePath goldenFilePath renderArgs = do+    let r = renderOptions $ words renderArgs+    input <- readFile inFilePath+    golden <- readFile goldenFilePath+    either+        (\_ -> do+            putStrLn $ "FAIL: " ++ goldenFilePath+            return False)+        (\parsed -> do+            let rendered = toHtml r parsed+            if rendered == golden+                then do+                    putStrLn $ "PASS: " ++ goldenFilePath+                    return True+                else do+                    let failPath = goldenFilePath ++ ".fail"+                    writeFile failPath rendered+                    putStrLn $ "FAIL: " ++ goldenFilePath+                    return False)+        $ runParser ast initialState inFilePath input++main :: IO ()+main = do+    results <- sequence [+        testItalics,+        testBold,+        testBoldItalics,+        testCode,+        testInlineHtml,+        testMultipleAttrs,+        testFootnoteRef,+        testLink,+        testLinkWithContents,+        testH1,+        testH6,+        testHardRule,+        testHardRuleLong,+        testParagraph,+        testEscapeCharacters,+        testHashInParagraph,+        testOrderedList,+        testUnorderedList,+        testBlockQuote,+        testBlockQuotePreFormatted,+        testBlockCode,+        testBlockCodeWhitespace,+        testBlockCodeSpecialChars,+        testBlockHtml,+        testTable,+        testTableHeader,+        testTableMinimal,+        testTableHeaderMinimal,+        testFootnoteDef,+        testFootnoteDefs,+        testFootnoteOrdering,+        testContinuation,+        testNestedBold,+        testMismatchedBoldItalics,+        testSwappedItalicsBold,+        testNestedLink,+        testUnclosedOpeningTag,+        testUnclosedTag,+        testMismatchedTags,+        goldenTest "Readme.md" "Readme.html" "--em-dashes --inline-css --inline-js",+        goldenTest "test/goldens/golden1.md" "test/goldens/golden1.html" "--em-dashes",+        goldenTest "test/goldens/golden1.md" "test/goldens/golden1-backlinks.html" "--em-dashes --footnote-backlinks",+        goldenTest "test/goldens/golden2.md" "test/goldens/golden2.html" "--em-dashes",+        goldenTest "test/goldens/golden2.md" "test/goldens/golden2-backlinks.html" "--em-dashes --footnote-backlinks"]+    if and results+        then exitSuccess+        else exitFailure