[sagenb-export] 01/02: New upstream version 2.0

Ximin Luo infinity0 at debian.org
Sun Oct 16 17:26:59 UTC 2016


This is an automated email from the git hooks/post-receive script.

infinity0 pushed a commit to branch master
in repository sagenb-export.

commit bf4c9512c278c5b5d3a80479c81572cf43ece123
Author: Ximin Luo <infinity0 at debian.org>
Date:   Sun Oct 16 18:49:31 2016 +0200

    New upstream version 2.0
---
 .gitignore                                         |  10 +
 .travis.yml                                        |  11 +
 LICENSE                                            | 674 +++++++++++++++++++++
 MANIFEST.in                                        |   6 +
 Makefile                                           |  11 +
 README.md                                          |  57 ++
 sagenb_export/__init__.py                          |   0
 sagenb_export/actions.py                           |  29 +
 sagenb_export/cmdline.py                           |  68 +++
 sagenb_export/defaults.py                          |   7 +
 sagenb_export/ipynb_writer.py                      |  59 ++
 sagenb_export/logger.py                            |   5 +
 sagenb_export/nbextension/__init__.py              |  32 +
 sagenb_export/nbextension/asset_handler.py         |  15 +
 sagenb_export/nbextension/export_handler.py        |  62 ++
 sagenb_export/nbextension/jinja2_env.py            |  15 +
 sagenb_export/nbextension/list_handler.html        |  57 ++
 sagenb_export/nbextension/list_handler.py          |  33 +
 sagenb_export/nbextension/www/sagemath_icon.svg    | 369 +++++++++++
 sagenb_export/nbextension/www/sagenb-export.css    | 124 ++++
 sagenb_export/nbextension/www/sagenb-export.js     |  15 +
 sagenb_export/sagenb_reader.py                     | 178 ++++++
 sagenb_export/text_writer.py                       |  44 ++
 sagenb_export/unescape.py                          |   8 +
 setup.py                                           |  24 +
 test/__init__.py                                   |   0
 .../10/cells/1/sage0-size500-249051951.jmol.zip    | Bin 0 -> 2374 bytes
 .../212/2123/admin/10/cells/1/sage0-size500.jmol   |   2 +
 .../10/cells/12/sage0-size500-900865605.jmol.zip   | Bin 0 -> 1147 bytes
 .../212/2123/admin/10/cells/12/sage0-size500.jmol  |   2 +
 .../21/212/2123/admin/10/snapshots/1297626260.bz2  | Bin 0 -> 100 bytes
 .../21/212/2123/admin/10/snapshots/1297626396.bz2  | Bin 0 -> 162 bytes
 .../21/212/2123/admin/10/snapshots/1297626546.bz2  | Bin 0 -> 293 bytes
 .../21/212/2123/admin/10/snapshots/1297637968.bz2  | Bin 0 -> 603 bytes
 .../21/212/2123/admin/10/snapshots/1297637997.bz2  | Bin 0 -> 1194 bytes
 .../21/212/2123/admin/10/snapshots/1297648831.bz2  | Bin 0 -> 3281 bytes
 .../21/212/2123/admin/10/snapshots/1297678871.bz2  | Bin 0 -> 3313 bytes
 .../21/212/2123/admin/10/snapshots/1297680848.bz2  | Bin 0 -> 2677 bytes
 .../2/21/212/2123/admin/10/worksheet.html          | 233 +++++++
 .../2/21/212/2123/admin/10/worksheet_conf.pickle   |  56 ++
 .../__store__/2/21/212/2123/admin/4/worksheet.html |  26 +
 .../2/21/212/2123/admin/4/worksheet_conf.pickle    |  58 ++
 .../__store__/2/21/212/2123/admin/5/worksheet.html |   0
 .../2/21/212/2123/admin/5/worksheet_conf.pickle    |  66 ++
 .../9/9b/9bb/9bbe/_sage_/4/worksheet.html          | 282 +++++++++
 .../9/9b/9bb/9bbe/_sage_/4/worksheet_conf.pickle   |  61 ++
 test/test_sagenb_list.py                           |  28 +
 test/test_sagenb_reader.py                         |  68 +++
 test/test_sagenb_writer.py                         |  39 ++
 tox.ini                                            |  12 +
 50 files changed, 2846 insertions(+)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..7bba8d4
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,10 @@
+*~
+*.pyc
+.\#*
+\#*\#
+/.tox
+MANIFEST
+
+/build/
+/dist/
+/sagenb_export.egg-info/
\ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..f5e6610
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,11 @@
+language: python
+
+env:
+- TOXENV=py27
+- TOXENV=py34
+
+install:
+- pip install tox
+
+script:
+- tox
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..ef7e7ef
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,674 @@
+GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 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 General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is 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.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+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.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  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 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. Use with the GNU Affero General Public License.
+
+  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 Affero 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 special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU 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 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 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 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 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 General Public License for more details.
+
+    You should have received a copy of the GNU 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 the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    {project}  Copyright (C) {year}  {fullname}
+    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  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 GPL, see
+<http://www.gnu.org/licenses/>.
+
+  The GNU General Public License does not permit incorporating your program
+into proprietary programs.  If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.  But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 0000000..8c0e208
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1,6 @@
+include README* LICENSE*
+recursive-include sagenb_export *.py
+recursive-include sagenb_export/nbextension *.html
+recursive-include sagenb_export/nbextension/www *
+recursive-exclude sagenb_export /#*.py .\#*.py *~
+recursive-exclude test *
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..1f01b88
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,11 @@
+
+tox:
+	tox
+
+watch-%:
+	while true ; do \
+	    $(MAKE) $* ; \
+	    inotifywait -e close_write -r . --exclude '.*~' --exclude '#.*' ; \
+	done
+
+.PHONY: tox
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..0a13806
--- /dev/null
+++ b/README.md
@@ -0,0 +1,57 @@
+Convert SageNB Notebooks
+========================
+
+This is a tool to convert SageNB notebooks to other formats, in
+particular IPython/Jupyter notebooks.
+
+
+Install
+-------
+
+    pip install git+https://github.com/vbraun/ExportSageNB.git
+
+or
+
+    sage -pip install git+https://github.com/vbraun/ExportSageNB.git
+
+
+Usage
+-----
+
+First, you want to list the existing notebooks. Each notebook has a
+unique id and a not necessarily unique name:
+
+    $ sagenb-export --list
+    Unique ID       | Notebook Name
+    -------------------------------------------------------------------------------
+    admin:10        | Oxford Seminar (1,1)-Calabi Yau
+
+You can specify notebooks by the ID or by name; If the name is not
+unique, the first notebook found in the filesystem wins. To convert it
+to a Jupyter/IPython notebook, use the `--ipynb` switch as in
+
+    $ sagenb-export --ipynb=Output.ipynb admin:10
+
+You can then open the saved `Output.ipynb` via
+
+    $ sage --notebook=jupyter Output.ipynb
+
+
+Notes
+-----
+
+* Various output formats are not supported, e.g. no pictures. The
+  simplest solution is to re-evaluate.
+
+* SageNB html input cells are converted to Jupyter raw NBConvert
+  cells; In the interactive Jupyter notebook these are not rendered as
+  html but shown as their html source code. If you export to HTML
+  (File -> Download as -> HTML) they are rendered as html, though.
+
+
+Testing and Python Compatibility
+--------------------------------
+
+* The git-trac command supports Python 2.7, and 3.4+.
+* Most recent [Travis CI](https://travis-ci.org/vbraun/ExportSageNB) test:
+  [![Build Status](https://travis-ci.org/vbraun/ExportSageNB.svg?branch=master)](https://travis-ci.org/vbraun/ExportSageNB)
diff --git a/sagenb_export/__init__.py b/sagenb_export/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/sagenb_export/actions.py b/sagenb_export/actions.py
new file mode 100644
index 0000000..c78e5df
--- /dev/null
+++ b/sagenb_export/actions.py
@@ -0,0 +1,29 @@
+
+import sys
+from sagenb_export.logger import log
+from sagenb_export.sagenb_reader import NotebookSageNB
+
+
+def action_list(dot_sage):
+    def tr(unique_id, name):
+        print(u'{0:<15} | {1}'.format(unique_id, name))
+    tr('Unique ID', 'Notebook Name')
+    print('-' * 79)
+    notebooks = dict(
+        (notebook.sort_key, notebook)
+        for notebook in NotebookSageNB.all_iter(dot_sage)
+    )
+    for key in sorted(notebooks.keys()):
+        notebook = notebooks[key]
+        tr(notebook.unique_id, notebook.name)
+
+
+
+def action_print(sagenb):
+    from sagenb_export.text_writer import TextWriter
+    TextWriter(sagenb).write(sys.stdout)
+
+
+def action_convert_ipynb(sagenb, ipynb_filename):
+    from sagenb_export.ipynb_writer import IpynbWriter
+    IpynbWriter(sagenb).write(ipynb_filename)
diff --git a/sagenb_export/cmdline.py b/sagenb_export/cmdline.py
new file mode 100644
index 0000000..850e08b
--- /dev/null
+++ b/sagenb_export/cmdline.py
@@ -0,0 +1,68 @@
+## -*- encoding: utf-8 -*-
+"""
+Handle Command Line Options
+"""
+
+import os
+import sys
+import argparse
+
+from sagenb_export.defaults import DOT_SAGE
+from sagenb_export.logger import log
+from sagenb_export.sagenb_reader import NotebookSageNB
+from sagenb_export.actions import action_list, action_print, action_convert_ipynb
+
+
+description = \
+"""
+Export SageNB notebooks
+"""
+
+
+def make_parser():
+    parser = argparse.ArgumentParser(description=description)
+    parser.add_argument('--log', dest='log', default=None,
+                        help='one of [DEBUG, INFO, ERROR, WARNING, CRITICAL]')
+    parser.add_argument('--dot-sage', dest='dot_sage', default=DOT_SAGE,
+                        help='location of the .sage directory')
+    parser.add_argument('--list', dest='list', action='store_true',
+                        help='list all SageNB notebooks')
+    parser.add_argument('--ipynb', dest='ipynb', default=None,
+                        help='output .ipynb notebook filename')
+    parser.add_argument('--print', dest='print_text', action='store_true',
+                        help='print notebook')
+    parser.add_argument('sagenb', default=None, nargs='?',
+                        help='SageNB notebook name or unique id')
+    return parser
+
+        
+
+def main():
+    parser = make_parser()
+    args = parser.parse_args()
+    if args.log is not None:
+        import logging
+        level = getattr(logging, args.log)
+        log.setLevel(level=level)
+    dot_sage = os.path.expanduser(args.dot_sage)
+    if args.list:
+        action_list(dot_sage)
+
+    if not args.sagenb:
+        sys.exit(0)
+    sagenb = NotebookSageNB.find(dot_sage, args.sagenb)
+
+    if args.print_text:
+        action_print(sagenb)
+
+    if args.ipynb:
+        ipynb_name = args.ipynb.format(nb=sagenb)
+        if os.path.exists(ipynb_name):
+            raise RuntimeError('file exists: {0}'.format(ipynb_name))
+        action_convert_ipynb(sagenb, ipynb_name)
+    
+
+        
+        
+if __name__ == '__main__':
+    main()
diff --git a/sagenb_export/defaults.py b/sagenb_export/defaults.py
new file mode 100644
index 0000000..e6de613
--- /dev/null
+++ b/sagenb_export/defaults.py
@@ -0,0 +1,7 @@
+## -*- encoding: utf-8 -*-
+"""
+Default Values
+"""
+
+
+DOT_SAGE = '~/.sage'
diff --git a/sagenb_export/ipynb_writer.py b/sagenb_export/ipynb_writer.py
new file mode 100644
index 0000000..34a095c
--- /dev/null
+++ b/sagenb_export/ipynb_writer.py
@@ -0,0 +1,59 @@
+
+
+from nbformat import write
+from nbformat.v4 import (
+    new_code_cell, new_markdown_cell,
+    new_notebook,
+    new_output
+)
+from nbformat.v4.nbbase import new_raw_cell
+
+
+from sagenb_export.logger import log
+from sagenb_export.sagenb_reader import TextCell, ComputeCell
+
+
+class IpynbWriter(object):
+
+    def __init__(self, sagenb):
+        self.nb = sagenb
+
+        
+    @property
+    def cells(self):
+        for cell in self.nb.cells:
+            if isinstance(cell, TextCell):
+                yield new_markdown_cell(
+                    source=cell.input,
+                )
+            elif isinstance(cell, ComputeCell):
+                yield new_code_cell(
+                    source=cell.input,
+                    execution_count=cell.index,
+                    outputs=[
+                        new_output(
+                            output_type=u'execute_result',
+                            data={
+                                'text/plain': cell.output,
+                            },
+                            execution_count=cell.index,
+                        )
+                    ]
+                )
+            else:
+                log.critical('unknown cell: {0}'.format(cell))
+
+        
+    def write(self, filename):
+        ipynb = new_notebook(
+            cells=list(self.cells),
+            metadata=dict(
+                kernelspec=dict(
+                    display_name="SageMath",
+                    name="sagemath",
+                ),
+                language='python',
+            )
+        )
+        write(ipynb, filename)
+        
diff --git a/sagenb_export/logger.py b/sagenb_export/logger.py
new file mode 100644
index 0000000..89a232e
--- /dev/null
+++ b/sagenb_export/logger.py
@@ -0,0 +1,5 @@
+
+import logging
+logging.basicConfig()
+
+log = logging.getLogger('sagenb-export')
diff --git a/sagenb_export/nbextension/__init__.py b/sagenb_export/nbextension/__init__.py
new file mode 100644
index 0000000..69ea7f3
--- /dev/null
+++ b/sagenb_export/nbextension/__init__.py
@@ -0,0 +1,32 @@
+
+from tornado.web import StaticFileHandler
+
+from notebook.utils import url_path_join
+
+from sagenb_export.nbextension.list_handler import ListSageNBHandler
+from sagenb_export.nbextension.export_handler import ExportSageNBHandler
+from sagenb_export.nbextension.asset_handler import AssetHandler
+
+
+
+
+def load_jupyter_server_extension(nb_server_app):
+    """
+    Called when the extension is loaded.
+
+    Args:
+        nb_server_app (NotebookWebApplication): handle to the Notebook webserver instance.
+    """
+    web_app = nb_server_app.web_app
+    host_pattern = '.*$'
+    def url(path):
+        return url_path_join(web_app.settings['base_url'], path)
+    web_app.add_handlers(
+        host_pattern, [
+            (url(r'/sagenb'), ListSageNBHandler),
+            (url(r'/sagenb/export'), ExportSageNBHandler),
+            (url(r'/sagenb/www/(.*)'), AssetHandler),
+        ]
+    )
+
+
diff --git a/sagenb_export/nbextension/asset_handler.py b/sagenb_export/nbextension/asset_handler.py
new file mode 100644
index 0000000..3df77b0
--- /dev/null
+++ b/sagenb_export/nbextension/asset_handler.py
@@ -0,0 +1,15 @@
+
+import os
+from tornado.web import StaticFileHandler
+
+from notebook.base.handlers import FileFindHandler
+
+
+class AssetHandler(FileFindHandler):
+
+    def initialize(self, **kwds):
+        kwds['path'] = [
+            os.path.join(os.path.dirname(__file__), 'www'),
+        ]
+        super(AssetHandler, self).initialize(**kwds)
+        
diff --git a/sagenb_export/nbextension/export_handler.py b/sagenb_export/nbextension/export_handler.py
new file mode 100644
index 0000000..c202910
--- /dev/null
+++ b/sagenb_export/nbextension/export_handler.py
@@ -0,0 +1,62 @@
+import os
+import string
+
+from notebook.base.handlers import IPythonHandler
+ 
+from sagenb_export.defaults import DOT_SAGE
+from sagenb_export.logger import log
+from sagenb_export.sagenb_reader import NotebookSageNB
+from sagenb_export.ipynb_writer import IpynbWriter
+
+
+
+def filename_escape(name):
+    def escape(ch):
+        if ch in string.ascii_letters + string.digits:
+            return ch
+        else:
+            return '_'
+    return ''.join(map(escape, name))
+
+
+
+
+class ExportSageNBHandler(IPythonHandler):
+    """
+    Return a web page that lists the current SageNB worksheets
+    """
+
+    @property
+    def dot_sage(self):
+        return os.path.expanduser(DOT_SAGE)
+        
+    
+    def post(self):
+        print('POST', self.request, self.request.body)
+        ipynb_filename = self.safe_filename()
+        IpynbWriter(self.notebook()).write(ipynb_filename)
+        relative_url = '/notebooks/' + ipynb_filename
+        self.finish(relative_url)
+
+    @property
+    def unique_id(self):
+        return self.request.body
+        
+    def notebook(self):
+        try:
+            nb = self._notebook
+        except AttributeError:
+            nb = self._notebook = NotebookSageNB.find(self.dot_sage, self.unique_id)
+        return nb
+    
+    def safe_filename(self):
+        basename = filename_escape(self.notebook().name)
+        filename = '{0}.ipynb'.format(basename)
+        if not os.path.exists(filename):
+            return filename
+        i = 2
+        while True:
+            filename = '{0} ({1}).ipynb'.format(basename, i)
+            if not os.path.exists(filename):
+                return filename
+    
diff --git a/sagenb_export/nbextension/jinja2_env.py b/sagenb_export/nbextension/jinja2_env.py
new file mode 100644
index 0000000..bbfc265
--- /dev/null
+++ b/sagenb_export/nbextension/jinja2_env.py
@@ -0,0 +1,15 @@
+# -*- coding: utf-8 -*-
+"""
+Jinja2 Environment for Embedded Pages
+"""
+
+import os
+from jinja2 import Environment, FileSystemLoader
+
+
+jinja2_env = Environment(
+    loader=FileSystemLoader([
+        os.path.dirname(__file__)
+    ]),
+    autoescape=True,
+)
diff --git a/sagenb_export/nbextension/list_handler.html b/sagenb_export/nbextension/list_handler.html
new file mode 100644
index 0000000..ffdbe4e
--- /dev/null
+++ b/sagenb_export/nbextension/list_handler.html
@@ -0,0 +1,57 @@
+<html>
+    <head>
+        <title>SageMath</title>
+        <link rel="stylesheet" href="/sagenb/www/sagenb-export.css"/>
+        <script type="text/javascript" src="/sagenb/www/sagenb-export.js"></script>
+    </head>
+    <body>
+        
+        <div class="header">
+            <h1>Sage Mathematics Software</h1>
+            <img src="/sagenb/www/sagemath_icon.svg">
+        </div>
+            
+        <div class="warn">
+            <p>
+                <b>Note:</b>
+                The Sage notebook changed.
+            </p>
+            <a href="/tree">Take me to the new Sage/Jupyter notebook</a>
+        </div>
+
+        <div class="old">
+            <p>
+                To skip this screen and go directly to the new
+                notebook, run
+            </p>
+            <pre>sage --notebook=jupyter</pre>
+            <p>
+                To launch the old notebook instead, run
+            </p>
+            <pre>sage --notebook=sagenb</pre>
+            <p>
+                on the command line. Or click on one of your old
+                notebooks below to convert your old notebook to a new
+                Sage/Jupyter notebook.
+            </p>
+        </div>
+
+        <div class="error">
+        </div>
+        
+        <div class="nb-list">
+            <div class="nb-row nb-header">
+                <div class="nb-id">ID</div>
+                <div class="nb-name">Name</div>
+            </div>
+            {% for nb in notebooks %}
+            <div class="nb-row clickable" onclick="exportSageNB('{{nb.unique_id}}')">
+                <div class="nb-id"><span class="ellipsis">{{ nb.unique_id }}</span></div>
+                <div class="nb-name"><span class="ellipsis">{{ nb.name }}</span></div>
+            </div>
+            {% endfor %}
+        </div>
+        
+    </body>
+</html>
+
diff --git a/sagenb_export/nbextension/list_handler.py b/sagenb_export/nbextension/list_handler.py
new file mode 100644
index 0000000..eec6491
--- /dev/null
+++ b/sagenb_export/nbextension/list_handler.py
@@ -0,0 +1,33 @@
+import os
+
+from notebook.base.handlers import IPythonHandler
+ 
+from sagenb_export.defaults import DOT_SAGE
+from sagenb_export.logger import log
+from sagenb_export.sagenb_reader import NotebookSageNB
+from sagenb_export.nbextension.jinja2_env import jinja2_env
+
+
+
+class ListSageNBHandler(IPythonHandler):
+    """
+    Return a web page that lists the current SageNB worksheets
+    """
+    
+    def notebook_iter(self):
+        dot_sage = os.path.expanduser(DOT_SAGE)
+        notebooks = dict(
+            (notebook.sort_key, notebook)
+            for notebook in NotebookSageNB.all_iter(dot_sage)
+        )
+        for key in sorted(notebooks.keys()):
+            yield notebooks[key]
+    
+    def get(self):
+        template = jinja2_env.get_template('list_handler.html')
+        html = template.render(dict(
+            notebooks=tuple(self.notebook_iter()),
+        ))
+        self.finish(html)
+
+
diff --git a/sagenb_export/nbextension/www/sagemath_icon.svg b/sagenb_export/nbextension/www/sagemath_icon.svg
new file mode 100644
index 0000000..014e5e7
--- /dev/null
+++ b/sagenb_export/nbextension/www/sagemath_icon.svg
@@ -0,0 +1,369 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="400"
+   height="400"
+   id="svg2"
+   sodipodi:version="0.32"
+   inkscape:version="0.46"
+   sodipodi:docname="sagemath-icon.svg"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape"
+   version="1.0"
+   inkscape:export-filename="/users/schilly/Dokumente/sage/sagemath-icon-16.png"
+   inkscape:export-xdpi="3.5999999"
+   inkscape:export-ydpi="3.5999999">
+  <defs
+     id="defs4">
+    <inkscape:perspective
+       sodipodi:type="inkscape:persp3d"
+       inkscape:vp_x="0 : 526.18109 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_z="744.09448 : 526.18109 : 1"
+       inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+       id="perspective10" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     gridtolerance="10000"
+     guidetolerance="10"
+     objecttolerance="10"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="0.9899495"
+     inkscape:cx="206.64893"
+     inkscape:cy="205.58295"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     inkscape:window-width="1280"
+     inkscape:window-height="968"
+     inkscape:window-x="-5"
+     inkscape:window-y="-3" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1">
+    <rect
+       inkscape:export-ydpi="90"
+       inkscape:export-xdpi="90"
+       style="fill:#1919bf;fill-opacity:0.94117647;stroke:none;stroke-width:2;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       id="rect5084"
+       width="400"
+       height="399.99997"
+       x="0"
+       y="1.5258789e-05"
+       rx="37.939529"
+       ry="37.939529" />
+    <g
+       transform="matrix(2.514339,0,0,2.5959441,-3731.0374,-5840.643)"
+       id="g5086">
+      <path
+         sodipodi:type="arc"
+         style="opacity:0.98999999;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+         id="path5088"
+         sodipodi:cx="1324.929"
+         sodipodi:cy="1792.4095"
+         sodipodi:rx="7.3256478"
+         sodipodi:ry="7.3256478"
+         d="M 1332.2546,1792.4095 A 7.3256478,7.3256478 0 1 1 1317.6033,1792.4095 A 7.3256478,7.3256478 0 1 1 1332.2546,1792.4095 z"
+         transform="matrix(0.9672595,0,0,0.9672595,235.69886,548.04605)" />
+      <path
+         transform="matrix(0.9672595,0,0,0.9672595,308.74604,539.35166)"
+         d="M 1332.2546,1792.4095 A 7.3256478,7.3256478 0 1 1 1317.6033,1792.4095 A 7.3256478,7.3256478 0 1 1 1332.2546,1792.4095 z"
+         sodipodi:ry="7.3256478"
+         sodipodi:rx="7.3256478"
+         sodipodi:cy="1792.4095"
+         sodipodi:cx="1324.929"
+         id="path5090"
+         style="opacity:0.98999999;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+         sodipodi:type="arc" />
+      <path
+         sodipodi:type="arc"
+         style="opacity:0.98999999;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+         id="path5092"
+         sodipodi:cx="1324.929"
+         sodipodi:cy="1792.4095"
+         sodipodi:rx="7.3256478"
+         sodipodi:ry="7.3256478"
+         d="M 1332.2546,1792.4095 A 7.3256478,7.3256478 0 1 1 1317.6033,1792.4095 A 7.3256478,7.3256478 0 1 1 1332.2546,1792.4095 z"
+         transform="matrix(1.2053547,0,0,1.2053547,-25.196551,182.19849)" />
+      <path
+         sodipodi:type="arc"
+         style="opacity:0.98999999;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+         id="path5094"
+         sodipodi:cx="1324.929"
+         sodipodi:cy="1792.4095"
+         sodipodi:rx="7.3256478"
+         sodipodi:ry="7.3256478"
+         d="M 1332.2546,1792.4095 A 7.3256478,7.3256478 0 1 1 1317.6033,1792.4095 A 7.3256478,7.3256478 0 1 1 1332.2546,1792.4095 z"
+         transform="matrix(0.7953719,0,0,0.7953719,449.34462,925.45441)" />
+      <path
+         transform="matrix(0.7953719,0,0,0.7953719,499.30379,973.24771)"
+         d="M 1332.2546,1792.4095 A 7.3256478,7.3256478 0 1 1 1317.6033,1792.4095 A 7.3256478,7.3256478 0 1 1 1332.2546,1792.4095 z"
+         sodipodi:ry="7.3256478"
+         sodipodi:rx="7.3256478"
+         sodipodi:cy="1792.4095"
+         sodipodi:cx="1324.929"
+         id="path5096"
+         style="opacity:0.98999999;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+         sodipodi:type="arc" />
+      <path
+         sodipodi:type="arc"
+         style="opacity:0.98999999;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+         id="path5098"
+         sodipodi:cx="1324.929"
+         sodipodi:cy="1792.4095"
+         sodipodi:rx="7.3256478"
+         sodipodi:ry="7.3256478"
+         d="M 1332.2546,1792.4095 A 7.3256478,7.3256478 0 1 1 1317.6033,1792.4095 A 7.3256478,7.3256478 0 1 1 1332.2546,1792.4095 z"
+         transform="matrix(0.7953719,0,0,0.7953719,556.29908,956.69427)" />
+      <path
+         transform="matrix(0.7953719,0,0,0.7953719,581.00221,902.90894)"
+         d="M 1332.2546,1792.4095 A 7.3256478,7.3256478 0 1 1 1317.6033,1792.4095 A 7.3256478,7.3256478 0 1 1 1332.2546,1792.4095 z"
+         sodipodi:ry="7.3256478"
+         sodipodi:rx="7.3256478"
+         sodipodi:cy="1792.4095"
+         sodipodi:cx="1324.929"
+         id="path5100"
+         style="opacity:0.98999999;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+         sodipodi:type="arc" />
+      <path
+         sodipodi:type="arc"
+         style="opacity:0.98999999;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+         id="path5102"
+         sodipodi:cx="1324.929"
+         sodipodi:cy="1792.4095"
+         sodipodi:rx="7.3256478"
+         sodipodi:ry="7.3256478"
+         d="M 1332.2546,1792.4095 A 7.3256478,7.3256478 0 1 1 1317.6033,1792.4095 A 7.3256478,7.3256478 0 1 1 1332.2546,1792.4095 z"
+         transform="matrix(0.6234843,0,0,0.6234843,800.65848,1180.6657)" />
+      <path
+         transform="matrix(0.6234843,0,0,0.6234843,766.33259,1139.9519)"
+         d="M 1332.2546,1792.4095 A 7.3256478,7.3256478 0 1 1 1317.6033,1792.4095 A 7.3256478,7.3256478 0 1 1 1332.2546,1792.4095 z"
+         sodipodi:ry="7.3256478"
+         sodipodi:rx="7.3256478"
+         sodipodi:cy="1792.4095"
+         sodipodi:cx="1324.929"
+         id="path5104"
+         style="opacity:0.98999999;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+         sodipodi:type="arc" />
+      <path
+         sodipodi:type="arc"
+         style="opacity:0.98999999;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+         id="path5106"
+         sodipodi:cx="1324.929"
+         sodipodi:cy="1792.4095"
+         sodipodi:rx="7.3256478"
+         sodipodi:ry="7.3256478"
+         d="M 1332.2546,1792.4095 A 7.3256478,7.3256478 0 1 1 1317.6033,1792.4095 A 7.3256478,7.3256478 0 1 1 1332.2546,1792.4095 z"
+         transform="matrix(0.6234843,0,0,0.6234843,664.75027,1230.1938)" />
+      <path
+         transform="matrix(0.6234843,0,0,0.6234843,701.58781,1267.5497)"
+         d="M 1332.2546,1792.4095 A 7.3256478,7.3256478 0 1 1 1317.6033,1792.4095 A 7.3256478,7.3256478 0 1 1 1332.2546,1792.4095 z"
+         sodipodi:ry="7.3256478"
+         sodipodi:rx="7.3256478"
+         sodipodi:cy="1792.4095"
+         sodipodi:cx="1324.929"
+         id="path5108"
+         style="opacity:0.98999999;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+         sodipodi:type="arc" />
+      <path
+         transform="matrix(0.7953719,0,0,0.7953719,440.41431,869.81028)"
+         d="M 1332.2546,1792.4095 A 7.3256478,7.3256478 0 1 1 1317.6033,1792.4095 A 7.3256478,7.3256478 0 1 1 1332.2546,1792.4095 z"
+         sodipodi:ry="7.3256478"
+         sodipodi:rx="7.3256478"
+         sodipodi:cy="1792.4095"
+         sodipodi:cx="1324.929"
+         id="path5110"
+         style="opacity:0.98999999;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+         sodipodi:type="arc" />
+      <path
+         sodipodi:type="arc"
+         style="opacity:0.98999999;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+         id="path5112"
+         sodipodi:cx="1324.929"
+         sodipodi:cy="1792.4095"
+         sodipodi:rx="7.3256478"
+         sodipodi:ry="7.3256478"
+         d="M 1332.2546,1792.4095 A 7.3256478,7.3256478 0 1 1 1317.6033,1792.4095 A 7.3256478,7.3256478 0 1 1 1332.2546,1792.4095 z"
+         transform="matrix(0.7953719,0,0,0.7953719,484.50773,829.39633)" />
+      <path
+         sodipodi:type="arc"
+         style="opacity:0.98999999;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+         id="path5114"
+         sodipodi:cx="1324.929"
+         sodipodi:cy="1792.4095"
+         sodipodi:rx="7.3256478"
+         sodipodi:ry="7.3256478"
+         d="M 1332.2546,1792.4095 A 7.3256478,7.3256478 0 1 1 1317.6033,1792.4095 A 7.3256478,7.3256478 0 1 1 1332.2546,1792.4095 z"
+         transform="matrix(0.6234843,0,0,0.6234843,699.00639,1196.6154)" />
+      <path
+         transform="matrix(0.6234843,0,0,0.6234843,749.0301,1256.3969)"
+         d="M 1332.2546,1792.4095 A 7.3256478,7.3256478 0 1 1 1317.6033,1792.4095 A 7.3256478,7.3256478 0 1 1 1332.2546,1792.4095 z"
+         sodipodi:ry="7.3256478"
+         sodipodi:rx="7.3256478"
+         sodipodi:cy="1792.4095"
+         sodipodi:cx="1324.929"
+         id="path5116"
+         style="opacity:0.98999999;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+         sodipodi:type="arc" />
+      <path
+         style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+         d="M 1590.0065,2271.6035 L 1571.3784,2344.2766"
+         id="path5118" />
+      <path
+         style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+         d="M 1589.0995,2273.5223 L 1516.5407,2281.9768"
+         id="path5120" />
+      <path
+         style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+         d="M 1571.3087,2343.4372 L 1516.6105,2281.5571"
+         id="path5122" />
+      <path
+         style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:6;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+         d="M 1571.3087,2343.8569 L 1553.2387,2399.8009"
+         id="path5124" />
+      <path
+         style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+         d="M 1516.4012,2281.3172 L 1537.4014,2256.9729"
+         id="path5126" />
+      <path
+         style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+         d="M 1493.9359,2295.4681 L 1515.7035,2281.1374"
+         id="path5128" />
+      <path
+         style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+         d="M 1503.0057,2350.6326 L 1516.4012,2280.6577"
+         id="path5130" />
+      <path
+         style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:6;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+         d="M 1552.1922,2398.5417 L 1502.6569,2351.9517"
+         id="path5132" />
+      <path
+         style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:6;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+         d="M 1610.0997,2381.3328 L 1551.9829,2398.3018"
+         id="path5134" />
+      <path
+         style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:6;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+         d="M 1634.2395,2328.3869 L 1610.0997,2381.5126"
+         id="path5136" />
+      <path
+         style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:6;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+         d="M 1570.5412,2343.4372 L 1633.4023,2328.8066"
+         id="path5138" />
+      <path
+         style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:6;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+         d="M 1590.1461,2273.0426 L 1633.2627,2328.207"
+         id="path5140" />
+      <path
+         style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:6;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+         d="M 1501.7987,2350.6326 L 1568.7761,2343.4372"
+         id="path5142" />
+      <path
+         style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+         d="M 1551.4038,2398.062 L 1526.985,2384.9304"
+         id="path5144" />
+      <path
+         style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+         d="M 1489.38,2347.2747 L 1525.7292,2384.5707"
+         id="path5146" />
+      <path
+         style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+         d="M 1493.2173,2295.9478 L 1490.4963,2347.2747"
+         id="path5148" />
+      <path
+         style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+         d="M 1537.3805,2254.8743 L 1591.4507,2256.7331"
+         id="path5150" />
+      <path
+         style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+         d="M 1627.172,2298.5261 L 1592.9158,2256.5532"
+         id="path5152" />
+      <path
+         style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+         d="M 1635.4744,2328.207 L 1626.4743,2298.1064"
+         id="path5154" />
+      <path
+         style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+         d="M 1589.4972,2272.2631 L 1592.7065,2256.9729"
+         id="path5156" />
+      <path
+         style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+         d="M 1503.6825,2351.2305 L 1489.5196,2348.5322"
+         id="path5158" />
+      <path
+         style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+         d="M 1526.985,2385.4084 L 1573.7296,2373.9558"
+         id="path5160" />
+      <path
+         style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+         d="M 1574.9157,2373.8958 L 1608.5439,2381.1512"
+         id="path5162" />
+      <path
+         style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+         d="M 1492.5121,2295.941 L 1553.7151,2289.0932"
+         id="path5164" />
+      <path
+         style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+         d="M 1553.7151,2287.7387 L 1590.1392,2274.1935"
+         id="path5166" />
+      <path
+         sodipodi:type="arc"
+         style="opacity:0.98999999;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+         id="path5168"
+         sodipodi:cx="1324.929"
+         sodipodi:cy="1792.4095"
+         sodipodi:rx="7.3256478"
+         sodipodi:ry="7.3256478"
+         d="M 1332.2546,1792.4095 A 7.3256478,7.3256478 0 1 1 1317.6033,1792.4095 A 7.3256478,7.3256478 0 1 1 1332.2546,1792.4095 z"
+         transform="matrix(0.6234843,0,0,0.6234843,726.86911,1170.9368)" />
+      <path
+         transform="matrix(0.5833906,0,0,0.5833906,837.2333,1298.2147)"
+         d="M 1332.2546,1792.4095 A 7.3256478,7.3256478 0 1 1 1317.6033,1792.4095 A 7.3256478,7.3256478 0 1 1 1332.2546,1792.4095 z"
+         sodipodi:ry="7.3256478"
+         sodipodi:rx="7.3256478"
+         sodipodi:cy="1792.4095"
+         sodipodi:cx="1324.929"
+         id="path5170"
+         style="opacity:0.98999999;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+         sodipodi:type="arc" />
+      <path
+         style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+         d="M 1609.6576,2343.739 L 1626.0616,2297.6751"
+         id="path5172" />
+      <path
+         style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+         d="M 1609.4298,2381.481 L 1609.9994,2342.711"
+         id="path5174" />
+      <path
+         id="path5176"
+         d="M 1502.1919,2350.0723 L 1525.6258,2314.115"
+         style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+      <path
+         id="path5178"
+         d="M 1537.8572,2255.3651 L 1524.94,2314.115"
+         style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+    </g>
+  </g>
+</svg>
diff --git a/sagenb_export/nbextension/www/sagenb-export.css b/sagenb_export/nbextension/www/sagenb-export.css
new file mode 100644
index 0000000..14753b2
--- /dev/null
+++ b/sagenb_export/nbextension/www/sagenb-export.css
@@ -0,0 +1,124 @@
+* {
+    box-sizing: border-box;
+}
+
+html {
+    font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
+    padding: 0;
+}
+
+body {
+    margin: 0;
+    padding: 0;
+}
+
+h1 {
+    text-align: center;
+    font-size: 48px;
+    font-weight: normal;
+}
+
+p {
+    margin: 0;
+    padding: 0;
+}
+
+.header {
+    display: flex;
+    flex-direction: row;
+    justify-content: space-around;
+    align-items: center;
+    height: 128px;
+}
+
+.header h1 {
+    flex: 8 1 0;
+    margin: auto;
+}
+
+.header img {
+    flex: 2 0 0;
+    margin: auto;
+    height: 64px;
+}
+
+.warn {
+    width: 60%;
+    margin: auto;
+    border: 2px solid #ff7f7f;
+    background: #ffdddd;
+    border-radius: 10px;
+    padding: 1em 3em;
+}
+
+.warn a {
+    padding-top: 1em;
+    display: block;
+    text-align: center;
+}
+
+.error {
+    background-color: #7f0000;
+    color: #ccffcc;
+    padding: 0 2em;
+}
+
+.old {
+    padding: 0 3em;
+    margin-top: 2em;
+    margin-bottom: 2em;
+}
+
+.old pre {
+    font-size: 130%;
+    text-align: center;
+}
+
+
+.ellipsis {
+    width: 100%;
+    display: inline-block;
+    overflow: hidden;
+    text-overflow: ellipsis;
+}
+
+.nb-row.nb-header {
+    font-weight: bold;
+    text-align: center;
+    background-color: #7f7f7f !important;
+    color: #ffffff;
+}
+
+
+.nb-row {
+    display: flex;
+    flex-direction: row;
+    line-height: 48px;
+    white-space: nowrap;
+}
+
+.nb-row:nth-child(even) {
+    background: #FFF;
+}
+
+.nb-row:nth-child(odd) {
+    background: #eee;
+}
+
+.nb-row.clickable:hover {
+    background: #aaaaff;
+    cursor: pointer;
+}
+
+.nb-id {
+    padding: 0 1em 0 2em;
+    display: flex;
+    flex: 1 0 0;
+    font-family: "Lucida Console", Monaco, monospace;
+}
+
+.nb-name {
+    padding: 0 2em 0 1em;
+    display: flex;
+    flex: 8 1 0;
+}
diff --git a/sagenb_export/nbextension/www/sagenb-export.js b/sagenb_export/nbextension/www/sagenb-export.js
new file mode 100644
index 0000000..a0e1d6c
--- /dev/null
+++ b/sagenb_export/nbextension/www/sagenb-export.js
@@ -0,0 +1,15 @@
+'use strict';
+
+var exportSageNB = function(uniqueId) {
+    console.log('Converting ' + uniqueId);
+    var xhttp = new XMLHttpRequest();
+    xhttp.onreadystatechange = function() {
+        if (xhttp.readyState == 4 && xhttp.status == 200) {
+            document.location = 'http://' + document.location.host + xhttp.responseText;
+        } else {
+            document.querySelector('.error').innerText = xhttp.responseText;
+        }
+    };
+    xhttp.open('POST', '/sagenb/export', true);
+    xhttp.send(uniqueId);
+};
diff --git a/sagenb_export/sagenb_reader.py b/sagenb_export/sagenb_reader.py
new file mode 100644
index 0000000..ef98e3d
--- /dev/null
+++ b/sagenb_export/sagenb_reader.py
@@ -0,0 +1,178 @@
+
+import os
+import re
+
+try:
+    # Python 2
+    import cPickle as pickle
+except ImportError:
+    # Python 3
+    import pickle
+
+from sagenb_export.logger import log
+from sagenb_export.unescape import unescape
+
+
+CELL_FRONT = re.compile(u'^\{\{\{id=(?P<index>[0-9]*)\|$')
+CELL_MID = re.compile(u'^///$')
+CELL_BACK = re.compile(u'^\}\}\}$')
+
+
+
+class Cell(object):
+
+    def __init__(self, input):
+        self.input = input
+
+    def __repr__(self):
+        return '{0}:"{1}"'.format(type(self), self.input.encode('utf-8', 'replace'))
+
+class ComputeCell(Cell):
+
+    def __init__(self, index, input, output):
+        assert index >= 0
+        super(ComputeCell, self).__init__(input)
+        self.index = index
+        self.output = output
+    
+
+class TextCell(Cell):
+    pass
+    
+
+
+class WorksheetParser(object):
+
+    def __init__(self, worksheet_html):
+        self.worksheet_lines = worksheet_html.splitlines()
+        self.pos = 0
+        self.index = -1
+        log.debug('Worksheet has %s lines', len(self.worksheet_lines))
+        
+    @property
+    def line(self):
+        return self.worksheet_lines[self.pos]
+
+    def get_line_and_forward(self):
+        line = self.line
+        self.pos += 1
+        return line
+
+    @property
+    def is_finished(self):
+        return self.pos >= len(self.worksheet_lines)
+
+    @property
+    def is_cell_front(self):
+        match = CELL_FRONT.match(self.line)
+        if match:
+            self.index = int(match.group('index'))
+            return True
+        else:
+            return False
+
+    @property
+    def is_cell_mid(self):
+        match = CELL_MID.match(self.line)
+        return match != None
+
+    @property
+    def is_cell_back(self):
+        match = CELL_BACK.match(self.line)
+        return match != None
+
+    def _try_read_text(self):
+        accumulator = []
+        while not (self.is_cell_front or self.is_finished):
+            log.debug('Read text: %s', self.line)
+            accumulator.append(self.get_line_and_forward())
+        accumulator = u'\n'.join(accumulator).strip()
+        if accumulator:
+            return TextCell(unescape(accumulator))
+
+    def _read_cell_input(self):
+        assert self.is_cell_front
+        self.pos += 1
+        accumulator = []
+        while not (self.is_cell_mid or self.is_finished):
+            log.debug('Read cell input: %s', self.line)
+            accumulator.append(self.get_line_and_forward())
+        return unescape(u'\n'.join(accumulator).strip())
+
+    def _read_cell_output(self):
+        assert self.is_cell_mid
+        self.pos += 1
+        accumulator = []
+        while not (self.is_cell_back or self.is_finished):
+            log.debug('Read cell output: %s', self.line)
+            accumulator.append(self.get_line_and_forward())
+        return unescape(u'\n'.join(accumulator).strip())
+
+    def _read_cell(self):
+        input = self._read_cell_input()
+        output = self._read_cell_output()
+        return ComputeCell(self.index, input, output)
+        
+    def __iter__(self):
+        while not self.is_finished:
+            text = self._try_read_text()
+            if text:
+                yield text
+            yield self._read_cell()
+            if not self.is_finished:
+                assert self.is_cell_back
+                self.pos += 1
+        
+
+
+class NotebookSageNB(object):
+
+    def __init__(self, path):
+        log.debug('opening notebook root directory: %s', path)
+        self.path = path
+        with open(os.path.join(path, 'worksheet.html'), 'rb') as f:
+            self.ws = f.read().decode('utf-8')
+        with open(os.path.join(path, 'worksheet_conf.pickle'), 'rb') as f:
+            self.conf = pickle.load(f)
+
+    def __repr__(self):
+        return '{0}:"{1}"'.format(self.unique_id, self.name.encode('utf-8', 'replace'))
+            
+    @classmethod
+    def all_iter(cls, dot_sage):
+        store = os.path.join(dot_sage, 'sage_notebook.sagenb', 'home', '__store__')
+        for path, dirs, files in os.walk(store):
+            worksheet = os.path.join(path, 'worksheet.html')
+            if os.path.isfile(worksheet):
+                yield cls(path)
+
+    @classmethod
+    def find(cls, dot_sage, name_or_unique_id):
+        for notebook in cls.all_iter(dot_sage):
+            if notebook.unique_id == name_or_unique_id:
+                return notebook
+            if notebook.name == name_or_unique_id:
+                return notebook
+        raise ValueError('no such notebook: {0}'.format(name_or_unique_id))
+                
+    @property
+    def sort_key(self):
+        return (self.conf['owner'], self.conf['id_number'])
+
+    def __lt__(lhs, rhs):
+        return lhs.sort_key < rhs.sort_key
+    
+    @property
+    def unique_id(self):
+        return '{0}:{1}'.format(self.conf['owner'], self.conf['id_number'])
+
+    @property
+    def name(self):
+        return self.conf['name']
+
+    @property
+    def cells(self):
+        for cell in WorksheetParser(self.ws):
+            log.debug('Cell: %s', cell)
+            yield cell
+            
diff --git a/sagenb_export/text_writer.py b/sagenb_export/text_writer.py
new file mode 100644
index 0000000..4d4e63c
--- /dev/null
+++ b/sagenb_export/text_writer.py
@@ -0,0 +1,44 @@
+
+from sagenb_export.logger import log
+from sagenb_export.sagenb_reader import TextCell, ComputeCell
+
+
+HEADER = u"""
+Name: {nb.name}
+"""
+
+TEXT_CELL = u"""
+Text: {cell.input}
+"""
+
+
+COMPUTE_CELL = u"""
+In[{cell.index}]: {cell.input}
+Out[{cell.index}]: {cell.output}
+"""
+
+
+class TextWriter(object):
+
+    def __init__(self, sagenb):
+        self.nb = sagenb
+
+    @property
+    def header(self):
+        return HEADER.format(nb=self.nb)
+
+    @property
+    def cells(self):
+        for cell in self.nb.cells:
+            if isinstance(cell, TextCell):
+                yield TEXT_CELL.format(cell=cell)
+            elif isinstance(cell, ComputeCell):
+                yield COMPUTE_CELL.format(cell=cell)
+            else:
+                log.critical('unknown cell: {0}'.format(cell))
+    
+    def write(self, stream):
+        stream.write(self.header)
+        for cell in self.cells:
+            stream.write(cell)
+    
diff --git a/sagenb_export/unescape.py b/sagenb_export/unescape.py
new file mode 100644
index 0000000..a329698
--- /dev/null
+++ b/sagenb_export/unescape.py
@@ -0,0 +1,8 @@
+
+try:
+    # python 3
+    from html import unescape
+except ImportError:
+    # python 2
+    import HTMLParser
+    unescape = HTMLParser.HTMLParser().unescape
diff --git a/setup.py b/setup.py
new file mode 100755
index 0000000..0530977
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,24 @@
+#!/usr/bin/env python
+
+from setuptools import setup, find_packages
+
+setup(
+    name='sagenb_export',
+    description='Export Notebooks from SageNB',
+    author='Volker Braun',
+    author_email='vbraun.name at gmail.com',
+    install_requires=[
+        'six',
+        'ipython>=4',
+        'nbconvert>=4',
+    ],
+    packages=find_packages(),
+    include_package_data=True,
+    entry_points={
+        'console_scripts': [
+            'sagenb-export = sagenb_export.cmdline:main',
+        ],
+    },
+    version='2.0',
+    url='https://github.com/vbraun/ExportSageNB',
+)
diff --git a/test/__init__.py b/test/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/test/dot_sage/sage_notebook.sagenb/home/__store__/2/21/212/2123/admin/10/cells/1/sage0-size500-249051951.jmol.zip b/test/dot_sage/sage_notebook.sagenb/home/__store__/2/21/212/2123/admin/10/cells/1/sage0-size500-249051951.jmol.zip
new file mode 100755
index 0000000..c6732e7
Binary files /dev/null and b/test/dot_sage/sage_notebook.sagenb/home/__store__/2/21/212/2123/admin/10/cells/1/sage0-size500-249051951.jmol.zip differ
diff --git a/test/dot_sage/sage_notebook.sagenb/home/__store__/2/21/212/2123/admin/10/cells/1/sage0-size500.jmol b/test/dot_sage/sage_notebook.sagenb/home/__store__/2/21/212/2123/admin/10/cells/1/sage0-size500.jmol
new file mode 100755
index 0000000..34ccaf5
--- /dev/null
+++ b/test/dot_sage/sage_notebook.sagenb/home/__store__/2/21/212/2123/admin/10/cells/1/sage0-size500.jmol
@@ -0,0 +1,2 @@
+set defaultdirectory "sage0-size500-249051951.jmol.zip"
+script SCRIPT
diff --git a/test/dot_sage/sage_notebook.sagenb/home/__store__/2/21/212/2123/admin/10/cells/12/sage0-size500-900865605.jmol.zip b/test/dot_sage/sage_notebook.sagenb/home/__store__/2/21/212/2123/admin/10/cells/12/sage0-size500-900865605.jmol.zip
new file mode 100755
index 0000000..3f89e14
Binary files /dev/null and b/test/dot_sage/sage_notebook.sagenb/home/__store__/2/21/212/2123/admin/10/cells/12/sage0-size500-900865605.jmol.zip differ
diff --git a/test/dot_sage/sage_notebook.sagenb/home/__store__/2/21/212/2123/admin/10/cells/12/sage0-size500.jmol b/test/dot_sage/sage_notebook.sagenb/home/__store__/2/21/212/2123/admin/10/cells/12/sage0-size500.jmol
new file mode 100755
index 0000000..b46d1b5
--- /dev/null
+++ b/test/dot_sage/sage_notebook.sagenb/home/__store__/2/21/212/2123/admin/10/cells/12/sage0-size500.jmol
@@ -0,0 +1,2 @@
+set defaultdirectory "sage0-size500-900865605.jmol.zip"
+script SCRIPT
diff --git a/test/dot_sage/sage_notebook.sagenb/home/__store__/2/21/212/2123/admin/10/snapshots/1297626260.bz2 b/test/dot_sage/sage_notebook.sagenb/home/__store__/2/21/212/2123/admin/10/snapshots/1297626260.bz2
new file mode 100644
index 0000000..e4d0260
Binary files /dev/null and b/test/dot_sage/sage_notebook.sagenb/home/__store__/2/21/212/2123/admin/10/snapshots/1297626260.bz2 differ
diff --git a/test/dot_sage/sage_notebook.sagenb/home/__store__/2/21/212/2123/admin/10/snapshots/1297626396.bz2 b/test/dot_sage/sage_notebook.sagenb/home/__store__/2/21/212/2123/admin/10/snapshots/1297626396.bz2
new file mode 100644
index 0000000..cfdb776
Binary files /dev/null and b/test/dot_sage/sage_notebook.sagenb/home/__store__/2/21/212/2123/admin/10/snapshots/1297626396.bz2 differ
diff --git a/test/dot_sage/sage_notebook.sagenb/home/__store__/2/21/212/2123/admin/10/snapshots/1297626546.bz2 b/test/dot_sage/sage_notebook.sagenb/home/__store__/2/21/212/2123/admin/10/snapshots/1297626546.bz2
new file mode 100644
index 0000000..6983ab2
Binary files /dev/null and b/test/dot_sage/sage_notebook.sagenb/home/__store__/2/21/212/2123/admin/10/snapshots/1297626546.bz2 differ
diff --git a/test/dot_sage/sage_notebook.sagenb/home/__store__/2/21/212/2123/admin/10/snapshots/1297637968.bz2 b/test/dot_sage/sage_notebook.sagenb/home/__store__/2/21/212/2123/admin/10/snapshots/1297637968.bz2
new file mode 100644
index 0000000..e834ae1
Binary files /dev/null and b/test/dot_sage/sage_notebook.sagenb/home/__store__/2/21/212/2123/admin/10/snapshots/1297637968.bz2 differ
diff --git a/test/dot_sage/sage_notebook.sagenb/home/__store__/2/21/212/2123/admin/10/snapshots/1297637997.bz2 b/test/dot_sage/sage_notebook.sagenb/home/__store__/2/21/212/2123/admin/10/snapshots/1297637997.bz2
new file mode 100644
index 0000000..dcb6159
Binary files /dev/null and b/test/dot_sage/sage_notebook.sagenb/home/__store__/2/21/212/2123/admin/10/snapshots/1297637997.bz2 differ
diff --git a/test/dot_sage/sage_notebook.sagenb/home/__store__/2/21/212/2123/admin/10/snapshots/1297648831.bz2 b/test/dot_sage/sage_notebook.sagenb/home/__store__/2/21/212/2123/admin/10/snapshots/1297648831.bz2
new file mode 100644
index 0000000..a4879d3
Binary files /dev/null and b/test/dot_sage/sage_notebook.sagenb/home/__store__/2/21/212/2123/admin/10/snapshots/1297648831.bz2 differ
diff --git a/test/dot_sage/sage_notebook.sagenb/home/__store__/2/21/212/2123/admin/10/snapshots/1297678871.bz2 b/test/dot_sage/sage_notebook.sagenb/home/__store__/2/21/212/2123/admin/10/snapshots/1297678871.bz2
new file mode 100644
index 0000000..85df33c
Binary files /dev/null and b/test/dot_sage/sage_notebook.sagenb/home/__store__/2/21/212/2123/admin/10/snapshots/1297678871.bz2 differ
diff --git a/test/dot_sage/sage_notebook.sagenb/home/__store__/2/21/212/2123/admin/10/snapshots/1297680848.bz2 b/test/dot_sage/sage_notebook.sagenb/home/__store__/2/21/212/2123/admin/10/snapshots/1297680848.bz2
new file mode 100644
index 0000000..d685cd6
Binary files /dev/null and b/test/dot_sage/sage_notebook.sagenb/home/__store__/2/21/212/2123/admin/10/snapshots/1297680848.bz2 differ
diff --git a/test/dot_sage/sage_notebook.sagenb/home/__store__/2/21/212/2123/admin/10/worksheet.html b/test/dot_sage/sage_notebook.sagenb/home/__store__/2/21/212/2123/admin/10/worksheet.html
new file mode 100644
index 0000000..8911a2a
--- /dev/null
+++ b/test/dot_sage/sage_notebook.sagenb/home/__store__/2/21/212/2123/admin/10/worksheet.html
@@ -0,0 +1,233 @@
+
+
+<h1 style="text-align: center;">The 24-Cell</h1>
+
+{{{id=4|
+cell24 = polytopes.twenty_four_cell()
+cell24.f_vector()   # it is self-dual
+///
+(1, 24, 96, 96, 24, 1)
+}}}
+
+{{{id=86|
+cell24.f_vector?
+///
+<html><!--notruncate-->
+
+<div class="docstring">
+    
+  <p><strong>File:</strong> /home/vbraun/Sage/sage/local/lib/python2.6/site-packages/sage/geometry/polyhedra.py</p>
+<p><strong>Type:</strong> <type ‘instancemethod’></p>
+<p><strong>Definition:</strong> cell24.f_vector()</p>
+<p><strong>Docstring:</strong></p>
+<blockquote>
+<p>Return the f-vector.</p>
+<p>OUTPUT:</p>
+<p>Returns a vector whose <tt class="docutils literal"><span class="pre">i</span></tt>-th entry is the number of
+<tt class="docutils literal"><span class="pre">i</span></tt>-dimensional faces of the polytope.</p>
+<p>EXAMPLES:</p>
+<div class="highlight-python"><div class="highlight"><pre class="literal-block"><span class="gp">sage: </span><span class="n">p</span> <span class="o">=</span> <span class="n">Polyhedron</span><span class="p">(</span><span class="n">vertices</span> <span class="o">=</span> <span class="p">[[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">],</span> <span class="p">[</span><span class="mi">1 [...]
+<span class="gp">sage: </span><span class="n">p</span><span class="o">.</span><span class="n">f_vector</span><span class="p">()</span>
+<span class="go">(1, 7, 12, 7, 1)</span>
+</pre></div>
+</div>
+</blockquote>
+
+
+</div>
+</html>
+}}}
+
+<p>Here is a picture of the 24-cell projected into 3 dimensions:</p>
+
+{{{id=1|
+cell24.plot()
+///
+}}}
+
+<p><span style="font-weight: normal;"><span style="font-size: medium;">The "round" 24-cell can be $GL(4,\mathbb{Q})$-squished into a lattice polytope:</span></span></p>
+
+{{{id=13|
+cell24 = Polyhedron(vertices=[
+  (1,0,0,0),(0,1,0,0),(0,0,1,0),(0,0,0,1),(1,-1,-1,1),(0,0,-1,1),          
+  (0,-1,0,1),(-1,0,0,1),(1,0,0,-1),(0,1,0,-1),(0,0,1,-1),(-1,1,1,-1),
+  (1,-1,-1,0),(0,0,-1,0),(0,-1,0,0),(-1,0,0,0),(1,-1,0,0),(1,0,-1,0),
+  (0,1,1,-1),(-1,1,1,0),(-1,1,0,0),(-1,0,1,0),(0,-1,-1,1),(0,0,0,-1)])
+cell24.f_vector()
+///
+(1, 24, 96, 96, 24, 1)
+}}}
+
+{{{id=67|
+cell24.lattice_polytope().is_reflexive()
+///
+True
+}}}
+
+<h2>Symmetry groups</h2>
+<p>Here is the symmetry group of the 24-cell</p>
+
+{{{id=27|
+Aut = cell24.restricted_automorphism_group()
+Aut.cardinality()
+///
+1152
+}}}
+
+{{{id=34|
+Aut.gens()
+///
+[(3,9)(4,18)(7,13)(8,14)(10,20)(22,24), (2,3)(6,7)(10,11)(14,15)(17,18)(21,22), (2,4)(3,19)(5,18)(7,10)(8,21)(9,17)(12,22)(14,23)(15,24), (2,18)(3,17)(4,5)(8,23)(9,19)(12,24)(13,20)(14,21)(15,22), (2,19)(3,4)(5,17)(6,11)(8,22)(9,18)(12,21)(14,24)(15,23), (1,2,19,10,12,24,16,15,23,7,5,4)(3,18,20,9,21,11,14,22,13,8,17,6), (1,16)(2,21)(3,22)(4,8)(5,23)(9,24)(12,19)(14,18)(15,17)]
+}}}
+
+<p>Pick the following $G$-permutation action on the vertices of $\nabla$</p>
+
+{{{id=36|
+G = PermutationGroup([
+        '(1,14,22)(2,24,7)(3,18,16)(4,10,15)(5,21,11)(6,12,17)(8,19,13)(9,23,20)', 
+        '(1,10,16,7)(2,12,15,5)(3,9,14,8)(4,19,24,23)(6,20,11,13)(17,18,21,22)'])
+G.is_subgroup(Aut) and G.is_isomorphic( SL(2,3).as_matrix_group().as_permutation_group() )
+///
+True
+}}}
+
+{{{id=85|
+G.orbits()
+///
+[[1, 10, 14, 16, 15, 8, 22, 7, 3, 5, 4, 19, 17, 2, 9, 18, 21, 24, 13, 6, 12, 23, 11, 20]]
+}}}
+
+<p>Representatives for the 7 conjugacy classes:</p>
+
+{{{id=44|
+G.conjugacy_classes_representatives()
+///
+[(), (1,2,21,16,15,17)(3,19,10,14,23,7)(4,20,12,24,13,5)(6,8,22,11,9,18), (1,4,8,16,24,9)(2,6,23,15,11,19)(3,20,21,14,13,17)(5,7,22,12,10,18), (1,6,16,11)(2,14,15,3)(4,21,24,17)(5,8,12,9)(7,20,10,13)(18,23,22,19), (1,8,24)(2,23,11)(3,21,13)(4,16,9)(5,22,10)(6,15,19)(7,12,18)(14,17,20), (1,16)(2,15)(3,14)(4,24)(5,12)(6,11)(7,10)(8,9)(13,20)(17,21)(18,22)(19,23), (1,21,15)(2,16,17)(3,10,23)(4,12,13)(5,20,24)(6,22,9)(7,19,14)(8,11,18)]
+}}}
+
+<p>Here is the orbit of $1$ under the permutation $g_3$:</p>
+
+{{{id=52|
+(G.1).orbit(1)
+///
+[1, 14, 22]
+}}}
+
+<h1 style="text-align: center;">The 24-cell toric variety</h1>
+
+{{{id=45|
+fan = FaceFan(cell24.lattice_polytope())
+Pnabla = ToricVariety(FaceFan(cell24.lattice_polytope()),
+   coordinate_names='z1, z2, z3, z4, z5, z6, z7, z8, z9, z10, z11, z12, '+
+                    'z13, z14, z15, z16, z17, z18, z19, z20, z21, z22, z23, z24',
+   base_field=GF(101))
+Pnabla
+///
+4-d toric variety covered by 24 affine patches
+}}}
+
+{{{id=56|
+Pnabla.Chow_group().degree()
+///
+(Z, Z, C2 x C2 x Z^30, Z^20, Z)
+}}}
+
+{{{id=54|
+SR = Pnabla.Stanley_Reisner_ideal(); SR
+///
+Ideal (z2*z11, z4*z11, z5*z11, z6*z11, z8*z11, z11*z14, z11*z18, z11*z21, z11*z23, z1*z12, z4*z12, z5*z12, z6*z12, z7*z12, z12*z13, z12*z17, z12*z18, z12*z23, z1*z15, z2*z15, z4*z15, z6*z15, z10*z15, z15*z18, z15*z19, z15*z20, z15*z21, z1*z16, z2*z16, z3*z16, z4*z16, z5*z16, z9*z16, z16*z17, z16*z18, z16*z19, z1*z22, z2*z22, z5*z22, z6*z22, z9*z22, z10*z22, z13*z22, z14*z22, z18*z22, z1*z24, z2*z24, z3*z24, z4*z24, z5*z24, z6*z24, z7*z24, z8*z24, z20*z24, z3*z10, z4*z10, z5*z10, z7*z10,  [...]
+}}}
+
+{{{id=55|
+Pnabla.inject_variables()
+z1*z16 in SR  and  z1*z14*z22 in SR
+///
+Defining z1, z2, z3, z4, z5, z6, z7, z8, z9, z10, z11, z12, z13, z14, z15, z16, z17, z18, z19, z20, z21, z22, z23, z24
+True
+}}}
+
+<h1 style="text-align: center;">The Calabi-Yau Hypersurface</h1>
+<p style="text-align: left;">The polynomial $P=P_0+P_\infty$ is</p>
+
+{{{id=57|
+anticanonical_bundle = -Pnabla.K()
+P = sum(anticanonical_bundle.sections_monomials())
+P([1]*24)    # g_3 and g_4^2-fixed point z_i=1
+///
+25
+}}}
+
+<p>One of the maximal cones is $\langle p_1,p_2,p_3,p_4,p_{19},p_{20}\rangle$:</p>
+
+{{{id=69|
+cone = fan.generating_cone(8)
+cone.ambient_ray_indices()
+///
+(0, 1, 2, 3, 18, 19)
+}}}
+
+<p>The singularity of $\mathbb{P}_\nabla$ is where the corresponding homogeneous variables vanish:</p>
+
+{{{id=63|
+P(0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1)
+///
+1
+}}}
+
+<h1 style="text-align: center;">Checking Transversality of the Equation</h1>
+<p><span style="font-weight: normal;"><span style="font-size: medium;">The patches are non-complete intersections: 9 equations in $\mathbb{C}^8$ cutting out a $4$-dimensional affine toric variety</span></span></p>
+
+{{{id=71|
+ambient_patch = Pnabla.affine_algebraic_patch(cone, names='x+')
+ambient_patch
+///
+Closed subscheme of Affine Space of dimension 8 over Finite Field of size 101 defined by:
+  -x1*x4 + x0*x7,
+  -x1*x5 + x0*x6,
+  -x4*x6 + x5*x7,
+  x0*x4 - x3*x5,
+  -x2*x4 + x3*x7,
+  -x1*x4 + x3*x6,
+  x0*x2 - x1*x3,
+  x2*x6 - x1*x7,
+  -x1*x4 + x2*x5
+}}}
+
+<p>Of course there is a singularity at $0\in \mathbb{C}^8$:</p>
+
+{{{id=76|
+ambient_patch.is_smooth()
+///
+False
+}}}
+
+<p>Now we throw in the equation $P=0$ and go to the same patch again:</p>
+
+{{{id=70|
+Xtilde = Pnabla.subscheme(P)
+patch = Xtilde.affine_algebraic_patch(cone, names='x+')
+patch
+///
+Closed subscheme of Affine Space of dimension 8 over Finite Field of size 101 defined by:
+  -x1*x4 + x0*x7,
+  -x1*x5 + x0*x6,
+  -x4*x6 + x5*x7,
+  x0*x4 - x3*x5,
+  -x2*x4 + x3*x7,
+  -x1*x4 + x3*x6,
+  x0*x2 - x1*x3,
+  x2*x6 - x1*x7,
+  -x1*x4 + x2*x5,
+  x2^2*x5^2 + x0*x2*x5 + x2^2*x5 + x2*x3*x5 + x2*x4*x5 + x2*x5^2 + x0*x2*x6 + x2*x5*x6 + x2*x5*x7 + x0*x2 + x0*x4 + x2*x4 + x1*x5 + x2*x5 + x2*x6 + x5*x7 + x0 + x1 + x2 + x3 + x4 + x5 + x6 + x7 + 1
+}}}
+
+{{{id=73|
+patch.is_smooth()
+///
+True
+}}}
+
+{{{id=81|
+
+///
+}}}
\ No newline at end of file
diff --git a/test/dot_sage/sage_notebook.sagenb/home/__store__/2/21/212/2123/admin/10/worksheet_conf.pickle b/test/dot_sage/sage_notebook.sagenb/home/__store__/2/21/212/2123/admin/10/worksheet_conf.pickle
new file mode 100644
index 0000000..a39bf56
--- /dev/null
+++ b/test/dot_sage/sage_notebook.sagenb/home/__store__/2/21/212/2123/admin/10/worksheet_conf.pickle
@@ -0,0 +1,56 @@
+(dp1
+S'tags'
+p2
+(dp3
+S'admin'
+p4
+(lp5
+I0
+asS'guest'
+p6
+(lp7
+I1
+assS'id_number'
+p8
+I10
+sS'pretty_print'
+p9
+I00
+sS'system'
+p10
+S'sage'
+p11
+sS'published_id_number'
+p12
+NsS'owner'
+p13
+S'admin'
+p14
+sS'viewers'
+p15
+(lp16
+sS'ratings'
+p17
+(lp18
+sS'collaborators'
+p19
+(lp20
+sS'name'
+p21
+VOxford Seminar (1,1)-Calabi Yau
+p22
+sS'auto_publish'
+p23
+I00
+sS'last_change'
+p24
+(S'admin'
+p25
+F1297684209.597744
+tp26
+sS'worksheet_that_was_published'
+p27
+(g4
+I10
+tp28
+s.
\ No newline at end of file
diff --git a/test/dot_sage/sage_notebook.sagenb/home/__store__/2/21/212/2123/admin/4/worksheet.html b/test/dot_sage/sage_notebook.sagenb/home/__store__/2/21/212/2123/admin/4/worksheet.html
new file mode 100644
index 0000000..3edfbe5
--- /dev/null
+++ b/test/dot_sage/sage_notebook.sagenb/home/__store__/2/21/212/2123/admin/4/worksheet.html
@@ -0,0 +1,26 @@
+
+
+<h2>Równanie Newtona</h2>
+<h2>$$\vec F= m\vec a$$ </h2>
+<p>Rzut pionowy</p>
+<p>$$F= m a$$ </p>
+<p> </p>
+<h2> Krok czasowy: $\Delta t$.</h2>
+<p> </p>
+<ul>
+<li>$  v = \displaystyle\frac{ \Delta  y}{\Delta t}$</li>
+</ul>
+<ul>
+<li>$  a =  \displaystyle \frac{\Delta  v}{\Delta t}$</li>
+</ul>
+<div> </div>
+<p>$$\begin{cases} \quad \displaystyle \frac{\Delta y}{\Delta t} &=& v \\ \quad \displaystyle \frac{\Delta v}{\Delta t} &=& \displaystyle \frac { F}{m} \end{cases} $$</p>
+<p> </p>
+<p> </p>
+<div> $\:y_0\:$  i $\:v_{0}\:$</div>
+<p>$$\begin{cases}<br />\quad y &=&y_0\ +\ v_{0}\:\Delta t\\ <br /><br />\quad v &=&v_{0}\ +\  \frac{F}{m}\:\Delta t \end{cases}$$</p>
+
+{{{id=1|
+
+///
+}}}
\ No newline at end of file
diff --git a/test/dot_sage/sage_notebook.sagenb/home/__store__/2/21/212/2123/admin/4/worksheet_conf.pickle b/test/dot_sage/sage_notebook.sagenb/home/__store__/2/21/212/2123/admin/4/worksheet_conf.pickle
new file mode 100644
index 0000000..389a427
--- /dev/null
+++ b/test/dot_sage/sage_notebook.sagenb/home/__store__/2/21/212/2123/admin/4/worksheet_conf.pickle
@@ -0,0 +1,58 @@
+(dp1
+S'system'
+p2
+S'sage'
+p3
+sS'saved_by_info'
+p4
+(dp5
+sS'tags'
+p6
+(dp7
+S'admin'
+p8
+(lp9
+I1
+assS'id_number'
+p10
+I4
+sS'pretty_print'
+p11
+I00
+sS'live_3D'
+p12
+I00
+sS'published_id_number'
+p13
+NsS'owner'
+p14
+S'admin'
+p15
+sS'viewers'
+p16
+(lp17
+sS'ratings'
+p18
+(lp19
+sS'collaborators'
+p20
+(lp21
+sS'name'
+p22
+VMathJax_problem1
+p23
+sS'auto_publish'
+p24
+I00
+sS'last_change'
+p25
+(S'admin'
+p26
+F1467286221.0016849
+tp27
+sS'worksheet_that_was_published'
+p28
+(g15
+I4
+tp29
+s.
\ No newline at end of file
diff --git a/test/dot_sage/sage_notebook.sagenb/home/__store__/2/21/212/2123/admin/5/worksheet.html b/test/dot_sage/sage_notebook.sagenb/home/__store__/2/21/212/2123/admin/5/worksheet.html
new file mode 100644
index 0000000..e69de29
diff --git a/test/dot_sage/sage_notebook.sagenb/home/__store__/2/21/212/2123/admin/5/worksheet_conf.pickle b/test/dot_sage/sage_notebook.sagenb/home/__store__/2/21/212/2123/admin/5/worksheet_conf.pickle
new file mode 100644
index 0000000..6a68c39
--- /dev/null
+++ b/test/dot_sage/sage_notebook.sagenb/home/__store__/2/21/212/2123/admin/5/worksheet_conf.pickle
@@ -0,0 +1,66 @@
+(dp1
+S'system'
+p2
+S'sage'
+p3
+sS'saved_by_info'
+p4
+(dp5
+sS'tags'
+p6
+(dp7
+Vadmin
+p8
+(lp9
+I1
+asValeksandra.slapik
+p10
+(lp11
+I1
+asVmarcin.kostur
+p12
+(lp13
+I1
+assS'id_number'
+p14
+I44
+sS'pretty_print'
+p15
+I00
+sS'live_3D'
+p16
+I00
+sS'published_id_number'
+p17
+NsS'owner'
+p18
+Valeksandra.slapik
+p19
+sS'viewers'
+p20
+(lp21
+sS'ratings'
+p22
+(lp23
+sS'collaborators'
+p24
+(lp25
+sS'name'
+p26
+VWDI projekt - R�\u017cankowski, Kie\u0142pi\u0144ski, Kozok
+p27
+sS'auto_publish'
+p28
+I00
+sS'last_change'
+p29
+(S'aleksandra.slapik'
+p30
+F1453628866.7567761
+tp31
+sS'worksheet_that_was_published'
+p32
+(g10
+I44
+tp33
+s.
\ No newline at end of file
diff --git a/test/dot_sage/sage_notebook.sagenb/home/__store__/9/9b/9bb/9bbe/_sage_/4/worksheet.html b/test/dot_sage/sage_notebook.sagenb/home/__store__/9/9b/9bb/9bbe/_sage_/4/worksheet.html
new file mode 100644
index 0000000..a7d39db
--- /dev/null
+++ b/test/dot_sage/sage_notebook.sagenb/home/__store__/9/9b/9bb/9bbe/_sage_/4/worksheet.html
@@ -0,0 +1,282 @@
+
+
+<div class="related">
+      <h3>Navigation</h3>
+      <ul>
+        <li class="right" style="margin-right: 10px">
+          <a href="genindex.html" title="General Index" accesskey="I">index</a></li>
+        <li class="right">
+          <a href="py-modindex.html" title="Python Module Index">modules</a> |</li>
+        <li class="right">
+          <a href="introduction.html" title="Introduction" accesskey="N">next</a> |</li>
+  
+    
+      <a href="../index.html"><img src="_static/sagelogo.png" style="vertical-align: middle" title="Sage Logo"></a>
+    
+  
+  
+        <li><a href="#">Sage Tutorial v6.4.rc1</a> »</li>
+ 
+      </ul>
+    </div>  
+
+    <div class="document">
+      <div class="documentwrapper">
+        <div class="bodywrapper">
+          <div class="body">
+            
+  <div class="section" id="welcome-to-the-sage-tutorial">
+<h1>Welcome to the Sage Tutorial!<a class="headerlink" href="#welcome-to-the-sage-tutorial" title="Permalink to this headline">¶</a></h1>
+<p>Sage is free, open-source math software that supports research and
+teaching in algebra, geometry, number theory, cryptography,
+numerical computation, and related areas. Both the Sage development
+model and the technology in Sage itself are distinguished by an
+extremely strong emphasis on openness, community, cooperation, and
+collaboration: we are building the car, not reinventing the wheel.
+The overall goal of Sage is to create a viable, free, open-source
+alternative to Maple, Mathematica, Magma, and MATLAB.</p>
+<p>This tutorial is the best way to become familiar with Sage in only
+a few hours. You can read it in HTML or PDF versions, or from the
+Sage notebook (click <tt class="docutils literal"><span class="pre">Help</span></tt>, then click <tt class="docutils literal"><span class="pre">Tutorial</span></tt> to interactively
+work through the tutorial from within Sage).</p>
+<p>This work is licensed under a <a class="reference external" href="http://creativecommons.org/licenses/by-sa/3.0/">Creative Commons Attribution-Share Alike
+3.0 License</a>.</p>
+<div class="toctree-wrapper compound">
+<ul>
+<li class="toctree-l1"><a class="reference internal" href="introduction.html">Introduction</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="introduction.html#installation">Installation</a></li>
+<li class="toctree-l2"><a class="reference internal" href="introduction.html#ways-to-use-sage">Ways to Use Sage</a></li>
+<li class="toctree-l2"><a class="reference internal" href="introduction.html#longterm-goals-for-sage">Longterm Goals for Sage</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="tour.html">A Guided Tour</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="tour_assignment.html">Assignment, Equality, and Arithmetic</a></li>
+<li class="toctree-l2"><a class="reference internal" href="tour_help.html">Getting Help</a></li>
+<li class="toctree-l2"><a class="reference internal" href="tour_help.html#functions-indentation-and-counting">Functions, Indentation, and Counting</a></li>
+<li class="toctree-l2"><a class="reference internal" href="tour_algebra.html">Basic Algebra and Calculus</a></li>
+<li class="toctree-l2"><a class="reference internal" href="tour_plotting.html">Plotting</a></li>
+<li class="toctree-l2"><a class="reference internal" href="tour_functions.html">Some Common Issues with Functions</a></li>
+<li class="toctree-l2"><a class="reference internal" href="tour_rings.html">Basic Rings</a></li>
+<li class="toctree-l2"><a class="reference internal" href="tour_linalg.html">Linear Algebra</a></li>
+<li class="toctree-l2"><a class="reference internal" href="tour_polynomial.html">Polynomials</a></li>
+<li class="toctree-l2"><a class="reference internal" href="tour_coercion.html">Parents, Conversion and Coercion</a></li>
+<li class="toctree-l2"><a class="reference internal" href="tour_groups.html">Finite Groups, Abelian Groups</a></li>
+<li class="toctree-l2"><a class="reference internal" href="tour_numtheory.html">Number Theory</a></li>
+<li class="toctree-l2"><a class="reference internal" href="tour_advanced.html">Some More Advanced Mathematics</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="interactive_shell.html">The Interactive Shell</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="interactive_shell.html#your-sage-session">Your Sage Session</a></li>
+<li class="toctree-l2"><a class="reference internal" href="interactive_shell.html#logging-input-and-output">Logging Input and Output</a></li>
+<li class="toctree-l2"><a class="reference internal" href="interactive_shell.html#paste-ignores-prompts">Paste Ignores Prompts</a></li>
+<li class="toctree-l2"><a class="reference internal" href="interactive_shell.html#timing-commands">Timing Commands</a></li>
+<li class="toctree-l2"><a class="reference internal" href="interactive_shell.html#other-ipython-tricks">Other IPython tricks</a></li>
+<li class="toctree-l2"><a class="reference internal" href="interactive_shell.html#errors-and-exceptions">Errors and Exceptions</a></li>
+<li class="toctree-l2"><a class="reference internal" href="interactive_shell.html#reverse-search-and-tab-completion">Reverse Search and Tab Completion</a></li>
+<li class="toctree-l2"><a class="reference internal" href="interactive_shell.html#integrated-help-system">Integrated Help System</a></li>
+<li class="toctree-l2"><a class="reference internal" href="interactive_shell.html#saving-and-loading-individual-objects">Saving and Loading Individual Objects</a></li>
+<li class="toctree-l2"><a class="reference internal" href="interactive_shell.html#saving-and-loading-complete-sessions">Saving and Loading Complete Sessions</a></li>
+<li class="toctree-l2"><a class="reference internal" href="interactive_shell.html#the-notebook-interface">The Notebook Interface</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Interfaces</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="interfaces.html#gp-pari">GP/PARI</a></li>
+<li class="toctree-l2"><a class="reference internal" href="interfaces.html#gap">GAP</a></li>
+<li class="toctree-l2"><a class="reference internal" href="interfaces.html#singular">Singular</a></li>
+<li class="toctree-l2"><a class="reference internal" href="interfaces.html#maxima">Maxima</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="latex.html">Sage, LaTeX and Friends</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="latex.html#overview">Overview</a></li>
+<li class="toctree-l2"><a class="reference internal" href="latex.html#basic-use">Basic Use</a></li>
+<li class="toctree-l2"><a class="reference internal" href="latex.html#customizing-latex-generation">Customizing LaTeX Generation</a></li>
+<li class="toctree-l2"><a class="reference internal" href="latex.html#customizing-latex-processing">Customizing LaTeX Processing</a></li>
+<li class="toctree-l2"><a class="reference internal" href="latex.html#an-example-combinatorial-graphs-with-tkz-graph">An Example: Combinatorial Graphs with tkz-graph</a></li>
+<li class="toctree-l2"><a class="reference internal" href="latex.html#a-fully-capable-tex-installation">A Fully Capable TeX Installation</a></li>
+<li class="toctree-l2"><a class="reference internal" href="latex.html#external-programs">External Programs</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="programming.html">Programming</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="programming.html#loading-and-attaching-sage-files">Loading and Attaching Sage files</a></li>
+<li class="toctree-l2"><a class="reference internal" href="programming.html#creating-compiled-code">Creating Compiled Code</a></li>
+<li class="toctree-l2"><a class="reference internal" href="programming.html#standalone-python-sage-scripts">Standalone Python/Sage Scripts</a></li>
+<li class="toctree-l2"><a class="reference internal" href="programming.html#data-types">Data Types</a></li>
+<li class="toctree-l2"><a class="reference internal" href="programming.html#lists-tuples-and-sequences">Lists, Tuples, and Sequences</a></li>
+<li class="toctree-l2"><a class="reference internal" href="programming.html#dictionaries">Dictionaries</a></li>
+<li class="toctree-l2"><a class="reference internal" href="programming.html#sets">Sets</a></li>
+<li class="toctree-l2"><a class="reference internal" href="programming.html#iterators">Iterators</a></li>
+<li class="toctree-l2"><a class="reference internal" href="programming.html#loops-functions-control-statements-and-comparisons">Loops, Functions, Control Statements, and Comparisons</a></li>
+<li class="toctree-l2"><a class="reference internal" href="programming.html#profiling">Profiling</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="sagetex.html">Using SageTeX</a></li>
+<li class="toctree-l1"><a class="reference internal" href="afterword.html">Afterword</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="afterword.html#why-python">Why Python?</a></li>
+<li class="toctree-l2"><a class="reference internal" href="afterword.html#i-would-like-to-contribute-somehow-how-can-i">I would like to contribute somehow. How can I?</a></li>
+<li class="toctree-l2"><a class="reference internal" href="afterword.html#how-do-i-reference-sage">How do I reference Sage?</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="appendix.html">Appendix</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="appendix.html#arithmetical-binary-operator-precedence">Arithmetical binary operator precedence</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="bibliography.html">Bibliography</a></li>
+</ul>
+</div>
+</div>
+<div class="section" id="indices-and-tables">
+<h1>Indices and tables<a class="headerlink" href="#indices-and-tables" title="Permalink to this headline">¶</a></h1>
+<ul class="simple">
+<li><a class="reference internal" href="genindex.html"><em>Index</em></a></li>
+<li><a class="reference internal" href="py-modindex.html"><em>Module Index</em></a></li>
+<li><a class="reference internal" href="search.html"><em>Search Page</em></a></li>
+</ul>
+</div>
+
+
+          </div>
+        </div>
+      </div>
+      <div class="sphinxsidebar">
+        <div class="sphinxsidebarwrapper">
+            <h3><a href="#">Table Of Contents</a></h3>
+            <ul>
+<li><a class="reference internal" href="#">Welcome to the Sage Tutorial!</a></li>
+<li><a class="reference internal" href="#indices-and-tables">Indices and tables</a></li>
+</ul>
+
+            <h4>Next topic</h4>
+            <p class="topless"><a href="introduction.html" title="next chapter">Introduction</a></p>
+            <h3>This Page</h3>
+            <ul class="this-page-menu">
+              <li><a href="_sources/index.txt" rel="nofollow">Show Source</a></li>
+            </ul>
+          <div id="searchbox" style="display: none">
+            <h3>Quick search</h3>
+              <p class="searchtip" style="font-size: 90%">
+              Enter search terms or a module, class or function name.
+              </p>
+          </div>
+          <script type="text/javascript">$('#searchbox').show(0);</script>
+        </div>
+      </div>
+      <div class="clearer"></div>
+    </div>
+    <div class="related">
+      <h3>Navigation</h3>
+      <ul>
+        <li class="right" style="margin-right: 10px">
+          <a href="genindex.html" title="General Index">index</a></li>
+        <li class="right">
+          <a href="py-modindex.html" title="Python Module Index">modules</a> |</li>
+        <li class="right">
+          <a href="introduction.html" title="Introduction">next</a> |</li>
+  
+    
+      <a href="../index.html"><img src="_static/sagelogo.png" style="vertical-align: middle" title="Sage Logo"></a>
+    
+  
+  
+        <li><a href="#">Sage Tutorial v6.4.rc1</a> »</li>
+ 
+      </ul>
+    </div>
+    
+    <div class="footer">
+        © Copyright 2005--2014, The Sage Development Team.
+      Created using <a href="http://sphinx-doc.org/">Sphinx</a> 1.2.2.
+    </div>
+    <script type="text/javascript">
+/*global jQuery, window */
+/* Sphinx sidebar toggle.  Putting this code at the end of the body
+ * enables the toggle for the live, static, and offline docs.  Note:
+ * sage.misc.html.math_parse() eats jQuery's dollar-sign shortcut. */
+var jq = jQuery;
+jq(document).ready(function () {
+    var bar, bod, bg, fg, key, tog, wid_old, wid_new, resize, get_state, set_state;
+    bod = jq('div.bodywrapper');
+    bar = jq('div.sphinxsidebar');
+    tog = jq('<div class="sphinxsidebartoggle"></div>');
+
+    /* Delayed resize helper.  Not perfect but good enough. */
+    resize = function () {
+        setTimeout(function () {
+            tog.height(bod.height());
+        }, 100);
+    };
+    jq(window).resize(function () {
+        resize();
+    });
+
+    /* Setup and add the toggle. See Sphinx v0.5.1 default.css. */
+    fg = jq('div.sphinxsidebar p a').css('color') || 'rgb(152, 219, 204)';
+    bg = jq('div.document').css('background-color') || 'rgb(28, 78, 99)';
+    wid_old = '230px';
+    wid_new = '5px';
+    tog.css('background-color', bg)
+        .css('border-width', '0px')
+        .css('border-right', wid_new + ' ridge ' + bg)
+        .css('cursor', 'pointer')
+        .css('position', 'absolute')
+        .css('left', '-' + wid_new)
+        .css('top', '0px')
+        .css('width', wid_new);
+    bod.css('position', 'relative');
+    bod.prepend(tog);
+    resize();
+
+    /* Cookie helpers. */
+    key = 'sphinxsidebar=';
+    set_state = function (s) {
+        var date = new Date();
+        /* Expiry in 7 days. */
+        date.setTime(date.getTime() + (7 * 24 * 3600 * 1000));
+        document.cookie = key + encodeURIComponent(s) + '; expires=' +
+            date.toUTCString() + '; path=/';
+    };
+    get_state = function () {
+        var i, c, crumbs = document.cookie.split(';');
+        for (i = 0; i < crumbs.length; i += 1) {
+            c = crumbs[i].replace(/^\s+/, '');
+            if (c.indexOf(key) === 0) {
+                return decodeURIComponent(c.substring(key.length, c.length));
+            }
+        }
+        return null;
+    };
+
+    /* Event handlers. */
+    tog.mouseover(function (ev) {
+        tog.css('border-right-color', fg);
+    }).mouseout(function (ev) {
+        tog.css('border-right-color', bg);
+    }).click(function (ev) {
+        if (bod.hasClass('wide')) {
+            bod.removeClass('wide');
+            bod.css('margin-left', wid_old);
+            bar.css('width', wid_old);
+            bar.show();
+            set_state('visible');
+        } else {
+            set_state('hidden');
+            bar.hide();
+            bar.css('width', '0px');
+            bod.css('margin-left', wid_new);
+            bod.addClass('wide');
+        }
+        resize();
+    });
+
+    /* Hide the normally visible sidebar? */
+    if (get_state() === 'hidden') {
+        tog.trigger('click');
+    } else {
+        set_state('visible');
+    }
+});
+    </script>
+
+{{{id=1|
+
+///
+}}}
\ No newline at end of file
diff --git a/test/dot_sage/sage_notebook.sagenb/home/__store__/9/9b/9bb/9bbe/_sage_/4/worksheet_conf.pickle b/test/dot_sage/sage_notebook.sagenb/home/__store__/9/9b/9bb/9bbe/_sage_/4/worksheet_conf.pickle
new file mode 100644
index 0000000..094432c
--- /dev/null
+++ b/test/dot_sage/sage_notebook.sagenb/home/__store__/9/9b/9bb/9bbe/_sage_/4/worksheet_conf.pickle
@@ -0,0 +1,61 @@
+(dp1
+S'system'
+p2
+S'sage'
+p3
+sS'saved_by_info'
+p4
+(dp5
+sS'tags'
+p6
+(dp7
+S'admin'
+p8
+(lp9
+I1
+asS'_sage_'
+p10
+(lp11
+I1
+assS'id_number'
+p12
+I4
+sS'pretty_print'
+p13
+I00
+sS'live_3D'
+p14
+I00
+sS'published_id_number'
+p15
+NsS'owner'
+p16
+S'_sage_'
+p17
+sS'viewers'
+p18
+(lp19
+sS'ratings'
+p20
+(lp21
+sS'collaborators'
+p22
+(lp23
+sS'name'
+p24
+VWelcome to the Sage Tutorial! -- Sage Tutorial v6.4.rc1
+p25
+sS'auto_publish'
+p26
+I00
+sS'last_change'
+p27
+(g10
+F1415283547.06429
+tp28
+sS'worksheet_that_was_published'
+p29
+(g10
+I4
+tp30
+s.
\ No newline at end of file
diff --git a/test/test_sagenb_list.py b/test/test_sagenb_list.py
new file mode 100644
index 0000000..9696c6e
--- /dev/null
+++ b/test/test_sagenb_list.py
@@ -0,0 +1,28 @@
+# 
+
+
+import os
+import unittest
+from sagenb_export.sagenb_reader import (
+    NotebookSageNB,
+    TextCell, ComputeCell,
+)
+
+
+DOT_SAGE = os.path.join(os.path.dirname(__file__), 'dot_sage')
+
+class ListSageNB(unittest.TestCase):
+
+    def test_list(self):
+        nbks = list(NotebookSageNB.all_iter(DOT_SAGE))
+        self.assertEqual(len(nbks), 4)
+        sage_4, admin_4, admin_10, aleks = sorted(nbks)
+        self.assertEqual(sage_4.unique_id, '_sage_:4')
+        self.assertEqual(sage_4.name, u'Welcome to the Sage Tutorial! -- Sage Tutorial v6.4.rc1')
+        self.assertEqual(admin_4.unique_id, 'admin:4')
+        self.assertEqual(admin_4.name, u'MathJax_problem1')
+        self.assertEqual(admin_10.unique_id, 'admin:10')
+        self.assertEqual(admin_10.name, u'Oxford Seminar (1,1)-Calabi Yau')
+        self.assertEqual(aleks.unique_id, 'aleksandra.slapik:44')
+        self.assertEqual(aleks.name, u'WDI projekt - R\xf3\u017cankowski, Kie\u0142pi\u0144ski, Kozok')
+
diff --git a/test/test_sagenb_reader.py b/test/test_sagenb_reader.py
new file mode 100644
index 0000000..ada8c88
--- /dev/null
+++ b/test/test_sagenb_reader.py
@@ -0,0 +1,68 @@
+
+import os
+import unittest
+import six
+from sagenb_export.sagenb_reader import (
+    NotebookSageNB,
+    TextCell, ComputeCell,
+)
+
+
+if six.PY2:
+    string_type = unicode
+else:
+    string_type = str
+
+    
+DOT_SAGE = os.path.join(os.path.dirname(__file__), 'dot_sage')
+
+
+class ReadSageNB(unittest.TestCase):
+    """
+    Test various sample notebooks
+    """
+
+    def test_admin_10(self):
+        notebook = NotebookSageNB.find(DOT_SAGE, 'admin:10')
+        self.assertEqual(notebook.unique_id, 'admin:10')
+        self.assertEqual(notebook.name, 'Oxford Seminar (1,1)-Calabi Yau')
+        cell = list(notebook.cells)
+        self.assertEqual(len(cell), 37)
+        # First cell
+        self.assertIsInstance(cell[0], TextCell)
+        self.assertEqual(cell[0].input, '<h1 style="text-align: center;">The 24-Cell</h1>')
+        # Second cell
+        self.assertIsInstance(cell[1], ComputeCell)
+        self.assertEqual(cell[1].index, 4)
+        self.assertEqual(
+            cell[1].input,
+            'cell24 = polytopes.twenty_four_cell()\ncell24.f_vector()   # it is self-dual')
+        self.assertEqual(cell[1].output, '(1, 24, 96, 96, 24, 1)')
+
+    def test_sage_4(self):
+        notebook = NotebookSageNB.find(DOT_SAGE, '_sage_:4')
+        self.assertEqual(notebook.unique_id, '_sage_:4')
+        self.assertEqual(notebook.name, 'Welcome to the Sage Tutorial! -- Sage Tutorial v6.4.rc1')
+        cell = list(notebook.cells)
+        self.assertEqual(len(cell), 2)
+        self.assertEqual(type(cell[0].input), string_type)
+        self.assertEqual(type(cell[1].input), string_type)
+        self.assertEqual(type(cell[1].output), string_type)
+
+    def test_admin_4(self):
+        notebook = NotebookSageNB.find(DOT_SAGE, 'admin:4')
+        self.assertEqual(notebook.unique_id, 'admin:4')
+        self.assertEqual(notebook.name, u'MathJax_problem1')
+        cell_list = list(notebook.cells)
+        self.assertEqual(len(cell_list), 2)
+        # First cell
+        cell0 = cell_list[0]
+        self.assertIsInstance(cell0, TextCell)
+        self.assertEqual(cell0.input, u'<h2>R\xf3wnanie Newtona</h2>\n<h2>$$\\vec F= m\\vec a$$\xa0</h2>\n<p>Rzut pionowy</p>\n<p>$$F= m a$$\xa0</p>\n<p>\xa0</p>\n<h2>\xa0Krok czasowy: $\\Delta t$.</h2>\n<p>\xa0</p>\n<ul>\n<li>$ \xa0v\xa0=\xa0\\displaystyle\\frac{ \\Delta \xa0y}{\\Delta t}$</li>\n</ul>\n<ul>\n<li>$ \xa0a\xa0=\xa0 \\displaystyle \\frac{\\Delta \xa0v}{\\Delta t}$</li>\n</ul>\n<div>\xa0</div>\n<p>$$\\begin{cases} \\quad \\displaystyle \\frac{\\Delta y}{\\Delta t} &=& v \\\\ [...]
+        # Second cell
+        cell1 = cell_list[1]
+        self.assertIsInstance(cell1, ComputeCell)
+        self.assertEqual(cell1.index, 1)
+        self.assertEqual(cell1.input, '')
+        self.assertEqual(cell1.output, '')
+        
diff --git a/test/test_sagenb_writer.py b/test/test_sagenb_writer.py
new file mode 100644
index 0000000..c1b18a4
--- /dev/null
+++ b/test/test_sagenb_writer.py
@@ -0,0 +1,39 @@
+
+import os
+import unittest
+import tempfile
+import shutil
+from sagenb_export.sagenb_reader import (
+    NotebookSageNB,
+    TextCell, ComputeCell,
+)
+from sagenb_export.ipynb_writer import IpynbWriter
+
+
+DOT_SAGE = os.path.join(os.path.dirname(__file__), 'dot_sage')
+
+
+class ReadSageNB(unittest.TestCase):
+    """
+    Test various sample notebooks
+    """
+
+    def setUp(self):
+        self.tmp = tempfile.mkdtemp(prefix='sagenb_export_')
+        
+    def tearDown(self):
+        shutil.rmtree(self.tmp, ignore_errors=True)
+
+    def tmp_filename(self, name):
+        return os.path.join(self.tmp, name)
+        
+    def test_sage_4(self):
+        notebook = NotebookSageNB.find(DOT_SAGE, '_sage_:4')
+        ipynb = IpynbWriter(notebook)
+        ipynb.write(self.tmp_filename('sage:4.ipynb'))
+
+    def test_aleksandra_slapik_44(self):
+        notebook = NotebookSageNB.find(DOT_SAGE, 'aleksandra.slapik:44')
+        ipynb = IpynbWriter(notebook)
+        ipynb.write(self.tmp_filename('aleksandra_slapik_44.ipynb'))
+        ipynb.write(self.tmp_filename(u'WDI projekt - R\xf3\u017cankowski, Kie\u0142pi\u0144ski, Kozok.ipynb'))
diff --git a/tox.ini b/tox.ini
new file mode 100644
index 0000000..1522bb8
--- /dev/null
+++ b/tox.ini
@@ -0,0 +1,12 @@
+[tox]
+envlist = py27, py34
+
+[testenv:py27]
+commands=python2.7 -m unittest discover
+
+[testenv:py34]
+commands=python3.4 -m unittest discover
+
+[testenv:py35]
+commands=python3.5 -m unittest discover
+

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-science/packages/sagenb-export.git



More information about the debian-science-commits mailing list