Merge branch 'production'
authorRadek Czajka <radoslaw.czajka@nowoczesnapolska.org.pl>
Wed, 19 May 2010 13:45:02 +0000 (15:45 +0200)
committerRadek Czajka <radoslaw.czajka@nowoczesnapolska.org.pl>
Wed, 19 May 2010 13:45:02 +0000 (15:45 +0200)
203 files changed:
.gitignore
LICENSE [new file with mode: 0644]
NOTICE [new file with mode: 0644]
README [deleted file]
README.md [new file with mode: 0644]
apps/api/handlers.py
apps/catalogue/admin.py
apps/catalogue/fields.py
apps/catalogue/forms.py
apps/catalogue/locale/de/LC_MESSAGES/django.po [new file with mode: 0644]
apps/catalogue/locale/en/LC_MESSAGES/django.po [new file with mode: 0644]
apps/catalogue/locale/es/LC_MESSAGES/django.po [new file with mode: 0644]
apps/catalogue/locale/fr/LC_MESSAGES/django.po [new file with mode: 0644]
apps/catalogue/locale/lt/LC_MESSAGES/django.po [new file with mode: 0644]
apps/catalogue/locale/pl/LC_MESSAGES/django.mo [new file with mode: 0644]
apps/catalogue/locale/pl/LC_MESSAGES/django.po [new file with mode: 0644]
apps/catalogue/locale/ru/LC_MESSAGES/django.po [new file with mode: 0644]
apps/catalogue/management/commands/importbooks.py
apps/catalogue/migrations/0006_add_author_death.py [new file with mode: 0644]
apps/catalogue/migrations/0007_add_bookstub.py [new file with mode: 0644]
apps/catalogue/migrations/0008_fix_shelf_book_count.py [new file with mode: 0644]
apps/catalogue/models.py
apps/catalogue/templatetags/catalogue_tags.py
apps/catalogue/urls.py
apps/catalogue/utils.py
apps/catalogue/views.py
apps/chunks/locale/de/LC_MESSAGES/django.po [new file with mode: 0644]
apps/chunks/locale/en/LC_MESSAGES/django.po [new file with mode: 0644]
apps/chunks/locale/es/LC_MESSAGES/django.po [new file with mode: 0644]
apps/chunks/locale/fr/LC_MESSAGES/django.po [new file with mode: 0644]
apps/chunks/locale/lt/LC_MESSAGES/django.po [new file with mode: 0644]
apps/chunks/locale/pl/LC_MESSAGES/django.po [new file with mode: 0644]
apps/chunks/locale/ru/LC_MESSAGES/django.po [new file with mode: 0644]
apps/lessons/admin.py
apps/lessons/locale/de/LC_MESSAGES/django.po [new file with mode: 0644]
apps/lessons/locale/en/LC_MESSAGES/django.po [new file with mode: 0644]
apps/lessons/locale/es/LC_MESSAGES/django.po [new file with mode: 0644]
apps/lessons/locale/fr/LC_MESSAGES/django.po [new file with mode: 0644]
apps/lessons/locale/lt/LC_MESSAGES/django.po [new file with mode: 0644]
apps/lessons/locale/pl/LC_MESSAGES/django.po [new file with mode: 0644]
apps/lessons/locale/ru/LC_MESSAGES/django.po [new file with mode: 0644]
apps/lessons/models.py
apps/lessons/urls.py
apps/lessons/views.py
apps/newtagging/locale/de/LC_MESSAGES/django.po [new file with mode: 0644]
apps/newtagging/locale/en/LC_MESSAGES/django.po [new file with mode: 0644]
apps/newtagging/locale/es/LC_MESSAGES/django.po [new file with mode: 0644]
apps/newtagging/locale/fr/LC_MESSAGES/django.po [new file with mode: 0644]
apps/newtagging/locale/lt/LC_MESSAGES/django.po [new file with mode: 0644]
apps/newtagging/locale/pl/LC_MESSAGES/django.po [new file with mode: 0644]
apps/newtagging/locale/ru/LC_MESSAGES/django.po [new file with mode: 0644]
apps/newtagging/managers.py
apps/newtagging/models.py
apps/newtagging/views.py
apps/pagination/__init__.py [deleted file]
apps/pagination/middleware.py [deleted file]
apps/pagination/models.py [deleted file]
apps/pagination/templates/pagination/pagination.html [deleted file]
apps/pagination/templatetags/__init__.py [deleted file]
apps/pagination/templatetags/pagination_tags.py [deleted file]
apps/pagination/tests.py [deleted file]
apps/sorl/__init__.py [deleted file]
apps/sorl/thumbnail/__init__.py [deleted file]
apps/sorl/thumbnail/base.py [deleted file]
apps/sorl/thumbnail/defaults.py [deleted file]
apps/sorl/thumbnail/fields.py [deleted file]
apps/sorl/thumbnail/main.py [deleted file]
apps/sorl/thumbnail/management/__init__.py [deleted file]
apps/sorl/thumbnail/management/commands/__init__.py [deleted file]
apps/sorl/thumbnail/management/commands/thumbnail_cleanup.py [deleted file]
apps/sorl/thumbnail/models.py [deleted file]
apps/sorl/thumbnail/processors.py [deleted file]
apps/sorl/thumbnail/templatetags/__init__.py [deleted file]
apps/sorl/thumbnail/templatetags/thumbnail.py [deleted file]
apps/sorl/thumbnail/tests/__init__.py [deleted file]
apps/sorl/thumbnail/tests/base.py [deleted file]
apps/sorl/thumbnail/tests/classes.py [deleted file]
apps/sorl/thumbnail/tests/fields.py [deleted file]
apps/sorl/thumbnail/tests/templatetags.py [deleted file]
apps/sorl/thumbnail/tests/utils.py [deleted file]
apps/sorl/thumbnail/utils.py [deleted file]
apps/south/__init__.py [deleted file]
apps/south/db/__init__.py [deleted file]
apps/south/db/generic.py [deleted file]
apps/south/db/mysql.py [deleted file]
apps/south/db/postgresql_psycopg2.py [deleted file]
apps/south/db/sql_server/__init__.py [deleted file]
apps/south/db/sql_server/pyodbc.py [deleted file]
apps/south/db/sqlite3.py [deleted file]
apps/south/docs/CHANGELOG [deleted file]
apps/south/docs/CONTRIBUTING [deleted file]
apps/south/docs/LICENSE [deleted file]
apps/south/docs/README [deleted file]
apps/south/management/__init__.py [deleted file]
apps/south/management/commands/__init__.py [deleted file]
apps/south/management/commands/migrate.py [deleted file]
apps/south/management/commands/startmigration.py [deleted file]
apps/south/management/commands/syncdb.py [deleted file]
apps/south/management/commands/test.py [deleted file]
apps/south/migration.py [deleted file]
apps/south/models.py [deleted file]
apps/south/setup.py [deleted file]
apps/south/tests/__init__.py [deleted file]
apps/south/tests/db.py [deleted file]
apps/south/tests/fakeapp/__init__.py [deleted file]
apps/south/tests/fakeapp/migrations/0001_spam.py [deleted file]
apps/south/tests/fakeapp/migrations/0002_eggs.py [deleted file]
apps/south/tests/fakeapp/migrations/0003_alter_spam.py [deleted file]
apps/south/tests/fakeapp/migrations/__init__.py [deleted file]
apps/south/tests/fakeapp/models.py [deleted file]
apps/south/tests/logic.py [deleted file]
apps/sponsors/__init__.py
apps/sponsors/admin.py
apps/sponsors/fields.py
apps/sponsors/locale/en/LC_MESSAGES/django.po [new file with mode: 0644]
apps/sponsors/locale/es/LC_MESSAGES/django.po [new file with mode: 0644]
apps/sponsors/locale/fr/LC_MESSAGES/django.po [new file with mode: 0644]
apps/sponsors/locale/lt/LC_MESSAGES/django.po [new file with mode: 0644]
apps/sponsors/locale/pl/LC_MESSAGES/django.po [new file with mode: 0644]
apps/sponsors/locale/ru/LC_MESSAGES/django.po [new file with mode: 0644]
apps/sponsors/models.py
apps/sponsors/processors.py
apps/sponsors/templatetags/sponsor_tags.py
apps/sponsors/widgets.py
deployment.py [new file with mode: 0644]
lib/feedparser.py [deleted file]
requirements.txt
scripts/conv_genre_families.py
scripts/import_links.py
scripts/irename.py
scripts/remove_duplicates.py
scripts/setmainpage.py
scripts/test_api.py
wolnelektury.vhost.tmpl [new file with mode: 0644]
wolnelektury.wsgi.tmpl [new file with mode: 0644]
wolnelektury/context_processors.py [new file with mode: 0644]
wolnelektury/locale/de/LC_MESSAGES/django.mo [new file with mode: 0644]
wolnelektury/locale/de/LC_MESSAGES/django.po [new file with mode: 0644]
wolnelektury/locale/en/LC_MESSAGES/django.mo [new file with mode: 0644]
wolnelektury/locale/en/LC_MESSAGES/django.po [new file with mode: 0644]
wolnelektury/locale/es/LC_MESSAGES/django.mo [new file with mode: 0644]
wolnelektury/locale/es/LC_MESSAGES/django.po [new file with mode: 0644]
wolnelektury/locale/fr/LC_MESSAGES/django.mo [new file with mode: 0644]
wolnelektury/locale/fr/LC_MESSAGES/django.po [new file with mode: 0644]
wolnelektury/locale/lt/LC_MESSAGES/django.mo [new file with mode: 0644]
wolnelektury/locale/lt/LC_MESSAGES/django.po [new file with mode: 0644]
wolnelektury/locale/pl/LC_MESSAGES/django.mo
wolnelektury/locale/pl/LC_MESSAGES/django.po
wolnelektury/locale/ru/LC_MESSAGES/django.mo [new file with mode: 0644]
wolnelektury/locale/ru/LC_MESSAGES/django.po [new file with mode: 0644]
wolnelektury/settings.py
wolnelektury/static/css/jquery.countdown.css [new file with mode: 0644]
wolnelektury/static/css/master.book.css
wolnelektury/static/css/master.css
wolnelektury/static/css/sponsors.css
wolnelektury/static/js/catalogue.js
wolnelektury/static/js/jquery.autocomplete.js
wolnelektury/static/js/jquery.countdown-de.js [new file with mode: 0644]
wolnelektury/static/js/jquery.countdown-es.js [new file with mode: 0644]
wolnelektury/static/js/jquery.countdown-fr.js [new file with mode: 0644]
wolnelektury/static/js/jquery.countdown-lt.js [new file with mode: 0644]
wolnelektury/static/js/jquery.countdown-pl.js [new file with mode: 0644]
wolnelektury/static/js/jquery.countdown-ru.js [new file with mode: 0644]
wolnelektury/static/js/jquery.countdown.js [new file with mode: 0644]
wolnelektury/static/js/jquery.js
wolnelektury/templates/1percent.html
wolnelektury/templates/404.html
wolnelektury/templates/500.html
wolnelektury/templates/503.html [new file with mode: 0644]
wolnelektury/templates/admin/base_site.html
wolnelektury/templates/admin/catalogue/book/change_list.html
wolnelektury/templates/auth/login.html
wolnelektury/templates/base.html
wolnelektury/templates/catalogue/book_detail.html
wolnelektury/templates/catalogue/book_fragments.html
wolnelektury/templates/catalogue/book_list.html
wolnelektury/templates/catalogue/book_sets.html
wolnelektury/templates/catalogue/book_short.html
wolnelektury/templates/catalogue/book_stub_detail.html [new file with mode: 0644]
wolnelektury/templates/catalogue/book_text.html
wolnelektury/templates/catalogue/breadcrumbs.html
wolnelektury/templates/catalogue/folded_tag_list.html
wolnelektury/templates/catalogue/fragment_sets.html
wolnelektury/templates/catalogue/fragment_short.html
wolnelektury/templates/catalogue/main_page.html
wolnelektury/templates/catalogue/pd_counter.html [new file with mode: 0644]
wolnelektury/templates/catalogue/search_multiple_hits.html [new file with mode: 0644]
wolnelektury/templates/catalogue/search_no_hits.html [new file with mode: 0644]
wolnelektury/templates/catalogue/search_too_short.html [new file with mode: 0644]
wolnelektury/templates/catalogue/tag_list.html
wolnelektury/templates/catalogue/tagged_object_list.html
wolnelektury/templates/catalogue/user_shelves.html
wolnelektury/templates/info/about_us.html
wolnelektury/templates/info/help_us.html
wolnelektury/templates/info/join_us.html [new file with mode: 0644]
wolnelektury/templates/info/voluntary_services.html
wolnelektury/templates/lessons/ajax_document_detail.html
wolnelektury/templates/lessons/document_detail.html
wolnelektury/templates/lessons/document_list.html
wolnelektury/templates/pagination/pagination.html
wolnelektury/urls.py
wolnelektury/wolnelektury.fcgi [deleted file]
wolnelektury/wolnelektury.wsgi [deleted file]

index f11a79b..d78f78f 100644 (file)
@@ -1,12 +1,21 @@
 localsettings.py
 dev.sqlite
+dev.db
 
 # Python garbage
 *.pyc
 .coverage
+pip-log.txt
 
 # Mac OS X garbage
 .DS_Store
 
 # Windows garbage
 thumbs.db
+
+# Eclipse
+.project
+.settings
+.pydevproject
+.tmp_*
+
diff --git a/LICENSE b/LICENSE
new file mode 100644 (file)
index 0000000..2def0e8
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,661 @@
+                    GNU AFFERO GENERAL PUBLIC LICENSE
+                       Version 3, 19 November 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU Affero General Public License is a free, copyleft license for
+software and other kinds of works, specifically designed to ensure
+cooperation with the community in the case of network server software.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+our General Public Licenses are intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  Developers that use our General Public Licenses protect your rights
+with two steps: (1) assert copyright on the software, and (2) offer
+you this License which gives you legal permission to copy, distribute
+and/or modify the software.
+
+  A secondary benefit of defending all users' freedom is that
+improvements made in alternate versions of the program, if they
+receive widespread use, become available for other developers to
+incorporate.  Many developers of free software are heartened and
+encouraged by the resulting cooperation.  However, in the case of
+software used on network servers, this result may fail to come about.
+The GNU General Public License permits making a modified version and
+letting the public access it on a server without ever releasing its
+source code to the public.
+
+  The GNU Affero General Public License is designed specifically to
+ensure that, in such cases, the modified source code becomes available
+to the community.  It requires the operator of a network server to
+provide the source code of the modified version running there to the
+users of that server.  Therefore, public use of a modified version, on
+a publicly accessible server, gives the public access to the source
+code of the modified version.
+
+  An older license, called the Affero General Public License and
+published by Affero, was designed to accomplish similar goals.  This is
+a different license, not a version of the Affero GPL, but Affero has
+released a new version of the Affero GPL which permits relicensing under
+this license.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU Affero General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Remote Network Interaction; Use with the GNU General Public License.
+
+  Notwithstanding any other provision of this License, if you modify the
+Program, your modified version must prominently offer all users
+interacting with it remotely through a computer network (if your version
+supports such interaction) an opportunity to receive the Corresponding
+Source of your version by providing access to the Corresponding Source
+from a network server at no charge, through some standard or customary
+means of facilitating copying of software.  This Corresponding Source
+shall include the Corresponding Source for any work covered by version 3
+of the GNU General Public License that is incorporated pursuant to the
+following paragraph.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the work with which it is combined will remain governed by version
+3 of the GNU General Public License.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU Affero General Public License from time to time.  Such new versions
+will be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU Affero General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU Affero General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU Affero General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If your software can interact with users remotely through a computer
+network, you should also make sure that it provides a way for users to
+get its source.  For example, if your program is a web application, its
+interface could display a "Source" link that leads users to an archive
+of the code.  There are many ways you could offer source, and different
+solutions will be better for different programs; see section 13 for the
+specific requirements.
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU AGPL, see
+<http://www.gnu.org/licenses/>.
\ No newline at end of file
diff --git a/NOTICE b/NOTICE
new file mode 100644 (file)
index 0000000..7bd9844
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1,19 @@
+    
+    FNP Wolnelektury
+
+    Copyright © 2010 Fundacja Nowoczesna Polska <fundacja@nowoczesnapolska.org.pl>
+    
+    For full list of contributors see AUTHORS file. 
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
\ No newline at end of file
diff --git a/README b/README
deleted file mode 100644 (file)
index fa3064e..0000000
--- a/README
+++ /dev/null
@@ -1,93 +0,0 @@
-Dependencies
-============
-
- * [Django 1.0](http://djangoproject.com/) (application framework)
- * [lxml 2.0.0](http://codespeak.net/lxml/) (for importing books)
- * [librarian 1.1](http://redmine.nowoczesnapolska.org.pl/projects/show/librarian) (for importing books)
- * Python libraries from lib directory
- * Django applications from apps directory
-
-
-How to deploy
-=============
-
-Just execute this commands:
-    
-    cd wolnelektury.pl
-    source setpythonpath.sh
-    cd wolnelektury
-    ./manage.py syncdbc
-    ./manage.py importbooks ../books/01 ../books/02 ../books/03
-    ../setmainpage.py
-
-[TODO]
-
-
-Używany kod open-source
-=======================
-
-django
-------
- - Źródła: [djangoproject.com](http://www.djangoproject.com/)
- - Autorzy: [wielu autorów](http://code.djangoproject.com/browser/django/trunk/AUTHORS)
- - Licencja: [BSD License](http://code.djangoproject.com/browser/django/trunk/LICENSE)
- - Typ: framework
-
-django-chunks
--------------
- - Źródła: [Google Code](http://code.google.com/p/django-chunks/)
- - Autorzy: Clint Ecker <clintecker@gmail.com>
- - Licencja: [New BSD License](http://www.opensource.org/licenses/bsd-license.php)
- - Typ: biblioteka (aplikacja django)
-
-django-pagination
------------------
- - Źródła: [Google Code](http://code.google.com/p/django-pagination/)
- - Autorzy: James Tauber <jtauber@gmail.com>, leidel@gmail.com
- - Licencja: [New BSD License](http://www.opensource.org/licenses/bsd-license.php)
- - Typ: biblioteka (aplikacja django)
-
-django-compress
----------------
- - Źródła: [Google Code](http://code.google.com/p/django-compress/)
- - Autorzy: Andreas Pelme <andreas.pelme@gmail.com>
- - Licencja: [MIT License](http://www.opensource.org/licenses/mit-license.php)
- - Typ: biblioteka (aplikacja django)
-
-django-newtagging
------------------
- - Źródła: [BitBucket](http://www.bitbucket.org/zuber/django-newtagging/)
- - Autorzy: Marek Stępniowski <marek@stepniowski.com>
- - Licencja: [MIT License](http://www.opensource.org/licenses/mit-license.php)
- - Typ: biblioteka (aplikacja django)
- - Nota: Aplikacja wzorowana na [django-tagging](http://code.google.com/p/django-tagging/), która jest również wydana na licencji [MIT](http://www.opensource.org/licenses/mit-license.php) Około połowa kodu jest dzielona.
-
-south
------
-- Źródła: [aercode.org](http://south.aeracode.org/)
-- Autorzy: Andrew Godwin <andrew@aeracode.org>, Andy McCurdy <sedrik@gmail.com>
-- Licencja: [Apache License 2.0](http://www.opensource.org/licenses/apache2.0.php)
-- Typ: biblioteka (aplikacja django)
-
-feedparser
-----------
- - Źródła: [Google Code](http://code.google.com/p/feedparser/)
- - Autorzy: Mark Pilgrim <pilgrim@gmail.com>
- - Licencja: [MIT License](http://www.opensource.org/licenses/mit-license.php)
- - Typ: biblioteka
-
-markupstring
-------------
- - Żródła: [ASPN Cookbook](http://code.activestate.com/recipes/389023/)
- - Autorzy: Thomas Hinkle
- - Licencja: [MIT License](http://code.activestate.com/help/terms/)
- - Typ: biblioteka
- - Nota: Zmienione przez Marka Stępniowskiego <marek@stepniowski.com> tak, żeby akceptowało ciągi znaków Unicode
-lxml
-----
- - Żródła: [codespeak.net](http://codespeak.net/lxml/index.html#download)
- - Autorzy: [wielu autorów](http://codespeak.net/lxml/credits.html)
- - Licencja: [BSD License](http://codespeak.net/lxml/index.html#license)
- - Typ: biblioteka
-
diff --git a/README.md b/README.md
new file mode 100644 (file)
index 0000000..725dc7c
--- /dev/null
+++ b/README.md
@@ -0,0 +1,151 @@
+License
+-------
+
+  ![AGPL Logo](http://www.gnu.org/graphics/agplv3-155x51.png)
+    
+    Copyright © 2008,2009,2010 Fundacja Nowoczesna Polska <fundacja@nowoczesnapolska.org.pl>
+    
+    For full list of contributors see AUTHORS section at the end. 
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+    
+Dependencies
+============
+
+ * All packages listed in requirements.txt
+ * Python libraries from lib directory
+ * Django applications from apps directory
+
+How to deploy (development version)
+=============
+
+1. Checkout the source code from Github
+2. Install libraries (we recommend using pip):
+
+    pip install -r requirements.txt
+    
+3. Setup your local configuration based on settings.py. You need to generate a new SECRET_KEY, database stuff and domain related stuff.
+4. Populate database:
+    
+    ./wolnelektury/manage.py syncdb
+    ./wolnelektury/manage.py migrate
+
+5. Run the server
+
+   ./wolnelektury/manage.py runserver
+
+    
+6. Import some books which are available on http://www.wolnelektury.pl or on bitbucket mirror: http://bitbucket.org/lqc/wlbooks/
+   If you use Bitbucket, you also need Mercurial to fetch books (you can install it using: pip install mercurial).
+   After downloading books, log into administration, go to Books and choose 'Browse' to select book file,
+   then fire 'Import book' to upload it. Some books have invalid XML, so you can get an error
+   (just ignore it and look for other books).
+   
+7. We provide localization of the software in following languages: Polish, Russian, German, English, Spanish, French and Lithuanian.
+   Translation strings are based on gettext and can be found under 'locale' dir.
+   There are also JavaScript files for jQuery countdown plugin (static/js/jquery.countdown-*.js).
+
+Full list of used open-source software
+======================================
+
+External
+--------
+
+django
+--------
+ - Source: [djangoproject.com](http://www.djangoproject.com/)
+ - Authors: [many authors](http://code.djangoproject.com/browser/django/trunk/AUTHORS)
+ - License: [BSD License](http://code.djangoproject.com/browser/django/trunk/LICENSE)
+ - Type: framework
+
+django-pagination
+-----------------
+ - Source: [Google Code](http://code.google.com/p/django-pagination/)
+ - Authors: James Tauber <jtauber@gmail.com>, leidel@gmail.com
+ - License: [New BSD License](http://www.opensource.org/licenses/bsd-license.php)
+ - Type: library (django application)
+
+django-rosetta
+-----------------
+ - Source: [Google Code](http://code.google.com/p/django-rosetta/)
+ - Authors: James Tauber <jtauber@gmail.com>, leidel@gmail.com
+ - License: [MIT License](http://www.opensource.org/licenses/mit-license.php)
+ - Type: library (django application)
+
+Django South
+------------
+- Source: [aercode.org](http://south.aeracode.org/)
+- Authors: Andrew Godwin <andrew@aeracode.org>, Andy McCurdy <sedrik@gmail.com>
+- License: [Apache License 2.0](http://www.opensource.org/licenses/apache2.0.php)
+- Type: library (django application)
+
+lxml
+---------
+ - Source: [codespeak.net](http://codespeak.net/lxml/index.html#download)
+ - Authors: [many authors](http://codespeak.net/lxml/credits.html)
+ - License: [BSD License](http://codespeak.net/lxml/index.html#license)
+ - Type: library
+feedparser
+----------
+ - Source: [Google Code](http://code.google.com/p/feedparser/)
+ - Authors: Mark Pilgrim <pilgrim@gmail.com>
+ - License: [MIT License](http://www.opensource.org/licenses/mit-license.php)
+ - Type: library
+
+
+Internal (means we hacked on sources of those): 
+---------
+django-compress
+---------------
+ - Source: [Google Code](http://code.google.com/p/django-compress/)
+ - Authors: Andreas Pelme <andreas.pelme@gmail.com>
+ - License: [MIT License](http://www.opensource.org/licenses/mit-license.php)
+ - Type: library (Django application)
+ django-chunks
+-------------
+ - Source: [Google Code](http://code.google.com/p/django-chunks/)
+ - Authors: Clint Ecker <clintecker@gmail.com>
+ - License: [New BSD License](http://www.opensource.org/licenses/bsd-license.php)
+ - Type: library (Django application)
+django-newtagging
+-----------------
+ - Source: [BitBucket](http://www.bitbucket.org/zuber/django-newtagging/)
+ - Authors: Marek Stępniowski <marek@stepniowski.com>
+ - License: [MIT License](http://www.opensource.org/licenses/mit-license.php)
+ - Type: library (Django aplication)
+ - Notes: Aplication based on  [django-tagging](http://code.google.com/p/django-tagging/), also [MIT](http://www.opensource.org/licenses/mit-license.php) license.
+django-piston (0.2.3rc)
+------------------------
+ - http://bitbucket.org/jespern/django-piston/wiki/Home
+
+markupstring
+------------
+ - Source: [ASPN Cookbook](http://code.activestate.com/recipes/389023/)
+ - Authors: Thomas Hinkle
+ - License: [MIT License](http://code.activestate.com/help/terms/)
+ - Type: library
+ - Notes: Patched by Marek Stępniowski <marek@stepniowski.com> to accept Unicode strings
+Authors
+=======
+ * Marek Stępniowski  <marek@stepniowski.com>
+ * Łukasz Rekucki <lrekucki@gmail.com>
index 03115d8..799b608 100644 (file)
@@ -1,3 +1,7 @@
+# -*- coding: utf-8 -*-
+# This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
 from django.shortcuts import get_object_or_404
 from django.contrib.auth.decorators import login_required, user_passes_test
 from piston.handler import BaseHandler
index 9f1770d..8a718ff 100644 (file)
@@ -1,8 +1,11 @@
 # -*- coding: utf-8 -*-
+# This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
 from django.contrib import admin
 
 from newtagging.admin import TaggableModelAdmin
-from catalogue.models import Tag, Book, Fragment
+from catalogue.models import Tag, Book, Fragment, BookStub
 
 
 class TagAdmin(admin.ModelAdmin):
@@ -32,6 +35,17 @@ class FragmentAdmin(TaggableModelAdmin):
     ordering = ('book', 'anchor',)
 
 
+class BookStubAdmin(admin.ModelAdmin):
+    # tag_model = Tag
+    
+    list_display = ('title', 'author', 'slug','pd')
+    search_fields = ('title','author')
+    ordering = ('title',)
+
+    prepopulated_fields = {'slug': ('title',)}
+
+
+admin.site.register(BookStub, BookStubAdmin)
 admin.site.register(Tag, TagAdmin)
 admin.site.register(Book, BookAdmin)
 admin.site.register(Fragment, FragmentAdmin)
index eedff67..0478f9d 100644 (file)
@@ -1,4 +1,7 @@
 # -*- coding: utf-8 -*-
+# This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
 import datetime
 
 from django.conf import settings
@@ -10,6 +13,7 @@ from django.forms.util import smart_unicode
 from django.utils import simplejson as json
 from django.utils.html import escape
 from django.utils.safestring import mark_safe
+from django.utils.translation import ugettext_lazy as _
 
 
 class JSONEncoder(json.JSONEncoder):
@@ -39,7 +43,7 @@ class JSONFormField(forms.CharField):
             loads(value)
             return value
         except ValueError, e:
-            raise forms.ValidationError('Enter a valid JSON value. Error: %s' % e)
+            raise forms.ValidationError(_('Enter a valid JSON value. Error: %s') % e)
 
 
 class JSONField(models.TextField):
@@ -80,7 +84,7 @@ class JQueryAutoCompleteWidget(forms.TextInput):
         if self.options:
             options += ', %s' % self.options
         
-        return u'$(\'#%s\').autocomplete(%s%s);' % (field_id, source, options)
+        return u'$(\'#%s\').autocomplete(%s%s).result(autocomplete_result_handler);' % (field_id, source, options)
     
     def render(self, name, value=None, attrs=None):
         final_attrs = self.build_attrs(attrs, name=name)
index bf6b0df..076282b 100644 (file)
@@ -1,5 +1,9 @@
 # -*- coding: utf-8 -*-
+# This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
 from django import forms
+from django.utils.translation import ugettext_lazy as _
 from slughifi import slughifi
 
 from catalogue.models import Tag, Book
@@ -15,13 +19,13 @@ class BookImportForm(forms.Form):
 
 
 class SearchForm(forms.Form):
-    q = JQueryAutoCompleteField('/katalog/tags/', {'minChars': 2, 'selectFirst': True, 'cacheLength': 50})
+    q = JQueryAutoCompleteField('/katalog/tags/', {'minChars': 2, 'selectFirst': True, 'cacheLength': 50, 'matchContains': "word"})
     tags = forms.CharField(widget=forms.HiddenInput, required=False)
     
     def __init__(self, *args, **kwargs):
         tags = kwargs.pop('tags', [])
         super(SearchForm, self).__init__(*args, **kwargs)
-        self.fields['q'].widget.attrs['title'] = u'tytuł, autor, motyw/temat, epoka, rodzaj, gatunek'
+        self.fields['q'].widget.attrs['title'] = _('title, author, theme/topic, epoch, kind, genre')
         self.fields['tags'].initial = '/'.join(tag.slug for tag in Tag.get_tag_list(tags))
 
 
@@ -37,7 +41,7 @@ class ObjectSetsForm(forms.Form):
     def __init__(self, obj, user, *args, **kwargs):        
         super(ObjectSetsForm, self).__init__(*args, **kwargs)
         self.fields['set_ids'] = forms.MultipleChoiceField(
-            label=u'Półki',
+            label=_('Shelves'),
             required=False,
             choices=[(tag.id, "%s (%s)" % (tag.name, tag.book_count)) for tag in Tag.objects.filter(category='set', user=user)],
             initial=[tag.id for tag in obj.tags.filter(category='set', user=user)],
@@ -50,7 +54,7 @@ class NewSetForm(forms.Form):
     
     def __init__(self, *args, **kwargs):
         super(NewSetForm, self).__init__(*args, **kwargs)
-        self.fields['name'].widget.attrs['title'] = u'nazwa nowej półki'
+        self.fields['name'].widget.attrs['title'] = _('Name of the new shelf')
         
     def save(self, user, commit=True):
         name = self.cleaned_data['name']
diff --git a/apps/catalogue/locale/de/LC_MESSAGES/django.po b/apps/catalogue/locale/de/LC_MESSAGES/django.po
new file mode 100644 (file)
index 0000000..5362b95
--- /dev/null
@@ -0,0 +1,258 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2010-05-18 10:57+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: fields.py:45
+#, python-format
+msgid "Enter a valid JSON value. Error: %s"
+msgstr ""
+
+#: forms.py:28
+msgid "title, author, theme/topic, epoch, kind, genre"
+msgstr ""
+
+#: forms.py:44
+msgid "Shelves"
+msgstr ""
+
+#: forms.py:57
+msgid "Name of the new shelf"
+msgstr ""
+
+#: models.py:24 models.py:363
+msgid "author"
+msgstr ""
+
+#: models.py:25
+msgid "epoch"
+msgstr ""
+
+#: models.py:26
+msgid "kind"
+msgstr ""
+
+#: models.py:27
+msgid "genre"
+msgstr ""
+
+#: models.py:28
+msgid "theme"
+msgstr ""
+
+#: models.py:29
+msgid "set"
+msgstr ""
+
+#: models.py:30 models.py:322
+msgid "book"
+msgstr ""
+
+#: models.py:44 migrations/0001_initial.py:47
+msgid "name"
+msgstr ""
+
+#: models.py:45 models.py:103 models.py:365 migrations/0001_initial.py:19
+#: migrations/0001_initial.py:48
+msgid "slug"
+msgstr ""
+
+#: models.py:46 migrations/0001_initial.py:49
+msgid "sort key"
+msgstr ""
+
+#: models.py:47 migrations/0001_initial.py:50
+msgid "category"
+msgstr ""
+
+#: models.py:49 models.py:60 models.py:104 models.py:185
+#: migrations/0001_initial.py:20 migrations/0001_initial.py:51
+msgid "description"
+msgstr ""
+
+#: models.py:50 migrations/0001_initial.py:52
+msgid "main page"
+msgstr ""
+
+#: models.py:50 migrations/0001_initial.py:52
+msgid "Show tag on main page"
+msgstr ""
+
+#: models.py:53 migrations/0001_initial.py:54
+msgid "book count"
+msgstr ""
+
+#: models.py:54
+msgid "year of death"
+msgstr ""
+
+#: models.py:80 migrations/0001_initial.py:61
+msgid "tag"
+msgstr ""
+
+#: models.py:81
+msgid "tags"
+msgstr ""
+
+#: models.py:102 models.py:362 migrations/0001_initial.py:18
+msgid "title"
+msgstr ""
+
+#: models.py:105 migrations/0001_initial.py:21
+msgid "creation date"
+msgstr ""
+
+#: models.py:106 migrations/0001_initial.py:22
+msgid "short HTML"
+msgstr ""
+
+#: models.py:107 migrations/0001_initial.py:23
+msgid "parent number"
+msgstr ""
+
+#: models.py:108
+msgid "extra information"
+msgstr ""
+
+#: models.py:114 migrations/0001_initial.py:24
+msgid "XML file"
+msgstr ""
+
+#: models.py:115 migrations/0001_initial.py:25
+msgid "HTML file"
+msgstr ""
+
+#: models.py:116 migrations/0001_initial.py:26
+msgid "PDF file"
+msgstr ""
+
+#: models.py:117 migrations/0001_initial.py:27
+msgid "ODT file"
+msgstr ""
+
+#: models.py:118 migrations/0001_initial.py:28
+msgid "TXT file"
+msgstr ""
+
+#: models.py:119
+msgid "MP3 file"
+msgstr ""
+
+#: models.py:120
+msgid "OGG file"
+msgstr ""
+
+#: models.py:141
+msgid "Read online"
+msgstr ""
+
+#: models.py:221
+#, python-format
+msgid "Book %s already exists"
+msgstr ""
+
+#: models.py:264
+#, python-format
+msgid "Book with slug = \"%s\" does not exist."
+msgstr ""
+
+#: models.py:323
+msgid "books"
+msgstr ""
+
+#: models.py:357
+msgid "fragment"
+msgstr ""
+
+#: models.py:358
+msgid "fragments"
+msgstr ""
+
+#: models.py:364
+msgid "goes to public domain"
+msgstr ""
+
+#: models.py:366
+msgid "translator"
+msgstr ""
+
+#: models.py:367
+msgid "year of translator's death"
+msgstr ""
+
+#: models.py:385
+msgid "book stub"
+msgstr ""
+
+#: models.py:386
+msgid "book stubs"
+msgstr ""
+
+#: views.py:308
+msgid "<p>To maintain your shelves you need to be logged in.</p>"
+msgstr ""
+
+#: views.py:326
+msgid "<p>Shelves were sucessfully saved.</p>"
+msgstr ""
+
+#: views.py:350
+msgid "Book was successfully removed from the shelf"
+msgstr ""
+
+#: views.py:352
+msgid "This book is not on the shelf"
+msgstr ""
+
+#: views.py:448
+#, python-format
+msgid "<p>Shelf <strong>%s</strong> was successfully created</p>"
+msgstr ""
+
+#: views.py:463
+#, python-format
+msgid "<p>Shelf <strong>%s</strong> was successfully removed</p>"
+msgstr ""
+
+#: views.py:522
+#, python-format
+msgid "Today is %(month)s, %(day)s."
+msgstr ""
+
+#: views.py:523
+#, python-format
+msgid ""
+"An error occurred: %(exception)s\n"
+"\n"
+"%(tb)s"
+msgstr ""
+
+#: views.py:524
+msgid "Book imported successfully"
+msgstr ""
+
+#: views.py:526
+#, python-format
+msgid "Error importing file: %r"
+msgstr ""
+
+#: migrations/0001_initial.py:62
+msgid "content type"
+msgstr ""
+
+#: migrations/0001_initial.py:63
+msgid "object id"
+msgstr ""
diff --git a/apps/catalogue/locale/en/LC_MESSAGES/django.po b/apps/catalogue/locale/en/LC_MESSAGES/django.po
new file mode 100644 (file)
index 0000000..5362b95
--- /dev/null
@@ -0,0 +1,258 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2010-05-18 10:57+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: fields.py:45
+#, python-format
+msgid "Enter a valid JSON value. Error: %s"
+msgstr ""
+
+#: forms.py:28
+msgid "title, author, theme/topic, epoch, kind, genre"
+msgstr ""
+
+#: forms.py:44
+msgid "Shelves"
+msgstr ""
+
+#: forms.py:57
+msgid "Name of the new shelf"
+msgstr ""
+
+#: models.py:24 models.py:363
+msgid "author"
+msgstr ""
+
+#: models.py:25
+msgid "epoch"
+msgstr ""
+
+#: models.py:26
+msgid "kind"
+msgstr ""
+
+#: models.py:27
+msgid "genre"
+msgstr ""
+
+#: models.py:28
+msgid "theme"
+msgstr ""
+
+#: models.py:29
+msgid "set"
+msgstr ""
+
+#: models.py:30 models.py:322
+msgid "book"
+msgstr ""
+
+#: models.py:44 migrations/0001_initial.py:47
+msgid "name"
+msgstr ""
+
+#: models.py:45 models.py:103 models.py:365 migrations/0001_initial.py:19
+#: migrations/0001_initial.py:48
+msgid "slug"
+msgstr ""
+
+#: models.py:46 migrations/0001_initial.py:49
+msgid "sort key"
+msgstr ""
+
+#: models.py:47 migrations/0001_initial.py:50
+msgid "category"
+msgstr ""
+
+#: models.py:49 models.py:60 models.py:104 models.py:185
+#: migrations/0001_initial.py:20 migrations/0001_initial.py:51
+msgid "description"
+msgstr ""
+
+#: models.py:50 migrations/0001_initial.py:52
+msgid "main page"
+msgstr ""
+
+#: models.py:50 migrations/0001_initial.py:52
+msgid "Show tag on main page"
+msgstr ""
+
+#: models.py:53 migrations/0001_initial.py:54
+msgid "book count"
+msgstr ""
+
+#: models.py:54
+msgid "year of death"
+msgstr ""
+
+#: models.py:80 migrations/0001_initial.py:61
+msgid "tag"
+msgstr ""
+
+#: models.py:81
+msgid "tags"
+msgstr ""
+
+#: models.py:102 models.py:362 migrations/0001_initial.py:18
+msgid "title"
+msgstr ""
+
+#: models.py:105 migrations/0001_initial.py:21
+msgid "creation date"
+msgstr ""
+
+#: models.py:106 migrations/0001_initial.py:22
+msgid "short HTML"
+msgstr ""
+
+#: models.py:107 migrations/0001_initial.py:23
+msgid "parent number"
+msgstr ""
+
+#: models.py:108
+msgid "extra information"
+msgstr ""
+
+#: models.py:114 migrations/0001_initial.py:24
+msgid "XML file"
+msgstr ""
+
+#: models.py:115 migrations/0001_initial.py:25
+msgid "HTML file"
+msgstr ""
+
+#: models.py:116 migrations/0001_initial.py:26
+msgid "PDF file"
+msgstr ""
+
+#: models.py:117 migrations/0001_initial.py:27
+msgid "ODT file"
+msgstr ""
+
+#: models.py:118 migrations/0001_initial.py:28
+msgid "TXT file"
+msgstr ""
+
+#: models.py:119
+msgid "MP3 file"
+msgstr ""
+
+#: models.py:120
+msgid "OGG file"
+msgstr ""
+
+#: models.py:141
+msgid "Read online"
+msgstr ""
+
+#: models.py:221
+#, python-format
+msgid "Book %s already exists"
+msgstr ""
+
+#: models.py:264
+#, python-format
+msgid "Book with slug = \"%s\" does not exist."
+msgstr ""
+
+#: models.py:323
+msgid "books"
+msgstr ""
+
+#: models.py:357
+msgid "fragment"
+msgstr ""
+
+#: models.py:358
+msgid "fragments"
+msgstr ""
+
+#: models.py:364
+msgid "goes to public domain"
+msgstr ""
+
+#: models.py:366
+msgid "translator"
+msgstr ""
+
+#: models.py:367
+msgid "year of translator's death"
+msgstr ""
+
+#: models.py:385
+msgid "book stub"
+msgstr ""
+
+#: models.py:386
+msgid "book stubs"
+msgstr ""
+
+#: views.py:308
+msgid "<p>To maintain your shelves you need to be logged in.</p>"
+msgstr ""
+
+#: views.py:326
+msgid "<p>Shelves were sucessfully saved.</p>"
+msgstr ""
+
+#: views.py:350
+msgid "Book was successfully removed from the shelf"
+msgstr ""
+
+#: views.py:352
+msgid "This book is not on the shelf"
+msgstr ""
+
+#: views.py:448
+#, python-format
+msgid "<p>Shelf <strong>%s</strong> was successfully created</p>"
+msgstr ""
+
+#: views.py:463
+#, python-format
+msgid "<p>Shelf <strong>%s</strong> was successfully removed</p>"
+msgstr ""
+
+#: views.py:522
+#, python-format
+msgid "Today is %(month)s, %(day)s."
+msgstr ""
+
+#: views.py:523
+#, python-format
+msgid ""
+"An error occurred: %(exception)s\n"
+"\n"
+"%(tb)s"
+msgstr ""
+
+#: views.py:524
+msgid "Book imported successfully"
+msgstr ""
+
+#: views.py:526
+#, python-format
+msgid "Error importing file: %r"
+msgstr ""
+
+#: migrations/0001_initial.py:62
+msgid "content type"
+msgstr ""
+
+#: migrations/0001_initial.py:63
+msgid "object id"
+msgstr ""
diff --git a/apps/catalogue/locale/es/LC_MESSAGES/django.po b/apps/catalogue/locale/es/LC_MESSAGES/django.po
new file mode 100644 (file)
index 0000000..5362b95
--- /dev/null
@@ -0,0 +1,258 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2010-05-18 10:57+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: fields.py:45
+#, python-format
+msgid "Enter a valid JSON value. Error: %s"
+msgstr ""
+
+#: forms.py:28
+msgid "title, author, theme/topic, epoch, kind, genre"
+msgstr ""
+
+#: forms.py:44
+msgid "Shelves"
+msgstr ""
+
+#: forms.py:57
+msgid "Name of the new shelf"
+msgstr ""
+
+#: models.py:24 models.py:363
+msgid "author"
+msgstr ""
+
+#: models.py:25
+msgid "epoch"
+msgstr ""
+
+#: models.py:26
+msgid "kind"
+msgstr ""
+
+#: models.py:27
+msgid "genre"
+msgstr ""
+
+#: models.py:28
+msgid "theme"
+msgstr ""
+
+#: models.py:29
+msgid "set"
+msgstr ""
+
+#: models.py:30 models.py:322
+msgid "book"
+msgstr ""
+
+#: models.py:44 migrations/0001_initial.py:47
+msgid "name"
+msgstr ""
+
+#: models.py:45 models.py:103 models.py:365 migrations/0001_initial.py:19
+#: migrations/0001_initial.py:48
+msgid "slug"
+msgstr ""
+
+#: models.py:46 migrations/0001_initial.py:49
+msgid "sort key"
+msgstr ""
+
+#: models.py:47 migrations/0001_initial.py:50
+msgid "category"
+msgstr ""
+
+#: models.py:49 models.py:60 models.py:104 models.py:185
+#: migrations/0001_initial.py:20 migrations/0001_initial.py:51
+msgid "description"
+msgstr ""
+
+#: models.py:50 migrations/0001_initial.py:52
+msgid "main page"
+msgstr ""
+
+#: models.py:50 migrations/0001_initial.py:52
+msgid "Show tag on main page"
+msgstr ""
+
+#: models.py:53 migrations/0001_initial.py:54
+msgid "book count"
+msgstr ""
+
+#: models.py:54
+msgid "year of death"
+msgstr ""
+
+#: models.py:80 migrations/0001_initial.py:61
+msgid "tag"
+msgstr ""
+
+#: models.py:81
+msgid "tags"
+msgstr ""
+
+#: models.py:102 models.py:362 migrations/0001_initial.py:18
+msgid "title"
+msgstr ""
+
+#: models.py:105 migrations/0001_initial.py:21
+msgid "creation date"
+msgstr ""
+
+#: models.py:106 migrations/0001_initial.py:22
+msgid "short HTML"
+msgstr ""
+
+#: models.py:107 migrations/0001_initial.py:23
+msgid "parent number"
+msgstr ""
+
+#: models.py:108
+msgid "extra information"
+msgstr ""
+
+#: models.py:114 migrations/0001_initial.py:24
+msgid "XML file"
+msgstr ""
+
+#: models.py:115 migrations/0001_initial.py:25
+msgid "HTML file"
+msgstr ""
+
+#: models.py:116 migrations/0001_initial.py:26
+msgid "PDF file"
+msgstr ""
+
+#: models.py:117 migrations/0001_initial.py:27
+msgid "ODT file"
+msgstr ""
+
+#: models.py:118 migrations/0001_initial.py:28
+msgid "TXT file"
+msgstr ""
+
+#: models.py:119
+msgid "MP3 file"
+msgstr ""
+
+#: models.py:120
+msgid "OGG file"
+msgstr ""
+
+#: models.py:141
+msgid "Read online"
+msgstr ""
+
+#: models.py:221
+#, python-format
+msgid "Book %s already exists"
+msgstr ""
+
+#: models.py:264
+#, python-format
+msgid "Book with slug = \"%s\" does not exist."
+msgstr ""
+
+#: models.py:323
+msgid "books"
+msgstr ""
+
+#: models.py:357
+msgid "fragment"
+msgstr ""
+
+#: models.py:358
+msgid "fragments"
+msgstr ""
+
+#: models.py:364
+msgid "goes to public domain"
+msgstr ""
+
+#: models.py:366
+msgid "translator"
+msgstr ""
+
+#: models.py:367
+msgid "year of translator's death"
+msgstr ""
+
+#: models.py:385
+msgid "book stub"
+msgstr ""
+
+#: models.py:386
+msgid "book stubs"
+msgstr ""
+
+#: views.py:308
+msgid "<p>To maintain your shelves you need to be logged in.</p>"
+msgstr ""
+
+#: views.py:326
+msgid "<p>Shelves were sucessfully saved.</p>"
+msgstr ""
+
+#: views.py:350
+msgid "Book was successfully removed from the shelf"
+msgstr ""
+
+#: views.py:352
+msgid "This book is not on the shelf"
+msgstr ""
+
+#: views.py:448
+#, python-format
+msgid "<p>Shelf <strong>%s</strong> was successfully created</p>"
+msgstr ""
+
+#: views.py:463
+#, python-format
+msgid "<p>Shelf <strong>%s</strong> was successfully removed</p>"
+msgstr ""
+
+#: views.py:522
+#, python-format
+msgid "Today is %(month)s, %(day)s."
+msgstr ""
+
+#: views.py:523
+#, python-format
+msgid ""
+"An error occurred: %(exception)s\n"
+"\n"
+"%(tb)s"
+msgstr ""
+
+#: views.py:524
+msgid "Book imported successfully"
+msgstr ""
+
+#: views.py:526
+#, python-format
+msgid "Error importing file: %r"
+msgstr ""
+
+#: migrations/0001_initial.py:62
+msgid "content type"
+msgstr ""
+
+#: migrations/0001_initial.py:63
+msgid "object id"
+msgstr ""
diff --git a/apps/catalogue/locale/fr/LC_MESSAGES/django.po b/apps/catalogue/locale/fr/LC_MESSAGES/django.po
new file mode 100644 (file)
index 0000000..5362b95
--- /dev/null
@@ -0,0 +1,258 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2010-05-18 10:57+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: fields.py:45
+#, python-format
+msgid "Enter a valid JSON value. Error: %s"
+msgstr ""
+
+#: forms.py:28
+msgid "title, author, theme/topic, epoch, kind, genre"
+msgstr ""
+
+#: forms.py:44
+msgid "Shelves"
+msgstr ""
+
+#: forms.py:57
+msgid "Name of the new shelf"
+msgstr ""
+
+#: models.py:24 models.py:363
+msgid "author"
+msgstr ""
+
+#: models.py:25
+msgid "epoch"
+msgstr ""
+
+#: models.py:26
+msgid "kind"
+msgstr ""
+
+#: models.py:27
+msgid "genre"
+msgstr ""
+
+#: models.py:28
+msgid "theme"
+msgstr ""
+
+#: models.py:29
+msgid "set"
+msgstr ""
+
+#: models.py:30 models.py:322
+msgid "book"
+msgstr ""
+
+#: models.py:44 migrations/0001_initial.py:47
+msgid "name"
+msgstr ""
+
+#: models.py:45 models.py:103 models.py:365 migrations/0001_initial.py:19
+#: migrations/0001_initial.py:48
+msgid "slug"
+msgstr ""
+
+#: models.py:46 migrations/0001_initial.py:49
+msgid "sort key"
+msgstr ""
+
+#: models.py:47 migrations/0001_initial.py:50
+msgid "category"
+msgstr ""
+
+#: models.py:49 models.py:60 models.py:104 models.py:185
+#: migrations/0001_initial.py:20 migrations/0001_initial.py:51
+msgid "description"
+msgstr ""
+
+#: models.py:50 migrations/0001_initial.py:52
+msgid "main page"
+msgstr ""
+
+#: models.py:50 migrations/0001_initial.py:52
+msgid "Show tag on main page"
+msgstr ""
+
+#: models.py:53 migrations/0001_initial.py:54
+msgid "book count"
+msgstr ""
+
+#: models.py:54
+msgid "year of death"
+msgstr ""
+
+#: models.py:80 migrations/0001_initial.py:61
+msgid "tag"
+msgstr ""
+
+#: models.py:81
+msgid "tags"
+msgstr ""
+
+#: models.py:102 models.py:362 migrations/0001_initial.py:18
+msgid "title"
+msgstr ""
+
+#: models.py:105 migrations/0001_initial.py:21
+msgid "creation date"
+msgstr ""
+
+#: models.py:106 migrations/0001_initial.py:22
+msgid "short HTML"
+msgstr ""
+
+#: models.py:107 migrations/0001_initial.py:23
+msgid "parent number"
+msgstr ""
+
+#: models.py:108
+msgid "extra information"
+msgstr ""
+
+#: models.py:114 migrations/0001_initial.py:24
+msgid "XML file"
+msgstr ""
+
+#: models.py:115 migrations/0001_initial.py:25
+msgid "HTML file"
+msgstr ""
+
+#: models.py:116 migrations/0001_initial.py:26
+msgid "PDF file"
+msgstr ""
+
+#: models.py:117 migrations/0001_initial.py:27
+msgid "ODT file"
+msgstr ""
+
+#: models.py:118 migrations/0001_initial.py:28
+msgid "TXT file"
+msgstr ""
+
+#: models.py:119
+msgid "MP3 file"
+msgstr ""
+
+#: models.py:120
+msgid "OGG file"
+msgstr ""
+
+#: models.py:141
+msgid "Read online"
+msgstr ""
+
+#: models.py:221
+#, python-format
+msgid "Book %s already exists"
+msgstr ""
+
+#: models.py:264
+#, python-format
+msgid "Book with slug = \"%s\" does not exist."
+msgstr ""
+
+#: models.py:323
+msgid "books"
+msgstr ""
+
+#: models.py:357
+msgid "fragment"
+msgstr ""
+
+#: models.py:358
+msgid "fragments"
+msgstr ""
+
+#: models.py:364
+msgid "goes to public domain"
+msgstr ""
+
+#: models.py:366
+msgid "translator"
+msgstr ""
+
+#: models.py:367
+msgid "year of translator's death"
+msgstr ""
+
+#: models.py:385
+msgid "book stub"
+msgstr ""
+
+#: models.py:386
+msgid "book stubs"
+msgstr ""
+
+#: views.py:308
+msgid "<p>To maintain your shelves you need to be logged in.</p>"
+msgstr ""
+
+#: views.py:326
+msgid "<p>Shelves were sucessfully saved.</p>"
+msgstr ""
+
+#: views.py:350
+msgid "Book was successfully removed from the shelf"
+msgstr ""
+
+#: views.py:352
+msgid "This book is not on the shelf"
+msgstr ""
+
+#: views.py:448
+#, python-format
+msgid "<p>Shelf <strong>%s</strong> was successfully created</p>"
+msgstr ""
+
+#: views.py:463
+#, python-format
+msgid "<p>Shelf <strong>%s</strong> was successfully removed</p>"
+msgstr ""
+
+#: views.py:522
+#, python-format
+msgid "Today is %(month)s, %(day)s."
+msgstr ""
+
+#: views.py:523
+#, python-format
+msgid ""
+"An error occurred: %(exception)s\n"
+"\n"
+"%(tb)s"
+msgstr ""
+
+#: views.py:524
+msgid "Book imported successfully"
+msgstr ""
+
+#: views.py:526
+#, python-format
+msgid "Error importing file: %r"
+msgstr ""
+
+#: migrations/0001_initial.py:62
+msgid "content type"
+msgstr ""
+
+#: migrations/0001_initial.py:63
+msgid "object id"
+msgstr ""
diff --git a/apps/catalogue/locale/lt/LC_MESSAGES/django.po b/apps/catalogue/locale/lt/LC_MESSAGES/django.po
new file mode 100644 (file)
index 0000000..5362b95
--- /dev/null
@@ -0,0 +1,258 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2010-05-18 10:57+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: fields.py:45
+#, python-format
+msgid "Enter a valid JSON value. Error: %s"
+msgstr ""
+
+#: forms.py:28
+msgid "title, author, theme/topic, epoch, kind, genre"
+msgstr ""
+
+#: forms.py:44
+msgid "Shelves"
+msgstr ""
+
+#: forms.py:57
+msgid "Name of the new shelf"
+msgstr ""
+
+#: models.py:24 models.py:363
+msgid "author"
+msgstr ""
+
+#: models.py:25
+msgid "epoch"
+msgstr ""
+
+#: models.py:26
+msgid "kind"
+msgstr ""
+
+#: models.py:27
+msgid "genre"
+msgstr ""
+
+#: models.py:28
+msgid "theme"
+msgstr ""
+
+#: models.py:29
+msgid "set"
+msgstr ""
+
+#: models.py:30 models.py:322
+msgid "book"
+msgstr ""
+
+#: models.py:44 migrations/0001_initial.py:47
+msgid "name"
+msgstr ""
+
+#: models.py:45 models.py:103 models.py:365 migrations/0001_initial.py:19
+#: migrations/0001_initial.py:48
+msgid "slug"
+msgstr ""
+
+#: models.py:46 migrations/0001_initial.py:49
+msgid "sort key"
+msgstr ""
+
+#: models.py:47 migrations/0001_initial.py:50
+msgid "category"
+msgstr ""
+
+#: models.py:49 models.py:60 models.py:104 models.py:185
+#: migrations/0001_initial.py:20 migrations/0001_initial.py:51
+msgid "description"
+msgstr ""
+
+#: models.py:50 migrations/0001_initial.py:52
+msgid "main page"
+msgstr ""
+
+#: models.py:50 migrations/0001_initial.py:52
+msgid "Show tag on main page"
+msgstr ""
+
+#: models.py:53 migrations/0001_initial.py:54
+msgid "book count"
+msgstr ""
+
+#: models.py:54
+msgid "year of death"
+msgstr ""
+
+#: models.py:80 migrations/0001_initial.py:61
+msgid "tag"
+msgstr ""
+
+#: models.py:81
+msgid "tags"
+msgstr ""
+
+#: models.py:102 models.py:362 migrations/0001_initial.py:18
+msgid "title"
+msgstr ""
+
+#: models.py:105 migrations/0001_initial.py:21
+msgid "creation date"
+msgstr ""
+
+#: models.py:106 migrations/0001_initial.py:22
+msgid "short HTML"
+msgstr ""
+
+#: models.py:107 migrations/0001_initial.py:23
+msgid "parent number"
+msgstr ""
+
+#: models.py:108
+msgid "extra information"
+msgstr ""
+
+#: models.py:114 migrations/0001_initial.py:24
+msgid "XML file"
+msgstr ""
+
+#: models.py:115 migrations/0001_initial.py:25
+msgid "HTML file"
+msgstr ""
+
+#: models.py:116 migrations/0001_initial.py:26
+msgid "PDF file"
+msgstr ""
+
+#: models.py:117 migrations/0001_initial.py:27
+msgid "ODT file"
+msgstr ""
+
+#: models.py:118 migrations/0001_initial.py:28
+msgid "TXT file"
+msgstr ""
+
+#: models.py:119
+msgid "MP3 file"
+msgstr ""
+
+#: models.py:120
+msgid "OGG file"
+msgstr ""
+
+#: models.py:141
+msgid "Read online"
+msgstr ""
+
+#: models.py:221
+#, python-format
+msgid "Book %s already exists"
+msgstr ""
+
+#: models.py:264
+#, python-format
+msgid "Book with slug = \"%s\" does not exist."
+msgstr ""
+
+#: models.py:323
+msgid "books"
+msgstr ""
+
+#: models.py:357
+msgid "fragment"
+msgstr ""
+
+#: models.py:358
+msgid "fragments"
+msgstr ""
+
+#: models.py:364
+msgid "goes to public domain"
+msgstr ""
+
+#: models.py:366
+msgid "translator"
+msgstr ""
+
+#: models.py:367
+msgid "year of translator's death"
+msgstr ""
+
+#: models.py:385
+msgid "book stub"
+msgstr ""
+
+#: models.py:386
+msgid "book stubs"
+msgstr ""
+
+#: views.py:308
+msgid "<p>To maintain your shelves you need to be logged in.</p>"
+msgstr ""
+
+#: views.py:326
+msgid "<p>Shelves were sucessfully saved.</p>"
+msgstr ""
+
+#: views.py:350
+msgid "Book was successfully removed from the shelf"
+msgstr ""
+
+#: views.py:352
+msgid "This book is not on the shelf"
+msgstr ""
+
+#: views.py:448
+#, python-format
+msgid "<p>Shelf <strong>%s</strong> was successfully created</p>"
+msgstr ""
+
+#: views.py:463
+#, python-format
+msgid "<p>Shelf <strong>%s</strong> was successfully removed</p>"
+msgstr ""
+
+#: views.py:522
+#, python-format
+msgid "Today is %(month)s, %(day)s."
+msgstr ""
+
+#: views.py:523
+#, python-format
+msgid ""
+"An error occurred: %(exception)s\n"
+"\n"
+"%(tb)s"
+msgstr ""
+
+#: views.py:524
+msgid "Book imported successfully"
+msgstr ""
+
+#: views.py:526
+#, python-format
+msgid "Error importing file: %r"
+msgstr ""
+
+#: migrations/0001_initial.py:62
+msgid "content type"
+msgstr ""
+
+#: migrations/0001_initial.py:63
+msgid "object id"
+msgstr ""
diff --git a/apps/catalogue/locale/pl/LC_MESSAGES/django.mo b/apps/catalogue/locale/pl/LC_MESSAGES/django.mo
new file mode 100644 (file)
index 0000000..fa865dc
Binary files /dev/null and b/apps/catalogue/locale/pl/LC_MESSAGES/django.mo differ
diff --git a/apps/catalogue/locale/pl/LC_MESSAGES/django.po b/apps/catalogue/locale/pl/LC_MESSAGES/django.po
new file mode 100644 (file)
index 0000000..dd45be0
--- /dev/null
@@ -0,0 +1,258 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+# 
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2010-05-18 10:57+0200\n"
+"PO-Revision-Date: 2010-05-19 14:20\n"
+"Last-Translator: <radoslaw.czajka@nowoczesnapolska.org.pl>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Translated-Using: django-rosetta 0.5.3\n"
+
+#: fields.py:45
+#, python-format
+msgid "Enter a valid JSON value. Error: %s"
+msgstr ""
+
+#: forms.py:28
+msgid "title, author, theme/topic, epoch, kind, genre"
+msgstr "tytuł, autor, motyw/temat, epoka, rodzaj, gatunek"
+
+#: forms.py:44
+msgid "Shelves"
+msgstr "Półki"
+
+#: forms.py:57
+msgid "Name of the new shelf"
+msgstr "nazwa nowej półki"
+
+#: models.py:24 models.py:363
+msgid "author"
+msgstr "autor"
+
+#: models.py:25
+msgid "epoch"
+msgstr "epoka"
+
+#: models.py:26
+msgid "kind"
+msgstr "rodzaj"
+
+#: models.py:27
+msgid "genre"
+msgstr "gatunek"
+
+#: models.py:28
+msgid "theme"
+msgstr "motyw"
+
+#: models.py:29
+msgid "set"
+msgstr "półka"
+
+#: models.py:30 models.py:322
+msgid "book"
+msgstr ""
+
+#: models.py:44 migrations/0001_initial.py:47
+msgid "name"
+msgstr ""
+
+#: models.py:45 models.py:103 models.py:365 migrations/0001_initial.py:19
+#: migrations/0001_initial.py:48
+msgid "slug"
+msgstr ""
+
+#: models.py:46 migrations/0001_initial.py:49
+msgid "sort key"
+msgstr ""
+
+#: models.py:47 migrations/0001_initial.py:50
+msgid "category"
+msgstr ""
+
+#: models.py:49 models.py:60 models.py:104 models.py:185
+#: migrations/0001_initial.py:20 migrations/0001_initial.py:51
+msgid "description"
+msgstr ""
+
+#: models.py:50 migrations/0001_initial.py:52
+msgid "main page"
+msgstr ""
+
+#: models.py:50 migrations/0001_initial.py:52
+msgid "Show tag on main page"
+msgstr ""
+
+#: models.py:53 migrations/0001_initial.py:54
+msgid "book count"
+msgstr "liczba książek"
+
+#: models.py:54
+msgid "year of death"
+msgstr "rok śmierci"
+
+#: models.py:80 migrations/0001_initial.py:61
+msgid "tag"
+msgstr "tag"
+
+#: models.py:81
+msgid "tags"
+msgstr "tagi"
+
+#: models.py:102 models.py:362 migrations/0001_initial.py:18
+msgid "title"
+msgstr "tytuł"
+
+#: models.py:105 migrations/0001_initial.py:21
+msgid "creation date"
+msgstr ""
+
+#: models.py:106 migrations/0001_initial.py:22
+msgid "short HTML"
+msgstr ""
+
+#: models.py:107 migrations/0001_initial.py:23
+msgid "parent number"
+msgstr ""
+
+#: models.py:108
+msgid "extra information"
+msgstr ""
+
+#: models.py:114 migrations/0001_initial.py:24
+msgid "XML file"
+msgstr "Plik XML"
+
+#: models.py:115 migrations/0001_initial.py:25
+msgid "HTML file"
+msgstr "Plik HTML"
+
+#: models.py:116 migrations/0001_initial.py:26
+msgid "PDF file"
+msgstr "Plik PDF"
+
+#: models.py:117 migrations/0001_initial.py:27
+msgid "ODT file"
+msgstr "Plik ODT"
+
+#: models.py:118 migrations/0001_initial.py:28
+msgid "TXT file"
+msgstr "Plik TXT"
+
+#: models.py:119
+msgid "MP3 file"
+msgstr "Plik MP3"
+
+#: models.py:120
+msgid "OGG file"
+msgstr "Plik OGG"
+
+#: models.py:141
+msgid "Read online"
+msgstr "Czytaj online"
+
+#: models.py:221
+#, python-format
+msgid "Book %s already exists"
+msgstr ""
+
+#: models.py:264
+#, python-format
+msgid "Book with slug = \"%s\" does not exist."
+msgstr ""
+
+#: models.py:323
+msgid "books"
+msgstr ""
+
+#: models.py:357
+msgid "fragment"
+msgstr ""
+
+#: models.py:358
+msgid "fragments"
+msgstr ""
+
+#: models.py:364
+msgid "goes to public domain"
+msgstr "trafia do domeny publicznej"
+
+#: models.py:366
+msgid "translator"
+msgstr "tłumacz"
+
+#: models.py:367
+msgid "year of translator's death"
+msgstr "rok śmierci tłumacza"
+
+#: models.py:385
+msgid "book stub"
+msgstr "zapowiedź książki"
+
+#: models.py:386
+msgid "book stubs"
+msgstr "zapowiedzi książek"
+
+#: views.py:308
+msgid "<p>To maintain your shelves you need to be logged in.</p>"
+msgstr "<p>Aby zarządzać swoimi półkami, musisz się zalogować.</p>"
+
+#: views.py:326
+msgid "<p>Shelves were sucessfully saved.</p>"
+msgstr "<p>Półki zostały zapisane.</p>"
+
+#: views.py:350
+msgid "Book was successfully removed from the shelf"
+msgstr "Usunięto"
+
+#: views.py:352
+msgid "This book is not on the shelf"
+msgstr "Książki nie ma na półce"
+
+#: views.py:448
+#, python-format
+msgid "<p>Shelf <strong>%s</strong> was successfully created</p>"
+msgstr "<p>Półka <strong>%s</strong> została utworzona</p>"
+
+#: views.py:463
+#, python-format
+msgid "<p>Shelf <strong>%s</strong> was successfully removed</p>"
+msgstr "<p>Półka <strong>%s</strong> została usunięta</p>"
+
+#: views.py:522
+#, python-format
+msgid "Today is %(month)s, %(day)s."
+msgstr ""
+
+#: views.py:523
+#, python-format
+msgid ""
+"An error occurred: %(exception)s\n"
+"\n"
+"%(tb)s"
+msgstr ""
+
+#: views.py:524
+msgid "Book imported successfully"
+msgstr ""
+
+#: views.py:526
+#, python-format
+msgid "Error importing file: %r"
+msgstr ""
+
+#: migrations/0001_initial.py:62
+msgid "content type"
+msgstr ""
+
+#: migrations/0001_initial.py:63
+msgid "object id"
+msgstr ""
diff --git a/apps/catalogue/locale/ru/LC_MESSAGES/django.po b/apps/catalogue/locale/ru/LC_MESSAGES/django.po
new file mode 100644 (file)
index 0000000..5362b95
--- /dev/null
@@ -0,0 +1,258 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2010-05-18 10:57+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: fields.py:45
+#, python-format
+msgid "Enter a valid JSON value. Error: %s"
+msgstr ""
+
+#: forms.py:28
+msgid "title, author, theme/topic, epoch, kind, genre"
+msgstr ""
+
+#: forms.py:44
+msgid "Shelves"
+msgstr ""
+
+#: forms.py:57
+msgid "Name of the new shelf"
+msgstr ""
+
+#: models.py:24 models.py:363
+msgid "author"
+msgstr ""
+
+#: models.py:25
+msgid "epoch"
+msgstr ""
+
+#: models.py:26
+msgid "kind"
+msgstr ""
+
+#: models.py:27
+msgid "genre"
+msgstr ""
+
+#: models.py:28
+msgid "theme"
+msgstr ""
+
+#: models.py:29
+msgid "set"
+msgstr ""
+
+#: models.py:30 models.py:322
+msgid "book"
+msgstr ""
+
+#: models.py:44 migrations/0001_initial.py:47
+msgid "name"
+msgstr ""
+
+#: models.py:45 models.py:103 models.py:365 migrations/0001_initial.py:19
+#: migrations/0001_initial.py:48
+msgid "slug"
+msgstr ""
+
+#: models.py:46 migrations/0001_initial.py:49
+msgid "sort key"
+msgstr ""
+
+#: models.py:47 migrations/0001_initial.py:50
+msgid "category"
+msgstr ""
+
+#: models.py:49 models.py:60 models.py:104 models.py:185
+#: migrations/0001_initial.py:20 migrations/0001_initial.py:51
+msgid "description"
+msgstr ""
+
+#: models.py:50 migrations/0001_initial.py:52
+msgid "main page"
+msgstr ""
+
+#: models.py:50 migrations/0001_initial.py:52
+msgid "Show tag on main page"
+msgstr ""
+
+#: models.py:53 migrations/0001_initial.py:54
+msgid "book count"
+msgstr ""
+
+#: models.py:54
+msgid "year of death"
+msgstr ""
+
+#: models.py:80 migrations/0001_initial.py:61
+msgid "tag"
+msgstr ""
+
+#: models.py:81
+msgid "tags"
+msgstr ""
+
+#: models.py:102 models.py:362 migrations/0001_initial.py:18
+msgid "title"
+msgstr ""
+
+#: models.py:105 migrations/0001_initial.py:21
+msgid "creation date"
+msgstr ""
+
+#: models.py:106 migrations/0001_initial.py:22
+msgid "short HTML"
+msgstr ""
+
+#: models.py:107 migrations/0001_initial.py:23
+msgid "parent number"
+msgstr ""
+
+#: models.py:108
+msgid "extra information"
+msgstr ""
+
+#: models.py:114 migrations/0001_initial.py:24
+msgid "XML file"
+msgstr ""
+
+#: models.py:115 migrations/0001_initial.py:25
+msgid "HTML file"
+msgstr ""
+
+#: models.py:116 migrations/0001_initial.py:26
+msgid "PDF file"
+msgstr ""
+
+#: models.py:117 migrations/0001_initial.py:27
+msgid "ODT file"
+msgstr ""
+
+#: models.py:118 migrations/0001_initial.py:28
+msgid "TXT file"
+msgstr ""
+
+#: models.py:119
+msgid "MP3 file"
+msgstr ""
+
+#: models.py:120
+msgid "OGG file"
+msgstr ""
+
+#: models.py:141
+msgid "Read online"
+msgstr ""
+
+#: models.py:221
+#, python-format
+msgid "Book %s already exists"
+msgstr ""
+
+#: models.py:264
+#, python-format
+msgid "Book with slug = \"%s\" does not exist."
+msgstr ""
+
+#: models.py:323
+msgid "books"
+msgstr ""
+
+#: models.py:357
+msgid "fragment"
+msgstr ""
+
+#: models.py:358
+msgid "fragments"
+msgstr ""
+
+#: models.py:364
+msgid "goes to public domain"
+msgstr ""
+
+#: models.py:366
+msgid "translator"
+msgstr ""
+
+#: models.py:367
+msgid "year of translator's death"
+msgstr ""
+
+#: models.py:385
+msgid "book stub"
+msgstr ""
+
+#: models.py:386
+msgid "book stubs"
+msgstr ""
+
+#: views.py:308
+msgid "<p>To maintain your shelves you need to be logged in.</p>"
+msgstr ""
+
+#: views.py:326
+msgid "<p>Shelves were sucessfully saved.</p>"
+msgstr ""
+
+#: views.py:350
+msgid "Book was successfully removed from the shelf"
+msgstr ""
+
+#: views.py:352
+msgid "This book is not on the shelf"
+msgstr ""
+
+#: views.py:448
+#, python-format
+msgid "<p>Shelf <strong>%s</strong> was successfully created</p>"
+msgstr ""
+
+#: views.py:463
+#, python-format
+msgid "<p>Shelf <strong>%s</strong> was successfully removed</p>"
+msgstr ""
+
+#: views.py:522
+#, python-format
+msgid "Today is %(month)s, %(day)s."
+msgstr ""
+
+#: views.py:523
+#, python-format
+msgid ""
+"An error occurred: %(exception)s\n"
+"\n"
+"%(tb)s"
+msgstr ""
+
+#: views.py:524
+msgid "Book imported successfully"
+msgstr ""
+
+#: views.py:526
+#, python-format
+msgid "Error importing file: %r"
+msgstr ""
+
+#: migrations/0001_initial.py:62
+msgid "content type"
+msgstr ""
+
+#: migrations/0001_initial.py:63
+msgid "object id"
+msgstr ""
index 0aa8081..52aa69f 100644 (file)
@@ -1,3 +1,7 @@
+# -*- coding: utf-8 -*-
+# This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
 import os
 import sys
 from optparse import make_option
diff --git a/apps/catalogue/migrations/0006_add_author_death.py b/apps/catalogue/migrations/0006_add_author_death.py
new file mode 100644 (file)
index 0000000..ef8491e
--- /dev/null
@@ -0,0 +1,13 @@
+# -*- coding: utf-8 -*-
+from south.db import db
+from django.db import models
+
+
+class Migration:    
+    def forwards(self):
+        db.add_column('catalogue_tag', 'death', models.IntegerField(blank=True,  null=True))
+    
+    def backwards(self):
+        db.delete_column('catalogue_tag', 'death')
+
+
diff --git a/apps/catalogue/migrations/0007_add_bookstub.py b/apps/catalogue/migrations/0007_add_bookstub.py
new file mode 100644 (file)
index 0000000..12de149
--- /dev/null
@@ -0,0 +1,127 @@
+# -*- coding: utf-8 -*-
+
+from south.db import db
+from django.db import models
+from catalogue.models import *
+
+class Migration:
+    
+    def forwards(self, orm):
+        
+        # Adding model 'BookStub'
+        db.create_table('catalogue_bookstub', (
+            ('id', orm['catalogue.BookStub:id']),
+            ('title', orm['catalogue.BookStub:title']),
+            ('author', orm['catalogue.BookStub:author']),
+            ('pd', orm['catalogue.BookStub:pd']),
+            ('slug', orm['catalogue.BookStub:slug']),
+            ('translator', orm['catalogue.BookStub:translator']),
+            ('translator_death', orm['catalogue.BookStub:translator_death']),
+        ))
+        db.send_create_signal('catalogue', ['BookStub'])
+        
+    
+    
+    def backwards(self, orm):
+        
+        # Deleting model 'BookStub'
+        db.delete_table('catalogue_bookstub')
+        
+    
+    
+    models = {
+        'auth.group': {
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+            'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'blank': 'True'})
+        },
+        'auth.permission': {
+            'Meta': {'unique_together': "(('content_type', 'codename'),)"},
+            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+        },
+        'auth.user': {
+            'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
+            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+            'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+            'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'blank': 'True'}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+        },
+        'catalogue.book': {
+            '_short_html': ('django.db.models.fields.TextField', [], {}),
+            'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
+            'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            'extra_info': ('JSONField', ["_('extra information')"], {}),
+            'gazeta_link': ('django.db.models.fields.CharField', [], {'max_length': '240', 'blank': 'True'}),
+            'html_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'mp3_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'blank': 'True'}),
+            'odt_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'blank': 'True'}),
+            'ogg_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'blank': 'True'}),
+            'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['catalogue.Book']"}),
+            'parent_number': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'pdf_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'blank': 'True'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '120', 'db_index': 'True'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '120'}),
+            'txt_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'blank': 'True'}),
+            'wiki_link': ('django.db.models.fields.CharField', [], {'max_length': '240', 'blank': 'True'}),
+            'xml_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'blank': 'True'})
+        },
+        'catalogue.bookstub': {
+            'author': ('django.db.models.fields.CharField', [], {'max_length': '120'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'pd': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '120', 'db_index': 'True'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '120'}),
+            'translator': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            'translator_death': ('django.db.models.fields.TextField', [], {'blank': 'True'})
+        },
+        'catalogue.fragment': {
+            '_short_html': ('django.db.models.fields.TextField', [], {}),
+            'anchor': ('django.db.models.fields.CharField', [], {'max_length': '120'}),
+            'book': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'fragments'", 'to': "orm['catalogue.Book']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'short_text': ('django.db.models.fields.TextField', [], {}),
+            'text': ('django.db.models.fields.TextField', [], {})
+        },
+        'catalogue.tag': {
+            'book_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'category': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'}),
+            'death': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+            'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            'gazeta_link': ('django.db.models.fields.CharField', [], {'max_length': '240', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'main_page': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True', 'blank': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '120', 'db_index': 'True'}),
+            'sort_key': ('django.db.models.fields.SlugField', [], {'max_length': '120', 'db_index': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
+            'wiki_link': ('django.db.models.fields.CharField', [], {'max_length': '240', 'blank': 'True'})
+        },
+        'catalogue.tagrelation': {
+            'Meta': {'unique_together': "(('tag', 'content_type', 'object_id'),)", 'db_table': "'catalogue_tag_relation'"},
+            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'object_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'items'", 'to': "orm['catalogue.Tag']"})
+        },
+        'contenttypes.contenttype': {
+            'Meta': {'unique_together': "(('app_label', 'model'),)", 'db_table': "'django_content_type'"},
+            'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        }
+    }
+    
+    complete_apps = ['catalogue']
diff --git a/apps/catalogue/migrations/0008_fix_shelf_book_count.py b/apps/catalogue/migrations/0008_fix_shelf_book_count.py
new file mode 100644 (file)
index 0000000..b4ee915
--- /dev/null
@@ -0,0 +1,18 @@
+# -*- coding: utf-8 -*-
+
+from south.db import db
+from django.db import models
+from catalogue.models import Tag, Book
+
+class Migration:
+    
+    def forwards(self):
+        "Write your forwards migration here"
+        for tag in Tag.objects.filter(user__isnull=False):
+            books = Tag.intermediary_table_model.objects.get_intersection_by_model(Book, [tag])
+            tag.book_count = len(books)
+            tag.save()
+    
+    def backwards(self, orm):
+        "Write your backwards migration here"
+        pass
index 2f4e7fe..59011fd 100644 (file)
@@ -1,4 +1,7 @@
 # -*- coding: utf-8 -*-
+# This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
 from django.db import models
 from django.db.models import permalink, Q
 from django.utils.translation import ugettext_lazy as _
@@ -7,6 +10,7 @@ from django.core.files import File
 from django.template.loader import render_to_string
 from django.utils.safestring import mark_safe
 from django.core.urlresolvers import reverse
+from datetime import datetime
 
 from newtagging.models import TagBase
 from newtagging import managers
@@ -47,6 +51,7 @@ class Tag(TagBase):
     
     user = models.ForeignKey(User, blank=True, null=True)
     book_count = models.IntegerField(_('book count'), default=0, blank=False, null=False)
+    death = models.IntegerField(_(u'year of death'), blank=True, null=True)
     gazeta_link = models.CharField(blank=True,  max_length=240)
     wiki_link = models.CharField(blank=True,  max_length=240)
     
@@ -55,6 +60,17 @@ class Tag(TagBase):
     has_description.short_description = _('description')
     has_description.boolean = True
 
+    def alive(self):
+        return self.death is None
+    
+    def in_pd(self):
+        """ tests whether an author is in public domain """
+        return self.death is not None and self.goes_to_pd() <= datetime.now().year
+    
+    def goes_to_pd(self):
+        """ calculates the year of public domain entry for an author """
+        return self.death + 71 if self.death is not None else None
+
     @permalink
     def get_absolute_url(self):
         return ('catalogue.views.tagged_object_list', [self.slug])
@@ -108,7 +124,6 @@ class Book(models.Model):
     objects = models.Manager()
     tagged = managers.ModelTaggedItemManager(Tag)
     tags = managers.TagDescriptor(Tag)
-
     
     @property
     def name(self):
@@ -123,7 +138,7 @@ class Book(models.Model):
 
             formats = []
             if self.html_file:
-                formats.append(u'<a href="%s">Czytaj online</a>' % reverse('book_text', kwargs={'slug': self.slug}))
+                formats.append(u'<a href="%s">%s</a>' % (reverse('book_text', kwargs={'slug': self.slug}), _('Read online')))
             if self.pdf_file:
                 formats.append(u'<a href="%s">PDF</a>' % self.pdf_file.url)
             if self.odt_file:
@@ -203,7 +218,7 @@ class Book(models.Model):
             book_shelves = []
         else:
             if not overwrite:
-                raise Book.AlreadyExists('Book %s already exists' % book_slug)
+                raise Book.AlreadyExists(_('Book %s already exists') % book_slug)
             # Save shelves for this book
             book_shelves = list(book.tags.filter(category='set'))
         
@@ -246,7 +261,7 @@ class Book(models.Model):
                     child_book.parent_number = n
                     child_book.save()
                 except Book.DoesNotExist, e:
-                    raise Book.DoesNotExist(u'Book with slug = "%s" does not exist.' % slug)
+                    raise Book.DoesNotExist(_('Book with slug = "%s" does not exist.') % slug)
         
         book_descendants = list(book.children.all())
         while len(book_descendants) > 0:
@@ -342,3 +357,30 @@ class Fragment(models.Model):
         verbose_name = _('fragment')
         verbose_name_plural = _('fragments')
 
+
+class BookStub(models.Model):
+    title = models.CharField(_('title'), max_length=120)
+    author = models.CharField(_('author'), max_length=120)
+    pd = models.IntegerField(_('goes to public domain'), null=True, blank=True)
+    slug = models.SlugField(_('slug'), max_length=120, unique=True, db_index=True)
+    translator = models.TextField(_('translator'), blank=True)
+    translator_death = models.TextField(_('year of translator\'s death'), blank=True)
+
+    def in_pd(self):
+        return self.pd is not None and self.pd <= datetime.now().year
+
+    @property
+    def name(self):
+        return self.title
+    
+    @permalink
+    def get_absolute_url(self):
+        return ('catalogue.views.book_detail', [self.slug])
+
+    def __unicode__(self):
+        return self.title
+    
+    class Meta:
+        ordering = ('title',)
+        verbose_name = _('book stub')
+        verbose_name_plural = _('book stubs')
\ No newline at end of file
index d3f608a..41be051 100644 (file)
@@ -1,4 +1,7 @@
 # -*- coding: utf-8 -*-
+# This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
 import feedparser
 import datetime
 
index 327e0fc..25389f4 100644 (file)
@@ -1,4 +1,7 @@
 # -*- coding: utf-8 -*-
+# This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
 from django.conf.urls.defaults import *
 
 
@@ -14,6 +17,9 @@ urlpatterns = patterns('catalogue.views',
     url(r'^polki/nowa/$', 'new_set', name='new_set'),
     url(r'^tags/$', 'tags_starting_with', name='hint'),
     url(r'^szukaj/$', 'search', name='search'),
+
+    # tools
+    url(r'^zegar', 'clock', name='clock'),
     
     # Public interface. Do not change this URLs.
     url(r'^lektura/(?P<slug>[a-zA-Z0-9-]+)\.html$', 'book_text', name='book_text'),
index 72b0973..36bd9e7 100644 (file)
@@ -1,4 +1,7 @@
 # -*- coding: utf-8 -*-
+# This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
 import random
 import time
 from base64 import urlsafe_b64encode
index 92aa3e0..cf1461f 100644 (file)
@@ -1,10 +1,15 @@
 # -*- coding: utf-8 -*-
+# This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
 import tempfile
 import zipfile
 import sys
 import pprint
 import traceback
+import re
 
+from django.conf import settings
 from django.template import RequestContext
 from django.shortcuts import render_to_response, get_object_or_404
 from django.http import HttpResponse, HttpResponseRedirect, Http404
@@ -18,7 +23,9 @@ from django.contrib.auth.forms import UserCreationForm, AuthenticationForm
 from django.utils import simplejson
 from django.utils.functional import Promise
 from django.utils.encoding import force_unicode
+from django.utils.http import urlquote_plus
 from django.views.decorators import cache
+from django.utils.translation import ugettext as _
 
 from catalogue import models
 from catalogue import forms
@@ -40,10 +47,10 @@ def main_page(request):
     if request.user.is_authenticated():
         shelves = models.Tag.objects.filter(category='set', user=request.user)
         new_set_form = forms.NewSetForm()
-    extra_where = 'NOT catalogue_tag.category = "set"'
+    extra_where = "NOT catalogue_tag.category = 'set'"
     tags = models.Tag.objects.usage_for_model(models.Book, counts=True, extra={'where': [extra_where]})
     fragment_tags = models.Tag.objects.usage_for_model(models.Fragment, counts=True,
-        extra={'where': ['catalogue_tag.category = "theme"'] + [extra_where]})
+        extra={'where': ["catalogue_tag.category = 'theme'"] + [extra_where]})
     categories = split_tags(tags)
     
     form = forms.SearchForm()
@@ -82,10 +89,12 @@ def tagged_object_list(request, tags=''):
     theme_is_set = len([tag for tag in tags if tag.category == 'theme']) > 0
     if theme_is_set:
         model = models.Fragment
+    only_author = len(tags) == 1 and tags[0].category == 'author'
+    pd_counter = only_author and tags[0].goes_to_pd()
 
     user_is_owner = (len(shelf) and request.user.is_authenticated() and request.user == shelf[0].user)
     
-    extra_where = 'catalogue_tag.category NOT IN ("set", "book")'
+    extra_where = "catalogue_tag.category NOT IN ('set', 'book')"
     related_tags = models.Tag.objects.related_for_model(tags, model, counts=True, extra={'where': [extra_where]})
     categories = split_tags(related_tags)
 
@@ -101,6 +110,8 @@ def tagged_object_list(request, tags=''):
         extra_context = {
             'categories': categories,
             'shelf_is_set': shelf_is_set,
+            'only_author': only_author,
+            'pd_counter': pd_counter,
             'user_is_owner': user_is_owner,
             'formats_form': forms.DownloadFormatsForm(),
         },
@@ -119,12 +130,16 @@ def book_fragments(request, book_slug, theme_slug):
 
 
 def book_detail(request, slug):
-    book = get_object_or_404(models.Book, slug=slug)
+    try:
+        book = models.Book.objects.get(slug=slug)
+    except models.Book.DoesNotExist:
+        return book_stub_detail(request, slug)
+
     book_tag = get_object_or_404(models.Tag, slug = 'l-' + slug)
     tags = list(book.tags.filter(~Q(category='set')))
     categories = split_tags(tags)
     book_children = book.children.all().order_by('parent_number')
-    extra_where = 'catalogue_tag.category = "theme"'
+    extra_where = "catalogue_tag.category = 'theme'"
     book_themes = models.Tag.objects.related_for_model(book_tag, models.Fragment, counts=True, extra={'where': [extra_where]})
     extra_info = book.get_extra_info_value()
     
@@ -133,6 +148,15 @@ def book_detail(request, slug):
         context_instance=RequestContext(request))
 
 
+def book_stub_detail(request, slug):
+    book = get_object_or_404(models.BookStub, slug=slug)
+    pd_counter = book.pd
+    form = forms.SearchForm()
+    
+    return render_to_response('catalogue/book_stub_detail.html', locals(),
+        context_instance=RequestContext(request))
+    
+
 def book_text(request, slug):
     book = get_object_or_404(models.Book, slug=slug)
     book_themes = {}
@@ -149,40 +173,109 @@ def book_text(request, slug):
 # ==========
 # = Search =
 # ==========
+
+def _no_diacritics_regexp(query):
+    """ returns a regexp for searching for a query without diacritics
+    
+    should be locale-aware """
+    names = {'a':u'ą', 'c':u'ć', 'e':u'ę', 'l': u'ł', 'n':u'ń', 'o':u'ó', 's':u'ś', 'z':u'ź|ż'}
+    def repl(m):
+        l = m.group()
+        return "(%s|%s)" % (l, names[l])
+    return re.sub('[%s]'%(''.join(names.keys())), repl, query)
+
+def _word_starts_with(name, prefix):
+    """returns a Q object getting models having `name` contain a word
+    starting with `prefix`
+    """
+    kwargs = {}
+    if settings.DATABASE_ENGINE in ('mysql', 'postgresql_psycopg2', 'postgresql'):
+        prefix = _no_diacritics_regexp(re.escape(prefix))
+        # we could use a [[:<:]] (word start), 
+        # but we want both `xy` and `(xy` to catch `(xyz)`
+        kwargs['%s__iregex' % name] = u"(^|[^[:alpha:]])%s" % prefix
+    else:
+        # don't know how to do a generic regex
+        # checking for simple icontain instead
+        kwargs['%s__icontains' % name] = prefix
+    return Q(**kwargs)
+
+
+def _tags_exact_matches(prefix, user):
+    book_stubs = models.BookStub.objects.filter(title__iexact = prefix)
+    books = models.Book.objects.filter(title__iexact = prefix)
+    book_stubs = filter(lambda x: x not in books, book_stubs)
+    tags = models.Tag.objects.filter(name__iexact = prefix)
+    if user.is_authenticated():
+        tags = tags.filter(~Q(category='book') & (~Q(category='set') | Q(user=user)))
+    else:
+        tags = tags.filter(~Q(category='book') & ~Q(category='set'))
+
+    return list(books) + list(tags) + list(book_stubs)
+
+
 def _tags_starting_with(prefix, user):
-    books = models.Book.objects.filter(title__icontains=prefix)
-    tags = models.Tag.objects.filter(name__icontains=prefix)
+    book_stubs = models.BookStub.objects.filter(_word_starts_with('title', prefix))
+    books = models.Book.objects.filter(_word_starts_with('title', prefix))
+    book_stubs = filter(lambda x: x not in books, book_stubs)
+    tags = models.Tag.objects.filter(_word_starts_with('name', prefix))
     if user.is_authenticated():
         tags = tags.filter(~Q(category='book') & (~Q(category='set') | Q(user=user)))
     else:
         tags = tags.filter(~Q(category='book') & ~Q(category='set'))
 
-    return list(books) + list(tags)
+    return list(books) + list(tags) + list(book_stubs)
         
 
+
+def _get_result_link(match, tag_list):
+    if isinstance(match, models.Book) or isinstance(match, models.BookStub):
+        return match.get_absolute_url()
+    else:
+        return reverse('catalogue.views.tagged_object_list', 
+            kwargs={'tags': '/'.join(tag.slug for tag in tag_list + [match])}
+        )
+
+def _get_result_type(match):
+    if isinstance(match, models.Book) or isinstance(match, models.BookStub):
+        type = 'book'
+    else:
+        type = match.category
+    return dict(models.TAG_CATEGORIES)[type]
+    
+
+
 def search(request):
     tags = request.GET.get('tags', '')
     prefix = request.GET.get('q', '')
-    # Prefix must have at least 2 characters
-    if len(prefix) < 2:
-        return HttpResponse('')
     
     try:
         tag_list = models.Tag.get_tag_list(tags)
     except:
         tag_list = []
+
+    # Prefix must have at least 2 characters
+    if len(prefix) < 2:
+        return render_to_response('catalogue/search_too_short.html', {'tags':tag_list, 'prefix':prefix},
+            context_instance=RequestContext(request))
     
-    result = _tags_starting_with(prefix, request.user)
-    if len(result) > 0:
-        tag = result[0]
-        if isinstance(tag, models.Book):
-            return HttpResponseRedirect(tag.get_absolute_url())
-        else:
-            tag_list.append(tag)
-        
-    return HttpResponseRedirect(reverse('catalogue.views.tagged_object_list', 
-        kwargs={'tags': '/'.join(tag.slug for tag in tag_list)}
-    ))
+    result = _tags_exact_matches(prefix, request.user)
+    
+    if len(result) > 1:
+        # multiple exact matches
+        return render_to_response('catalogue/search_multiple_hits.html', 
+            {'tags':tag_list, 'prefix':prefix, 'results':((x, _get_result_link(x, tag_list), _get_result_type(x)) for x in result)},
+            context_instance=RequestContext(request))
+    
+    if not result:
+        # no exact matches
+        result = _tags_starting_with(prefix, request.user)
+    
+    if result:
+        return HttpResponseRedirect(_get_result_link(result[0], tag_list))
+    else:
+        return render_to_response('catalogue/search_no_hits.html', {'tags':tag_list, 'prefix':prefix},
+            context_instance=RequestContext(request))
 
 
 def tags_starting_with(request):
@@ -212,7 +305,7 @@ def book_sets(request, slug):
     book_sets = book.tags.filter(category='set', user=request.user)
     
     if not request.user.is_authenticated():
-        return HttpResponse('<p>Aby zarządzać swoimi półkami, musisz się zalogować.</p>')
+        return HttpResponse(_('<p>To maintain your shelves you need to be logged in.</p>'))
     
     if request.method == 'POST':
         form = forms.ObjectSetsForm(book, request.user, request.POST)
@@ -230,7 +323,7 @@ def book_sets(request, slug):
             
             book.tags = new_shelves + list(book.tags.filter(~Q(category='set') | ~Q(user=request.user)))
             if request.is_ajax():
-                return HttpResponse('<p>Półki zostały zapisane.</p>')
+                return HttpResponse(_('<p>Shelves were sucessfully saved.</p>'))
             else:
                 return HttpResponseRedirect('/')
     else:
@@ -248,12 +341,15 @@ def remove_from_shelf(request, shelf, book):
     book = get_object_or_404(models.Book, slug=book)
     shelf = get_object_or_404(models.Tag, slug=shelf, category='set', user=request.user)
     
-    models.Tag.objects.remove_tag(book, shelf)
-    
-    shelf.book_count -= 1
-    shelf.save()
-    
-    return HttpResponse('Usunieto')
+    if shelf in book.tags:
+        models.Tag.objects.remove_tag(book, shelf)
+
+        shelf.book_count -= 1
+        shelf.save()
+
+        return HttpResponse(_('Book was successfully removed from the shelf'))
+    else:
+        return HttpResponse(_('This book is not on the shelf'))
 
 
 def collect_books(books):
@@ -286,7 +382,7 @@ def download_shelf(request, slug):
         formats = ['pdf', 'odt', 'txt', 'mp3', 'ogg']
     
     # Create a ZIP archive
-    temp = temp = tempfile.TemporaryFile()
+    temp = tempfile.TemporaryFile()
     archive = zipfile.ZipFile(temp, 'w')
     
     for book in collect_books(models.Book.tagged.with_all(shelf)):
@@ -349,7 +445,7 @@ def new_set(request):
         new_set = new_set_form.save(request.user)
 
         if request.is_ajax():
-            return HttpResponse(u'<p>Półka <strong>%s</strong> została utworzona</p>' % new_set)
+            return HttpResponse(_('<p>Shelf <strong>%s</strong> was successfully created</p>') % new_set)
         else:
             return HttpResponseRedirect('/')
 
@@ -364,7 +460,7 @@ def delete_shelf(request, slug):
     user_set.delete()
 
     if request.is_ajax():
-        return HttpResponse(u'<p>Półka <strong>%s</strong> została usunięta</p>' % user_set.name)
+        return HttpResponse(_('<p>Shelf <strong>%s</strong> was successfully removed</p>') % user_set.name)
     else:
         return HttpResponseRedirect('/')
 
@@ -404,7 +500,7 @@ def register(request):
 @cache.never_cache
 def logout_then_redirect(request):
     auth.logout(request)
-    return HttpResponseRedirect(request.GET.get('next', '/'))
+    return HttpResponseRedirect(urlquote_plus(request.GET.get('next', '/'), safe='/?='))
 
 
 
@@ -423,7 +519,17 @@ def import_book(request):
             info = sys.exc_info()
             exception = pprint.pformat(info[1])
             tb = '\n'.join(traceback.format_tb(info[2]))
-            return HttpResponse("An error occurred: %s\n\n%s" % (exception, tb), mimetype='text/plain')
-        return HttpResponse("Book imported successfully")
+            _('Today is %(month)s, %(day)s.') % {'month': m, 'day': d}
+            return HttpResponse(_("An error occurred: %(exception)s\n\n%(tb)s") % {'exception':exception, 'tb':tb}, mimetype='text/plain')
+        return HttpResponse(_("Book imported successfully"))
     else:
-        return HttpResponse("Error importing file: %r" % book_import_form.errors)
\ No newline at end of file
+        return HttpResponse(_("Error importing file: %r") % book_import_form.errors)
+
+
+
+def clock(request):
+    """ Provides server time for jquery.countdown,
+    in a format suitable for Date.parse()
+    """
+    from datetime import datetime
+    return HttpResponse(datetime.now().strftime('%Y/%m/%d %H:%M:%S'))
\ No newline at end of file
diff --git a/apps/chunks/locale/de/LC_MESSAGES/django.po b/apps/chunks/locale/de/LC_MESSAGES/django.po
new file mode 100644 (file)
index 0000000..41fcd00
--- /dev/null
@@ -0,0 +1,53 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2010-05-18 10:58+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: models.py:10 models.py:24
+msgid "key"
+msgstr ""
+
+#: models.py:10
+msgid "A unique name for this chunk of content"
+msgstr ""
+
+#: models.py:11
+msgid "description"
+msgstr ""
+
+#: models.py:12
+msgid "content"
+msgstr ""
+
+#: models.py:16
+msgid "chunk"
+msgstr ""
+
+#: models.py:17
+msgid "chunks"
+msgstr ""
+
+#: models.py:24
+msgid "A unique name for this attachment"
+msgstr ""
+
+#: models.py:29
+msgid "attachment"
+msgstr ""
+
+#: models.py:29
+msgid "attachments"
+msgstr ""
diff --git a/apps/chunks/locale/en/LC_MESSAGES/django.po b/apps/chunks/locale/en/LC_MESSAGES/django.po
new file mode 100644 (file)
index 0000000..41fcd00
--- /dev/null
@@ -0,0 +1,53 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2010-05-18 10:58+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: models.py:10 models.py:24
+msgid "key"
+msgstr ""
+
+#: models.py:10
+msgid "A unique name for this chunk of content"
+msgstr ""
+
+#: models.py:11
+msgid "description"
+msgstr ""
+
+#: models.py:12
+msgid "content"
+msgstr ""
+
+#: models.py:16
+msgid "chunk"
+msgstr ""
+
+#: models.py:17
+msgid "chunks"
+msgstr ""
+
+#: models.py:24
+msgid "A unique name for this attachment"
+msgstr ""
+
+#: models.py:29
+msgid "attachment"
+msgstr ""
+
+#: models.py:29
+msgid "attachments"
+msgstr ""
diff --git a/apps/chunks/locale/es/LC_MESSAGES/django.po b/apps/chunks/locale/es/LC_MESSAGES/django.po
new file mode 100644 (file)
index 0000000..936f3df
--- /dev/null
@@ -0,0 +1,53 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2010-05-18 10:59+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: models.py:10 models.py:24
+msgid "key"
+msgstr ""
+
+#: models.py:10
+msgid "A unique name for this chunk of content"
+msgstr ""
+
+#: models.py:11
+msgid "description"
+msgstr ""
+
+#: models.py:12
+msgid "content"
+msgstr ""
+
+#: models.py:16
+msgid "chunk"
+msgstr ""
+
+#: models.py:17
+msgid "chunks"
+msgstr ""
+
+#: models.py:24
+msgid "A unique name for this attachment"
+msgstr ""
+
+#: models.py:29
+msgid "attachment"
+msgstr ""
+
+#: models.py:29
+msgid "attachments"
+msgstr ""
diff --git a/apps/chunks/locale/fr/LC_MESSAGES/django.po b/apps/chunks/locale/fr/LC_MESSAGES/django.po
new file mode 100644 (file)
index 0000000..936f3df
--- /dev/null
@@ -0,0 +1,53 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2010-05-18 10:59+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: models.py:10 models.py:24
+msgid "key"
+msgstr ""
+
+#: models.py:10
+msgid "A unique name for this chunk of content"
+msgstr ""
+
+#: models.py:11
+msgid "description"
+msgstr ""
+
+#: models.py:12
+msgid "content"
+msgstr ""
+
+#: models.py:16
+msgid "chunk"
+msgstr ""
+
+#: models.py:17
+msgid "chunks"
+msgstr ""
+
+#: models.py:24
+msgid "A unique name for this attachment"
+msgstr ""
+
+#: models.py:29
+msgid "attachment"
+msgstr ""
+
+#: models.py:29
+msgid "attachments"
+msgstr ""
diff --git a/apps/chunks/locale/lt/LC_MESSAGES/django.po b/apps/chunks/locale/lt/LC_MESSAGES/django.po
new file mode 100644 (file)
index 0000000..936f3df
--- /dev/null
@@ -0,0 +1,53 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2010-05-18 10:59+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: models.py:10 models.py:24
+msgid "key"
+msgstr ""
+
+#: models.py:10
+msgid "A unique name for this chunk of content"
+msgstr ""
+
+#: models.py:11
+msgid "description"
+msgstr ""
+
+#: models.py:12
+msgid "content"
+msgstr ""
+
+#: models.py:16
+msgid "chunk"
+msgstr ""
+
+#: models.py:17
+msgid "chunks"
+msgstr ""
+
+#: models.py:24
+msgid "A unique name for this attachment"
+msgstr ""
+
+#: models.py:29
+msgid "attachment"
+msgstr ""
+
+#: models.py:29
+msgid "attachments"
+msgstr ""
diff --git a/apps/chunks/locale/pl/LC_MESSAGES/django.po b/apps/chunks/locale/pl/LC_MESSAGES/django.po
new file mode 100644 (file)
index 0000000..936f3df
--- /dev/null
@@ -0,0 +1,53 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2010-05-18 10:59+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: models.py:10 models.py:24
+msgid "key"
+msgstr ""
+
+#: models.py:10
+msgid "A unique name for this chunk of content"
+msgstr ""
+
+#: models.py:11
+msgid "description"
+msgstr ""
+
+#: models.py:12
+msgid "content"
+msgstr ""
+
+#: models.py:16
+msgid "chunk"
+msgstr ""
+
+#: models.py:17
+msgid "chunks"
+msgstr ""
+
+#: models.py:24
+msgid "A unique name for this attachment"
+msgstr ""
+
+#: models.py:29
+msgid "attachment"
+msgstr ""
+
+#: models.py:29
+msgid "attachments"
+msgstr ""
diff --git a/apps/chunks/locale/ru/LC_MESSAGES/django.po b/apps/chunks/locale/ru/LC_MESSAGES/django.po
new file mode 100644 (file)
index 0000000..936f3df
--- /dev/null
@@ -0,0 +1,53 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2010-05-18 10:59+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: models.py:10 models.py:24
+msgid "key"
+msgstr ""
+
+#: models.py:10
+msgid "A unique name for this chunk of content"
+msgstr ""
+
+#: models.py:11
+msgid "description"
+msgstr ""
+
+#: models.py:12
+msgid "content"
+msgstr ""
+
+#: models.py:16
+msgid "chunk"
+msgstr ""
+
+#: models.py:17
+msgid "chunks"
+msgstr ""
+
+#: models.py:24
+msgid "A unique name for this attachment"
+msgstr ""
+
+#: models.py:29
+msgid "attachment"
+msgstr ""
+
+#: models.py:29
+msgid "attachments"
+msgstr ""
index 813621f..1447453 100644 (file)
@@ -1,3 +1,7 @@
+# -*- coding: utf-8 -*-
+# This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
 from django.contrib import admin
 from django.utils.translation import ugettext_lazy as _
 
diff --git a/apps/lessons/locale/de/LC_MESSAGES/django.po b/apps/lessons/locale/de/LC_MESSAGES/django.po
new file mode 100644 (file)
index 0000000..f7d920c
--- /dev/null
@@ -0,0 +1,49 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2010-05-18 11:00+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: models.py:12
+msgid "title"
+msgstr ""
+
+#: models.py:13
+msgid "slug"
+msgstr ""
+
+#: models.py:14
+msgid "file"
+msgstr ""
+
+#: models.py:15
+msgid "author"
+msgstr ""
+
+#: models.py:16
+msgid "slideshare ID"
+msgstr ""
+
+#: models.py:17
+msgid "description"
+msgstr ""
+
+#: models.py:29
+msgid "document"
+msgstr ""
+
+#: models.py:29
+msgid "documents"
+msgstr ""
diff --git a/apps/lessons/locale/en/LC_MESSAGES/django.po b/apps/lessons/locale/en/LC_MESSAGES/django.po
new file mode 100644 (file)
index 0000000..f7d920c
--- /dev/null
@@ -0,0 +1,49 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2010-05-18 11:00+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: models.py:12
+msgid "title"
+msgstr ""
+
+#: models.py:13
+msgid "slug"
+msgstr ""
+
+#: models.py:14
+msgid "file"
+msgstr ""
+
+#: models.py:15
+msgid "author"
+msgstr ""
+
+#: models.py:16
+msgid "slideshare ID"
+msgstr ""
+
+#: models.py:17
+msgid "description"
+msgstr ""
+
+#: models.py:29
+msgid "document"
+msgstr ""
+
+#: models.py:29
+msgid "documents"
+msgstr ""
diff --git a/apps/lessons/locale/es/LC_MESSAGES/django.po b/apps/lessons/locale/es/LC_MESSAGES/django.po
new file mode 100644 (file)
index 0000000..f7d920c
--- /dev/null
@@ -0,0 +1,49 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2010-05-18 11:00+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: models.py:12
+msgid "title"
+msgstr ""
+
+#: models.py:13
+msgid "slug"
+msgstr ""
+
+#: models.py:14
+msgid "file"
+msgstr ""
+
+#: models.py:15
+msgid "author"
+msgstr ""
+
+#: models.py:16
+msgid "slideshare ID"
+msgstr ""
+
+#: models.py:17
+msgid "description"
+msgstr ""
+
+#: models.py:29
+msgid "document"
+msgstr ""
+
+#: models.py:29
+msgid "documents"
+msgstr ""
diff --git a/apps/lessons/locale/fr/LC_MESSAGES/django.po b/apps/lessons/locale/fr/LC_MESSAGES/django.po
new file mode 100644 (file)
index 0000000..cbbd035
--- /dev/null
@@ -0,0 +1,49 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2010-05-18 11:01+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: models.py:12
+msgid "title"
+msgstr ""
+
+#: models.py:13
+msgid "slug"
+msgstr ""
+
+#: models.py:14
+msgid "file"
+msgstr ""
+
+#: models.py:15
+msgid "author"
+msgstr ""
+
+#: models.py:16
+msgid "slideshare ID"
+msgstr ""
+
+#: models.py:17
+msgid "description"
+msgstr ""
+
+#: models.py:29
+msgid "document"
+msgstr ""
+
+#: models.py:29
+msgid "documents"
+msgstr ""
diff --git a/apps/lessons/locale/lt/LC_MESSAGES/django.po b/apps/lessons/locale/lt/LC_MESSAGES/django.po
new file mode 100644 (file)
index 0000000..f7d920c
--- /dev/null
@@ -0,0 +1,49 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2010-05-18 11:00+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: models.py:12
+msgid "title"
+msgstr ""
+
+#: models.py:13
+msgid "slug"
+msgstr ""
+
+#: models.py:14
+msgid "file"
+msgstr ""
+
+#: models.py:15
+msgid "author"
+msgstr ""
+
+#: models.py:16
+msgid "slideshare ID"
+msgstr ""
+
+#: models.py:17
+msgid "description"
+msgstr ""
+
+#: models.py:29
+msgid "document"
+msgstr ""
+
+#: models.py:29
+msgid "documents"
+msgstr ""
diff --git a/apps/lessons/locale/pl/LC_MESSAGES/django.po b/apps/lessons/locale/pl/LC_MESSAGES/django.po
new file mode 100644 (file)
index 0000000..f7d920c
--- /dev/null
@@ -0,0 +1,49 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2010-05-18 11:00+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: models.py:12
+msgid "title"
+msgstr ""
+
+#: models.py:13
+msgid "slug"
+msgstr ""
+
+#: models.py:14
+msgid "file"
+msgstr ""
+
+#: models.py:15
+msgid "author"
+msgstr ""
+
+#: models.py:16
+msgid "slideshare ID"
+msgstr ""
+
+#: models.py:17
+msgid "description"
+msgstr ""
+
+#: models.py:29
+msgid "document"
+msgstr ""
+
+#: models.py:29
+msgid "documents"
+msgstr ""
diff --git a/apps/lessons/locale/ru/LC_MESSAGES/django.po b/apps/lessons/locale/ru/LC_MESSAGES/django.po
new file mode 100644 (file)
index 0000000..f7d920c
--- /dev/null
@@ -0,0 +1,49 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2010-05-18 11:00+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: models.py:12
+msgid "title"
+msgstr ""
+
+#: models.py:13
+msgid "slug"
+msgstr ""
+
+#: models.py:14
+msgid "file"
+msgstr ""
+
+#: models.py:15
+msgid "author"
+msgstr ""
+
+#: models.py:16
+msgid "slideshare ID"
+msgstr ""
+
+#: models.py:17
+msgid "description"
+msgstr ""
+
+#: models.py:29
+msgid "document"
+msgstr ""
+
+#: models.py:29
+msgid "documents"
+msgstr ""
index b0cd593..dc113ed 100644 (file)
@@ -1,11 +1,14 @@
 # -*- coding: utf-8 -*-
+# This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
 from django.db import models
 from django.utils.translation import ugettext_lazy as _
 
 from os import path
 
 class Document(models.Model):
-    """Dokument - materiał pomocniczy dla nauczycieli."""
+    """Document - hand-out for teachers"""
     title = models.CharField(_('title'), max_length=120)
     slug = models.SlugField(_('slug'))
     file = models.FileField(_('file'), upload_to='lessons/document')
index d43ac3a..69a47e3 100644 (file)
@@ -1,4 +1,7 @@
 # -*- coding: utf-8 -*-
+# This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
 from django.conf.urls.defaults import *
 from catalogue import forms
 from lessons import models
index e226919..69380d6 100644 (file)
@@ -1,3 +1,7 @@
+# -*- coding: utf-8 -*-
+# This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
 from django.views.generic.list_detail import object_detail
 from catalogue import forms
 from lessons import models
diff --git a/apps/newtagging/locale/de/LC_MESSAGES/django.po b/apps/newtagging/locale/de/LC_MESSAGES/django.po
new file mode 100644 (file)
index 0000000..7089f26
--- /dev/null
@@ -0,0 +1,50 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2010-05-18 11:01+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: admin.py:38
+msgid "tags"
+msgstr ""
+
+#: models.py:491
+msgid "tag"
+msgstr ""
+
+#: models.py:492
+msgid "content type"
+msgstr ""
+
+#: models.py:493
+msgid "object id"
+msgstr ""
+
+#: views.py:30
+msgid "tagged_object_list must be called with a queryset or a model."
+msgstr ""
+
+#: views.py:32
+msgid "tagged_object_list must be called with a tag model."
+msgstr ""
+
+#: views.py:34
+msgid "tagged_object_list must be called with a tag."
+msgstr ""
+
+#: views.py:38
+#, python-format
+msgid "No tags found matching \"%s\"."
+msgstr ""
diff --git a/apps/newtagging/locale/en/LC_MESSAGES/django.po b/apps/newtagging/locale/en/LC_MESSAGES/django.po
new file mode 100644 (file)
index 0000000..ca7d41b
--- /dev/null
@@ -0,0 +1,50 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2010-05-18 11:02+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: admin.py:38
+msgid "tags"
+msgstr ""
+
+#: models.py:491
+msgid "tag"
+msgstr ""
+
+#: models.py:492
+msgid "content type"
+msgstr ""
+
+#: models.py:493
+msgid "object id"
+msgstr ""
+
+#: views.py:30
+msgid "tagged_object_list must be called with a queryset or a model."
+msgstr ""
+
+#: views.py:32
+msgid "tagged_object_list must be called with a tag model."
+msgstr ""
+
+#: views.py:34
+msgid "tagged_object_list must be called with a tag."
+msgstr ""
+
+#: views.py:38
+#, python-format
+msgid "No tags found matching \"%s\"."
+msgstr ""
diff --git a/apps/newtagging/locale/es/LC_MESSAGES/django.po b/apps/newtagging/locale/es/LC_MESSAGES/django.po
new file mode 100644 (file)
index 0000000..ca7d41b
--- /dev/null
@@ -0,0 +1,50 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2010-05-18 11:02+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: admin.py:38
+msgid "tags"
+msgstr ""
+
+#: models.py:491
+msgid "tag"
+msgstr ""
+
+#: models.py:492
+msgid "content type"
+msgstr ""
+
+#: models.py:493
+msgid "object id"
+msgstr ""
+
+#: views.py:30
+msgid "tagged_object_list must be called with a queryset or a model."
+msgstr ""
+
+#: views.py:32
+msgid "tagged_object_list must be called with a tag model."
+msgstr ""
+
+#: views.py:34
+msgid "tagged_object_list must be called with a tag."
+msgstr ""
+
+#: views.py:38
+#, python-format
+msgid "No tags found matching \"%s\"."
+msgstr ""
diff --git a/apps/newtagging/locale/fr/LC_MESSAGES/django.po b/apps/newtagging/locale/fr/LC_MESSAGES/django.po
new file mode 100644 (file)
index 0000000..ca7d41b
--- /dev/null
@@ -0,0 +1,50 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2010-05-18 11:02+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: admin.py:38
+msgid "tags"
+msgstr ""
+
+#: models.py:491
+msgid "tag"
+msgstr ""
+
+#: models.py:492
+msgid "content type"
+msgstr ""
+
+#: models.py:493
+msgid "object id"
+msgstr ""
+
+#: views.py:30
+msgid "tagged_object_list must be called with a queryset or a model."
+msgstr ""
+
+#: views.py:32
+msgid "tagged_object_list must be called with a tag model."
+msgstr ""
+
+#: views.py:34
+msgid "tagged_object_list must be called with a tag."
+msgstr ""
+
+#: views.py:38
+#, python-format
+msgid "No tags found matching \"%s\"."
+msgstr ""
diff --git a/apps/newtagging/locale/lt/LC_MESSAGES/django.po b/apps/newtagging/locale/lt/LC_MESSAGES/django.po
new file mode 100644 (file)
index 0000000..ca7d41b
--- /dev/null
@@ -0,0 +1,50 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2010-05-18 11:02+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: admin.py:38
+msgid "tags"
+msgstr ""
+
+#: models.py:491
+msgid "tag"
+msgstr ""
+
+#: models.py:492
+msgid "content type"
+msgstr ""
+
+#: models.py:493
+msgid "object id"
+msgstr ""
+
+#: views.py:30
+msgid "tagged_object_list must be called with a queryset or a model."
+msgstr ""
+
+#: views.py:32
+msgid "tagged_object_list must be called with a tag model."
+msgstr ""
+
+#: views.py:34
+msgid "tagged_object_list must be called with a tag."
+msgstr ""
+
+#: views.py:38
+#, python-format
+msgid "No tags found matching \"%s\"."
+msgstr ""
diff --git a/apps/newtagging/locale/pl/LC_MESSAGES/django.po b/apps/newtagging/locale/pl/LC_MESSAGES/django.po
new file mode 100644 (file)
index 0000000..7089f26
--- /dev/null
@@ -0,0 +1,50 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2010-05-18 11:01+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: admin.py:38
+msgid "tags"
+msgstr ""
+
+#: models.py:491
+msgid "tag"
+msgstr ""
+
+#: models.py:492
+msgid "content type"
+msgstr ""
+
+#: models.py:493
+msgid "object id"
+msgstr ""
+
+#: views.py:30
+msgid "tagged_object_list must be called with a queryset or a model."
+msgstr ""
+
+#: views.py:32
+msgid "tagged_object_list must be called with a tag model."
+msgstr ""
+
+#: views.py:34
+msgid "tagged_object_list must be called with a tag."
+msgstr ""
+
+#: views.py:38
+#, python-format
+msgid "No tags found matching \"%s\"."
+msgstr ""
diff --git a/apps/newtagging/locale/ru/LC_MESSAGES/django.po b/apps/newtagging/locale/ru/LC_MESSAGES/django.po
new file mode 100644 (file)
index 0000000..ca7d41b
--- /dev/null
@@ -0,0 +1,50 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2010-05-18 11:02+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: admin.py:38
+msgid "tags"
+msgstr ""
+
+#: models.py:491
+msgid "tag"
+msgstr ""
+
+#: models.py:492
+msgid "content type"
+msgstr ""
+
+#: models.py:493
+msgid "object id"
+msgstr ""
+
+#: views.py:30
+msgid "tagged_object_list must be called with a queryset or a model."
+msgstr ""
+
+#: views.py:32
+msgid "tagged_object_list must be called with a tag model."
+msgstr ""
+
+#: views.py:34
+msgid "tagged_object_list must be called with a tag."
+msgstr ""
+
+#: views.py:38
+#, python-format
+msgid "No tags found matching \"%s\"."
+msgstr ""
index 1dbcb29..7870092 100644 (file)
@@ -1,3 +1,4 @@
+# -*- coding: utf-8 -*-
 """
 Custom managers for Django models registered with the tagging
 application.
index e121994..2055ec3 100644 (file)
@@ -1,8 +1,12 @@
+# -*- coding: utf-8 -*-
 """
 Models and managers for generic tagging.
 """
+
 # Python 2.3 compatibility
-if not hasattr(__builtins__, 'set'):
+try:
+    set
+except NameError: 
     from sets import Set as set
 
 from django.contrib.contenttypes import generic
@@ -108,7 +112,7 @@ class TagManager(models.Manager):
         WHERE %(tagged_item)s.content_type_id = %(content_type_id)s
             %%s
             %(extra_where)s
-        GROUP BY %(tag)s.id, %(tag)s.name
+        GROUP BY %(tag_columns)s, %(tag)s.id, %(tag)s.name
         %%s
         ORDER BY %(tag)s.%(ordering)s ASC""" % {
             'tag': qn(self.model._meta.db_table),
index 150a084..b88e9c4 100644 (file)
@@ -1,3 +1,4 @@
+# -*- coding: utf-8 -*-
 """
 Tagging related views.
 """
diff --git a/apps/pagination/__init__.py b/apps/pagination/__init__.py
deleted file mode 100644 (file)
index 8b13789..0000000
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/apps/pagination/middleware.py b/apps/pagination/middleware.py
deleted file mode 100644 (file)
index 0bab767..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-class PaginationMiddleware(object):
-    """
-    Inserts a variable representing the current page onto the request object if
-    it exists in either **GET** or **POST** portions of the request.
-    """
-    def process_request(self, request):
-        try:
-            request.page = int(request.REQUEST['page'])
-        except (KeyError, ValueError):
-            request.page = 1
\ No newline at end of file
diff --git a/apps/pagination/models.py b/apps/pagination/models.py
deleted file mode 100644 (file)
index 8b13789..0000000
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/apps/pagination/templates/pagination/pagination.html b/apps/pagination/templates/pagination/pagination.html
deleted file mode 100644 (file)
index 3799314..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-{% if is_paginated %}
-<div class="pagination">
-    {% if page_obj.has_previous %}
-        <a href="?page={{ page_obj.previous_page_number }}{{ getvars }}" class="prev">&lsaquo;&lsaquo; previous</a>
-    {% else %}
-        <span class="disabled prev">&lsaquo;&lsaquo; previous</span>
-    {% endif %}
-    {% for page in pages %}
-        {% if page %}
-            {% ifequal page page_obj.number %}
-                <span class="current page">{{ page }}</span>
-            {% else %}
-                <a href="?page={{ page }}{{ getvars }}" class="page">{{ page }}</a>
-            {% endifequal %}
-        {% else %}
-            ...
-        {% endif %}
-    {% endfor %}
-    {% if page_obj.has_next %}
-        <a href="?page={{ page_obj.next_page_number }}{{ getvars }}" class="next">next &rsaquo;&rsaquo;</a>
-    {% else %}
-        <span class="disabled next">next &rsaquo;&rsaquo;</span>
-    {% endif %}
-</div>
-{% endif %}
diff --git a/apps/pagination/templatetags/__init__.py b/apps/pagination/templatetags/__init__.py
deleted file mode 100644 (file)
index 8b13789..0000000
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/apps/pagination/templatetags/pagination_tags.py b/apps/pagination/templatetags/pagination_tags.py
deleted file mode 100644 (file)
index 4908421..0000000
+++ /dev/null
@@ -1,202 +0,0 @@
-try:
-    set
-except NameError:
-    from sets import Set as set
-from django import template
-from django.db.models.query import QuerySet
-from django.core.paginator import Paginator, QuerySetPaginator, InvalidPage
-
-register = template.Library()
-
-DEFAULT_PAGINATION = 20
-DEFAULT_WINDOW = 4
-DEFAULT_ORPHANS = 0
-
-def do_autopaginate(parser, token):
-    """
-    Splits the arguments to the autopaginate tag and formats them correctly.
-    """
-    split = token.split_contents()
-    if len(split) == 2:
-        return AutoPaginateNode(split[1])
-    elif len(split) == 3:
-        try:
-            paginate_by = int(split[2])
-        except ValueError:
-            raise template.TemplateSyntaxError(u'Got %s, but expected integer.' % split[2])
-        return AutoPaginateNode(split[1], paginate_by=paginate_by)
-    elif len(split) == 4:
-        try:
-            paginate_by = int(split[2])
-        except ValueError:
-            raise template.TemplateSyntaxError(u'Got %s, but expected integer.' % split[2])
-        try:
-            orphans = int(split[3])
-        except ValueError:
-            raise template.TemplateSyntaxError(u'Got %s, but expected integer.' % split[3])           
-        return AutoPaginateNode(split[1], paginate_by=paginate_by, orphans=orphans)
-    else:
-        raise template.TemplateSyntaxError('%r tag takes one required argument and one optional argument' % split[0])
-
-class AutoPaginateNode(template.Node):
-    """
-    Emits the required objects to allow for Digg-style pagination.
-    
-    First, it looks in the current context for the variable specified.  This
-    should be either a QuerySet or a list.
-    
-    1. If it is a QuerySet, this ``AutoPaginateNode`` will emit a 
-       ``QuerySetPaginator`` and the current page object into the context names
-       ``paginator`` and ``page_obj``, respectively.
-    
-    2. If it is a list, this ``AutoPaginateNode`` will emit a simple
-       ``Paginator`` and the current page object into the context names 
-       ``paginator`` and ``page_obj``, respectively.
-    
-    It will then replace the variable specified with only the objects for the
-    current page.
-    
-    .. note::
-        
-        It is recommended to use *{% paginate %}* after using the autopaginate
-        tag.  If you choose not to use *{% paginate %}*, make sure to display the
-        list of availabale pages, or else the application may seem to be buggy.
-    """
-    def __init__(self, queryset_var, paginate_by=DEFAULT_PAGINATION, orphans=DEFAULT_ORPHANS):
-        self.queryset_var = template.Variable(queryset_var)
-        self.paginate_by = paginate_by
-        self.orphans = orphans
-
-    def render(self, context):
-        key = self.queryset_var.var
-        value = self.queryset_var.resolve(context)
-        if issubclass(value.__class__, QuerySet):
-            model = value.model
-            paginator_class = QuerySetPaginator
-        else:
-            value = list(value)
-            try:
-                model = value[0].__class__
-            except IndexError:
-                return u''
-            paginator_class = Paginator
-        paginator = paginator_class(value, self.paginate_by, self.orphans)
-        try:
-            page_obj = paginator.page(context['request'].page)
-        except InvalidPage:
-            # context[key] = []
-            # context['invalid_page'] = True
-            # return u''
-            from django.http import Http404
-            raise Http404
-        context[key] = page_obj.object_list
-        context['paginator'] = paginator
-        context['page_obj'] = page_obj
-        return u''
-
-def paginate(context, window=DEFAULT_WINDOW):
-    """
-    Renders the ``pagination/pagination.html`` template, resulting in a
-    Digg-like display of the available pages, given the current page.  If there
-    are too many pages to be displayed before and after the current page, then
-    elipses will be used to indicate the undisplayed gap between page numbers.
-    
-    Requires one argument, ``context``, which should be a dictionary-like data
-    structure and must contain the following keys:
-    
-    ``paginator``
-        A ``Paginator`` or ``QuerySetPaginator`` object.
-    
-    ``page_obj``
-        This should be the result of calling the page method on the 
-        aforementioned ``Paginator`` or ``QuerySetPaginator`` object, given
-        the current page.
-    
-    This same ``context`` dictionary-like data structure may also include:
-    
-    ``getvars``
-        A dictionary of all of the **GET** parameters in the current request.
-        This is useful to maintain certain types of state, even when requesting
-        a different page.
-        """
-    try:
-        paginator = context['paginator']
-        page_obj = context['page_obj']
-        page_range = paginator.page_range
-        # First and last are simply the first *n* pages and the last *n* pages,
-        # where *n* is the current window size.
-        first = set(page_range[:window])
-        last = set(page_range[-window:])
-        # Now we look around our current page, making sure that we don't wrap
-        # around.
-        current_start = page_obj.number-1-window
-        if current_start < 0:
-            current_start = 0
-        current_end = page_obj.number-1+window
-        if current_end < 0:
-            current_end = 0
-        current = set(page_range[current_start:current_end])
-        pages = []
-        # If there's no overlap between the first set of pages and the current
-        # set of pages, then there's a possible need for elusion.
-        if len(first.intersection(current)) == 0:
-            first_list = sorted(list(first))
-            second_list = sorted(list(current))
-            pages.extend(first_list)
-            diff = second_list[0] - first_list[-1]
-            # If there is a gap of two, between the last page of the first
-            # set and the first page of the current set, then we're missing a
-            # page.
-            if diff == 2:
-                pages.append(second_list[0] - 1)
-            # If the difference is just one, then there's nothing to be done,
-            # as the pages need no elusion and are correct.
-            elif diff == 1:
-                pass
-            # Otherwise, there's a bigger gap which needs to be signaled for
-            # elusion, by pushing a None value to the page list.
-            else:
-                pages.append(None)
-            pages.extend(second_list)
-        else:
-            pages.extend(sorted(list(first.union(current))))
-        # If there's no overlap between the current set of pages and the last
-        # set of pages, then there's a possible need for elusion.
-        if len(current.intersection(last)) == 0:
-            second_list = sorted(list(last))
-            diff = second_list[0] - pages[-1]
-            # If there is a gap of two, between the last page of the current
-            # set and the first page of the last set, then we're missing a 
-            # page.
-            if diff == 2:
-                pages.append(second_list[0] - 1)
-            # If the difference is just one, then there's nothing to be done,
-            # as the pages need no elusion and are correct.
-            elif diff == 1:
-                pass
-            # Otherwise, there's a bigger gap which needs to be signaled for
-            # elusion, by pushing a None value to the page list.
-            else:
-                pages.append(None)
-            pages.extend(second_list)
-        else:
-            pages.extend(sorted(list(last.difference(current))))
-        to_return = {
-            'pages': pages,
-            'page_obj': page_obj,
-            'paginator': paginator,
-            'is_paginated': paginator.count > paginator.per_page,
-        }
-        if 'request' in context:
-            getvars = context['request'].GET.copy()
-            if 'page' in getvars:
-                del getvars['page']
-            if len(getvars.keys()) > 0:
-                to_return['getvars'] = "&%s" % getvars.urlencode()
-            else:
-                to_return['getvars'] = ''
-        return to_return
-    except KeyError:
-        return {}
-register.inclusion_tag('pagination/pagination.html', takes_context=True)(paginate)
-register.tag('autopaginate', do_autopaginate)
\ No newline at end of file
diff --git a/apps/pagination/tests.py b/apps/pagination/tests.py
deleted file mode 100644 (file)
index 837e55c..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-"""
->>> from django.core.paginator import Paginator
->>> from pagination.templatetags.pagination_tags import paginate
->>> from django.template import Template, Context
-
->>> p = Paginator(range(15), 2)
->>> paginate({'paginator': p, 'page_obj': p.page(1)})['pages']
-[1, 2, 3, 4, 5, 6, 7, 8]
-
->>> p = Paginator(range(17), 2)
->>> paginate({'paginator': p, 'page_obj': p.page(1)})['pages']
-[1, 2, 3, 4, 5, 6, 7, 8, 9]
-
->>> p = Paginator(range(19), 2)
->>> paginate({'paginator': p, 'page_obj': p.page(1)})['pages']
-[1, 2, 3, 4, None, 7, 8, 9, 10]
-
->>> p = Paginator(range(21), 2)
->>> paginate({'paginator': p, 'page_obj': p.page(1)})['pages']
-[1, 2, 3, 4, None, 8, 9, 10, 11]
-
-# Testing orphans
->>> p = Paginator(range(5), 2, 1)
->>> paginate({'paginator': p, 'page_obj': p.page(1)})['pages']
-[1, 2]
-
->>> p = Paginator(range(21), 2, 1)
->>> paginate({'paginator': p, 'page_obj': p.page(1)})['pages']
-[1, 2, 3, 4, None, 7, 8, 9, 10]
-
->>> t = Template("{% load pagination_tags %}{% autopaginate var 2 %}{% paginate %}")
-
-# WARNING: Please, please nobody read this portion of the code!
->>> class GetProxy(object):
-...     def __iter__(self): yield self.__dict__.__iter__
-...     def copy(self): return self
-...     def urlencode(self): return u''
-...     def keys(self): return []
->>> class RequestProxy(object):
-...     page = 1
-...     GET = GetProxy()
->>>
-# ENDWARNING
-
->>> t.render(Context({'var': range(21), 'request': RequestProxy()}))
-u'\\n<div class="pagination">...
->>>
->>> t = Template("{% load pagination_tags %}{% autopaginate var %}{% paginate %}")
->>> t.render(Context({'var': range(21), 'request': RequestProxy()}))
-u'\\n<div class="pagination">...
->>>
-"""
\ No newline at end of file
diff --git a/apps/sorl/__init__.py b/apps/sorl/__init__.py
deleted file mode 100755 (executable)
index e69de29..0000000
diff --git a/apps/sorl/thumbnail/__init__.py b/apps/sorl/thumbnail/__init__.py
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/apps/sorl/thumbnail/base.py b/apps/sorl/thumbnail/base.py
deleted file mode 100755 (executable)
index 24f4d97..0000000
+++ /dev/null
@@ -1,285 +0,0 @@
-import os
-from os.path import isfile, isdir, getmtime, dirname, splitext, getsize
-from tempfile import mkstemp
-from shutil import copyfile
-
-from PIL import Image
-
-from sorl.thumbnail import defaults
-from sorl.thumbnail.processors import get_valid_options, dynamic_import
-
-
-class ThumbnailException(Exception):
-    # Stop Django templates from choking if something goes wrong.
-    silent_variable_failure = True
-
-
-class Thumbnail(object):
-    imagemagick_file_types = defaults.IMAGEMAGICK_FILE_TYPES
-
-    def __init__(self, source, requested_size, opts=None, quality=85,
-                 dest=None, convert_path=defaults.CONVERT,
-                 wvps_path=defaults.WVPS, processors=None):
-        # Paths to external commands
-        self.convert_path = convert_path
-        self.wvps_path = wvps_path
-        # Absolute paths to files
-        self.source = source
-        self.dest = dest
-
-        # Thumbnail settings
-        try:
-            x, y = [int(v) for v in requested_size]
-        except (TypeError, ValueError):
-            raise TypeError('Thumbnail received invalid value for size '
-                            'argument: %s' % repr(requested_size))
-        else:
-            self.requested_size = (x, y)
-        try:
-            self.quality = int(quality)
-            if not 0 < quality <= 100:
-                raise ValueError
-        except (TypeError, ValueError):
-            raise TypeError('Thumbnail received invalid value for quality '
-                            'argument: %r' % quality)
-
-        # Processors
-        if processors is None:
-            processors = dynamic_import(defaults.PROCESSORS)
-        self.processors = processors
-
-        # Handle old list format for opts.
-        opts = opts or {}
-        if isinstance(opts, (list, tuple)):
-            opts = dict([(opt, None) for opt in opts])
-
-        # Set Thumbnail opt(ion)s
-        VALID_OPTIONS = get_valid_options(processors)
-        for opt in opts:
-            if not opt in VALID_OPTIONS:
-                raise TypeError('Thumbnail received an invalid option: %s'
-                                % opt)
-        self.opts = opts
-
-        if self.dest is not None:
-            self.generate()
-
-    def generate(self):
-        """
-        Generates the thumbnail if it doesn't exist or if the file date of the
-        source file is newer than that of the thumbnail.
-        """
-        # Ensure dest(ination) attribute is set
-        if not self.dest:
-            raise ThumbnailException("No destination filename set.")
-
-        if not isinstance(self.dest, basestring):
-            # We'll assume dest is a file-like instance if it exists but isn't
-            # a string.
-            self._do_generate()
-        elif not isfile(self.dest) or (self.source_exists and
-            getmtime(self.source) > getmtime(self.dest)):
-
-            # Ensure the directory exists
-            directory = dirname(self.dest)
-            if directory and not isdir(directory):
-                os.makedirs(directory)
-
-            self._do_generate()
-
-    def _check_source_exists(self):
-        """
-        Ensure the source file exists. If source is not a string then it is
-        assumed to be a file-like instance which "exists".
-        """
-        if not hasattr(self, '_source_exists'):
-            self._source_exists = (self.source and
-                                   (not isinstance(self.source, basestring) or
-                                    isfile(self.source)))
-        return self._source_exists
-    source_exists = property(_check_source_exists)
-
-    def _get_source_filetype(self):
-        """
-        Set the source filetype. First it tries to use magic and
-        if import error it will just use the extension
-        """
-        if not hasattr(self, '_source_filetype'):
-            if not isinstance(self.source, basestring):
-                # Assuming a file-like object - we won't know it's type.
-                return None
-            try:
-                import magic
-            except ImportError:
-                self._source_filetype = splitext(self.source)[1].lower().\
-                   replace('.', '').replace('jpeg', 'jpg')
-            else:
-                m = magic.open(magic.MAGIC_NONE)
-                m.load()
-                ftype = m.file(self.source)
-                if ftype.find('Microsoft Office Document') != -1:
-                    self._source_filetype = 'doc'
-                elif ftype.find('PDF document') != -1:
-                    self._source_filetype = 'pdf'
-                elif ftype.find('JPEG') != -1:
-                    self._source_filetype = 'jpg'
-                else:
-                    self._source_filetype = ftype
-        return self._source_filetype
-    source_filetype = property(_get_source_filetype)
-
-    # data property is the image data of the (generated) thumbnail
-    def _get_data(self):
-        if not hasattr(self, '_data'):
-            try:
-                self._data = Image.open(self.dest)
-            except IOError, detail:
-                raise ThumbnailException(detail)
-        return self._data
-
-    def _set_data(self, im):
-        self._data = im
-    data = property(_get_data, _set_data)
-
-    # source_data property is the image data from the source file
-    def _get_source_data(self):
-        if not hasattr(self, '_source_data'):
-            if not self.source_exists:
-                raise ThumbnailException("Source file: '%s' does not exist." %
-                                         self.source)
-            if self.source_filetype == 'doc':
-                self._convert_wvps(self.source)
-            elif self.source_filetype in self.imagemagick_file_types:
-                self._convert_imagemagick(self.source)
-            else:
-                self.source_data = self.source
-        return self._source_data
-
-    def _set_source_data(self, image):
-        if isinstance(image, Image.Image):
-            self._source_data = image
-        else:
-            try:
-                self._source_data = Image.open(image)
-            except IOError, detail:
-                raise ThumbnailException("%s: %s" % (detail, image))
-            except MemoryError:
-                raise ThumbnailException("Memory Error: %s" % image)
-    source_data = property(_get_source_data, _set_source_data)
-
-    def _convert_wvps(self, filename):
-        try:
-            import subprocess
-        except ImportError:
-            raise ThumbnailException('wvps requires the Python 2.4 subprocess '
-                                     'package.')
-        tmp = mkstemp('.ps')[1]
-        try:
-            p = subprocess.Popen((self.wvps_path, filename, tmp),
-                                 stdout=subprocess.PIPE)
-            p.wait()
-        except OSError, detail:
-            os.remove(tmp)
-            raise ThumbnailException('wvPS error: %s' % detail)
-        self._convert_imagemagick(tmp)
-        os.remove(tmp)
-
-    def _convert_imagemagick(self, filename):
-        try:
-            import subprocess
-        except ImportError:
-            raise ThumbnailException('imagemagick requires the Python 2.4 '
-                                     'subprocess package.')
-        tmp = mkstemp('.png')[1]
-        if 'crop' in self.opts or 'autocrop' in self.opts:
-            x, y = [d * 3 for d in self.requested_size]
-        else:
-            x, y = self.requested_size
-        try:
-            p = subprocess.Popen((self.convert_path, '-size', '%sx%s' % (x, y),
-                '-antialias', '-colorspace', 'rgb', '-format', 'PNG24',
-                '%s[0]' % filename, tmp), stdout=subprocess.PIPE)
-            p.wait()
-        except OSError, detail:
-            os.remove(tmp)
-            raise ThumbnailException('ImageMagick error: %s' % detail)
-        self.source_data = tmp
-        os.remove(tmp)
-
-    def _do_generate(self):
-        """
-        Generates the thumbnail image.
-
-        This a semi-private method so it isn't directly available to template
-        authors if this object is passed to the template context.
-        """
-        im = self.source_data
-
-        for processor in self.processors:
-            im = processor(im, self.requested_size, self.opts)
-
-        self.data = im
-
-        filelike = not isinstance(self.dest, basestring)
-        if not filelike:
-            dest_extension = os.path.splitext(self.dest)[1][1:]
-            format = None
-        else:
-            dest_extension = None
-            format = 'JPEG'
-        if (self.source_filetype and self.source_filetype == dest_extension and
-                self.source_data == self.data):
-            copyfile(self.source, self.dest)
-        else:
-            try:
-                im.save(self.dest, format=format, quality=self.quality,
-                        optimize=1)
-            except IOError:
-                # Try again, without optimization (PIL can't optimize an image
-                # larger than ImageFile.MAXBLOCK, which is 64k by default)
-                try:
-                    im.save(self.dest, format=format, quality=self.quality)
-                except IOError, detail:
-                    raise ThumbnailException(detail)
-
-        if filelike:
-            self.dest.seek(0)
-
-    # Some helpful methods
-
-    def _dimension(self, axis):
-        if self.dest is None:
-            return None
-        return self.data.size[axis]
-
-    def width(self):
-        return self._dimension(0)
-
-    def height(self):
-        return self._dimension(1)
-
-    def _get_filesize(self):
-        if self.dest is None:
-            return None
-        if not hasattr(self, '_filesize'):
-            self._filesize = getsize(self.dest)
-        return self._filesize
-    filesize = property(_get_filesize)
-
-    def _source_dimension(self, axis):
-        if self.source_filetype in ['pdf', 'doc']:
-            return None
-        else:
-            return self.source_data.size[axis]
-
-    def source_width(self):
-        return self._source_dimension(0)
-
-    def source_height(self):
-        return self._source_dimension(1)
-
-    def _get_source_filesize(self):
-        if not hasattr(self, '_source_filesize'):
-            self._source_filesize = getsize(self.source)
-        return self._source_filesize
-    source_filesize = property(_get_source_filesize)
diff --git a/apps/sorl/thumbnail/defaults.py b/apps/sorl/thumbnail/defaults.py
deleted file mode 100644 (file)
index b4ae142..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-DEBUG = False
-BASEDIR = ''
-SUBDIR = ''
-PREFIX = ''
-QUALITY = 85
-CONVERT = '/usr/bin/convert'
-WVPS = '/usr/bin/wvPS'
-EXTENSION = 'jpg'
-PROCESSORS = (
-    'sorl.thumbnail.processors.colorspace',
-    'sorl.thumbnail.processors.autocrop',
-    'sorl.thumbnail.processors.scale_and_crop',
-    'sorl.thumbnail.processors.filters',
-)
-IMAGEMAGICK_FILE_TYPES = ('eps', 'pdf', 'psd')
diff --git a/apps/sorl/thumbnail/fields.py b/apps/sorl/thumbnail/fields.py
deleted file mode 100644 (file)
index 1b52743..0000000
+++ /dev/null
@@ -1,228 +0,0 @@
-from UserDict import DictMixin
-try:
-    from cStringIO import StringIO
-except ImportError:
-    from StringIO import StringIO
-
-from django.db.models.fields.files import ImageField, ImageFieldFile
-from django.core.files.base import ContentFile
-from django.utils.safestring import mark_safe
-from django.utils.html import escape
-
-from sorl.thumbnail.base import Thumbnail
-from sorl.thumbnail.main import DjangoThumbnail, build_thumbnail_name
-from sorl.thumbnail.utils import delete_thumbnails
-
-
-REQUIRED_ARGS = ('size',)
-ALL_ARGS = {
-    'size': 'requested_size',
-    'options': 'opts',
-    'quality': 'quality',
-    'basedir': 'basedir',
-    'subdir': 'subdir',
-    'prefix': 'prefix',
-    'extension': 'extension',
-}
-BASE_ARGS = {
-    'size': 'requested_size',
-    'options': 'opts',
-    'quality': 'quality',
-}
-TAG_HTML = '<img src="%(src)s" width="%(width)s" height="%(height)s" alt="" />'
-
-
-class ThumbsDict(object, DictMixin):
-    def __init__(self, descriptor):
-        super(ThumbsDict, self).__init__()
-        self.descriptor = descriptor
-
-    def keys(self):
-        return self.descriptor.field.extra_thumbnails.keys()
-
-
-class LazyThumbs(ThumbsDict):
-    def __init__(self, *args, **kwargs):
-        super(LazyThumbs, self).__init__(*args, **kwargs)
-        self.cached = {}
-
-    def __getitem__(self, key):
-        thumb = self.cached.get(key)
-        if not thumb:
-            args = self.descriptor.field.extra_thumbnails[key]
-            thumb = self.descriptor._build_thumbnail(args)
-            self.cached[key] = thumb
-        return thumb
-
-    def keys(self):
-        return self.descriptor.field.extra_thumbnails.keys()
-
-
-class ThumbTags(ThumbsDict):
-    def __getitem__(self, key):
-        thumb = self.descriptor.extra_thumbnails[key]
-        return self.descriptor._build_thumbnail_tag(thumb)
-
-
-class BaseThumbnailFieldFile(ImageFieldFile):
-    def _build_thumbnail(self, args):
-        # Build the DjangoThumbnail kwargs.
-        kwargs = {}
-        for k, v in args.items():
-            kwargs[ALL_ARGS[k]] = v
-        # Build the destination filename and return the thumbnail.
-        name_kwargs = {}
-        for key in ['size', 'options', 'quality', 'basedir', 'subdir',
-                    'prefix', 'extension']:
-            name_kwargs[key] = args.get(key)
-        source = getattr(self.instance, self.field.name)
-        dest = build_thumbnail_name(source.name, **name_kwargs)
-        return DjangoThumbnail(source, relative_dest=dest, **kwargs)
-
-    def _build_thumbnail_tag(self, thumb):
-        opts = dict(src=escape(thumb), width=thumb.width(),
-                    height=thumb.height())
-        return mark_safe(self.field.thumbnail_tag % opts)
-
-    def _get_extra_thumbnails(self):
-        if self.field.extra_thumbnails is None:
-            return None
-        if not hasattr(self, '_extra_thumbnails'):
-            self._extra_thumbnails = LazyThumbs(self)
-        return self._extra_thumbnails
-    extra_thumbnails = property(_get_extra_thumbnails)
-
-    def _get_extra_thumbnails_tag(self):
-        if self.field.extra_thumbnails is None:
-            return None
-        return ThumbTags(self)
-    extra_thumbnails_tag = property(_get_extra_thumbnails_tag)
-
-    def save(self, *args, **kwargs):
-        # Optionally generate the thumbnails after the image is saved.
-        super(BaseThumbnailFieldFile, self).save(*args, **kwargs)
-        if self.field.generate_on_save:
-            self.generate_thumbnails()
-
-    def delete(self, *args, **kwargs):
-        # Delete any thumbnails too (and not just ones defined here in case
-        # the {% thumbnail %} tag was used or the thumbnail sizes changed).
-        relative_source_path = getattr(self.instance, self.field.name).name
-        delete_thumbnails(relative_source_path)
-        super(BaseThumbnailFieldFile, self).delete(*args, **kwargs)
-
-    def generate_thumbnails(self):
-        # Getting the thumbs generates them.
-        if self.extra_thumbnails:
-            self.extra_thumbnails.values()
-
-
-class ImageWithThumbnailsFieldFile(BaseThumbnailFieldFile):
-    def _get_thumbnail(self):
-        return self._build_thumbnail(self.field.thumbnail)
-    thumbnail = property(_get_thumbnail)
-
-    def _get_thumbnail_tag(self):
-        return self._build_thumbnail_tag(self.thumbnail)
-    thumbnail_tag = property(_get_thumbnail_tag)
-
-    def generate_thumbnails(self, *args, **kwargs):
-        self.thumbnail.generate()
-        Super = super(ImageWithThumbnailsFieldFile, self)
-        return Super.generate_thumbnails(*args, **kwargs)
-
-
-class ThumbnailFieldFile(BaseThumbnailFieldFile):
-    def save(self, name, content, *args, **kwargs):
-        new_content = StringIO()
-        # Build the Thumbnail kwargs.
-        thumbnail_kwargs = {}
-        for k, argk in BASE_ARGS.items():
-            if not k in self.field.thumbnail:
-                continue
-            thumbnail_kwargs[argk] = self.field.thumbnail[k]
-        Thumbnail(source=content, dest=new_content, **thumbnail_kwargs)
-        new_content = ContentFile(new_content.read())
-        super(ThumbnailFieldFile, self).save(name, new_content, *args,
-                                             **kwargs)
-
-    def _get_thumbnail_tag(self):
-        opts = dict(src=escape(self.url), width=self.width,
-                    height=self.height)
-        return mark_safe(self.field.thumbnail_tag % opts)
-    thumbnail_tag = property(_get_thumbnail_tag)
-
-
-class BaseThumbnailField(ImageField):
-    def __init__(self, *args, **kwargs):
-        # The new arguments for this field aren't explicitly defined so that
-        # users can still use normal ImageField positional arguments.
-        self.extra_thumbnails = kwargs.pop('extra_thumbnails', None)
-        self.thumbnail_tag = kwargs.pop('thumbnail_tag', TAG_HTML)
-        self.generate_on_save = kwargs.pop('generate_on_save', False)
-
-        super(BaseThumbnailField, self).__init__(*args, **kwargs)
-        _verify_thumbnail_attrs(self.thumbnail)
-        if self.extra_thumbnails:
-            for extra, attrs in self.extra_thumbnails.items():
-                name = "%r of 'extra_thumbnails'"
-                _verify_thumbnail_attrs(attrs, name)
-
-    def south_field_triple(self):
-        """
-        Return a suitable description of this field for South.
-        """
-        # We'll just introspect ourselves, since we inherit.
-        from south.modelsinspector import introspector
-        field_class = "django.db.models.fields.files.ImageField"
-        args, kwargs = introspector(self)
-        # That's our definition!
-        return (field_class, args, kwargs)
-
-
-class ImageWithThumbnailsField(BaseThumbnailField):
-    """
-    photo = ImageWithThumbnailsField(
-        upload_to='uploads',
-        thumbnail={'size': (80, 80), 'options': ('crop', 'upscale'),
-                   'extension': 'png'},
-        extra_thumbnails={
-            'admin': {'size': (70, 50), 'options': ('sharpen',)},
-        }
-    )
-    """
-    attr_class = ImageWithThumbnailsFieldFile
-
-    def __init__(self, *args, **kwargs):
-        self.thumbnail = kwargs.pop('thumbnail', None)
-        super(ImageWithThumbnailsField, self).__init__(*args, **kwargs)
-
-
-class ThumbnailField(BaseThumbnailField):
-    """
-    avatar = ThumbnailField(
-        upload_to='uploads',
-        size=(200, 200),
-        options=('crop',),
-        extra_thumbnails={
-            'admin': {'size': (70, 50), 'options': (crop, 'sharpen')},
-        }
-    )
-    """
-    attr_class = ThumbnailFieldFile
-
-    def __init__(self, *args, **kwargs):
-        self.thumbnail = {}
-        for attr in ALL_ARGS:
-            if attr in kwargs:
-                self.thumbnail[attr] = kwargs.pop(attr)
-        super(ThumbnailField, self).__init__(*args, **kwargs)
-
-
-def _verify_thumbnail_attrs(attrs, name="'thumbnail'"):
-    for arg in REQUIRED_ARGS:
-        if arg not in attrs:
-            raise TypeError('Required attr %r missing in %s arg' % (arg, name))
-    for attr in attrs:
-        if attr not in ALL_ARGS:
-            raise TypeError('Invalid attr %r found in %s arg' % (arg, name))
diff --git a/apps/sorl/thumbnail/main.py b/apps/sorl/thumbnail/main.py
deleted file mode 100644 (file)
index a59b64f..0000000
+++ /dev/null
@@ -1,115 +0,0 @@
-import os
-
-from django.conf import settings
-from django.utils.encoding import iri_to_uri, force_unicode
-
-from sorl.thumbnail.base import Thumbnail
-from sorl.thumbnail.processors import dynamic_import
-from sorl.thumbnail import defaults
-
-
-def get_thumbnail_setting(setting, override=None):
-    """
-    Get a thumbnail setting from Django settings module, falling back to the
-    default.
-
-    If override is not None, it will be used instead of the setting.
-    """
-    if override is not None:
-        return override
-    if hasattr(settings, 'THUMBNAIL_%s' % setting):
-        return getattr(settings, 'THUMBNAIL_%s' % setting)
-    else:
-        return getattr(defaults, setting)
-
-
-def build_thumbnail_name(source_name, size, options=None,
-                         quality=None, basedir=None, subdir=None, prefix=None,
-                         extension=None):
-    quality = get_thumbnail_setting('QUALITY', quality)
-    basedir = get_thumbnail_setting('BASEDIR', basedir)
-    subdir = get_thumbnail_setting('SUBDIR', subdir)
-    prefix = get_thumbnail_setting('PREFIX', prefix)
-    extension = get_thumbnail_setting('EXTENSION', extension)
-    path, filename = os.path.split(source_name)
-    basename, ext = os.path.splitext(filename)
-    name = '%s%s' % (basename, ext.replace(os.extsep, '_'))
-    size = '%sx%s' % tuple(size)
-
-    # Handle old list format for opts.
-    options = options or {}
-    if isinstance(options, (list, tuple)):
-        options = dict([(opt, None) for opt in options])
-
-    opts = options.items()
-    opts.sort()   # options are sorted so the filename is consistent
-    opts = ['%s_' % (v is not None and '%s-%s' % (k, v) or k)
-            for k, v in opts]
-    opts = ''.join(opts)
-    extension = extension and '.%s' % extension
-    thumbnail_filename = '%s%s_%s_%sq%s%s' % (prefix, name, size, opts,
-                                              quality, extension)
-    return os.path.join(basedir, path, subdir, thumbnail_filename)
-
-
-class DjangoThumbnail(Thumbnail):
-    imagemagick_file_types = get_thumbnail_setting('IMAGEMAGICK_FILE_TYPES')
-
-    def __init__(self, relative_source, requested_size, opts=None,
-                 quality=None, basedir=None, subdir=None, prefix=None,
-                 relative_dest=None, processors=None, extension=None):
-        relative_source = force_unicode(relative_source)
-        # Set the absolute filename for the source file
-        source = self._absolute_path(relative_source)
-
-        quality = get_thumbnail_setting('QUALITY', quality)
-        convert_path = get_thumbnail_setting('CONVERT')
-        wvps_path = get_thumbnail_setting('WVPS')
-        if processors is None:
-            processors = dynamic_import(get_thumbnail_setting('PROCESSORS'))
-
-        # Call super().__init__ now to set the opts attribute. generate() won't
-        # get called because we are not setting the dest attribute yet.
-        super(DjangoThumbnail, self).__init__(source, requested_size,
-            opts=opts, quality=quality, convert_path=convert_path,
-            wvps_path=wvps_path, processors=processors)
-
-        # Get the relative filename for the thumbnail image, then set the
-        # destination filename
-        if relative_dest is None:
-            relative_dest = \
-               self._get_relative_thumbnail(relative_source, basedir=basedir,
-                                            subdir=subdir, prefix=prefix,
-                                            extension=extension)
-        filelike = not isinstance(relative_dest, basestring)
-        if filelike:
-            self.dest = relative_dest
-        else:
-            self.dest = self._absolute_path(relative_dest)
-
-        # Call generate now that the dest attribute has been set
-        self.generate()
-
-        # Set the relative & absolute url to the thumbnail
-        if not filelike:
-            self.relative_url = \
-                iri_to_uri('/'.join(relative_dest.split(os.sep)))
-            self.absolute_url = '%s%s' % (settings.MEDIA_URL,
-                                          self.relative_url)
-
-    def _get_relative_thumbnail(self, relative_source,
-                                basedir=None, subdir=None, prefix=None,
-                                extension=None):
-        """
-        Returns the thumbnail filename including relative path.
-        """
-        return build_thumbnail_name(relative_source, self.requested_size,
-                                    self.opts, self.quality, basedir, subdir,
-                                    prefix, extension)
-
-    def _absolute_path(self, filename):
-        absolute_filename = os.path.join(settings.MEDIA_ROOT, filename)
-        return absolute_filename.encode(settings.FILE_CHARSET)
-
-    def __unicode__(self):
-        return self.absolute_url
diff --git a/apps/sorl/thumbnail/management/__init__.py b/apps/sorl/thumbnail/management/__init__.py
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/apps/sorl/thumbnail/management/commands/__init__.py b/apps/sorl/thumbnail/management/commands/__init__.py
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/apps/sorl/thumbnail/management/commands/thumbnail_cleanup.py b/apps/sorl/thumbnail/management/commands/thumbnail_cleanup.py
deleted file mode 100644 (file)
index 690c42c..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-import os
-import re
-from django.db import models
-from django.conf import settings
-from django.core.management.base import NoArgsCommand
-from sorl.thumbnail.main import get_thumbnail_setting
-
-
-try:
-    set
-except NameError:
-    from sets import Set as set     # For Python 2.3
-
-thumb_re = re.compile(r'^%s(.*)_\d{1,}x\d{1,}_[-\w]*q([1-9]\d?|100)\.jpg' %
-                      get_thumbnail_setting('PREFIX'))
-
-
-def get_thumbnail_path(path):
-    basedir = get_thumbnail_setting('BASEDIR')
-    subdir = get_thumbnail_setting('SUBDIR')
-    return os.path.join(basedir, path, subdir)
-
-
-def clean_up():
-    paths = set()
-    for app in models.get_apps():
-        model_list = models.get_models(app)
-        for model in model_list:
-            for field in model._meta.fields:
-                if isinstance(field, models.ImageField):
-                    #TODO: take care of date formatted and callable upload_to.
-                    if (not callable(field.upload_to) and
-                            field.upload_to.find("%") == -1):
-                        paths = paths.union((field.upload_to,))
-    paths = list(paths)
-    for path in paths:
-        thumbnail_path = get_thumbnail_path(path)
-        try:
-            file_list = os.listdir(os.path.join(settings.MEDIA_ROOT,
-                                                thumbnail_path))
-        except OSError:
-            continue # Dir doesn't exists, no thumbnails here.
-        for fn in file_list:
-            m = thumb_re.match(fn)
-            if m:
-                # Due to that the naming of thumbnails replaces the dot before
-                # extension with an underscore we have 2 possibilities for the
-                # original filename. If either present we do not delete
-                # suspected thumbnail.
-                # org_fn is the expected original filename w/o extension
-                # org_fn_alt is the expected original filename with extension
-                org_fn = m.group(1)
-                org_fn_exists = os.path.isfile(
-                            os.path.join(settings.MEDIA_ROOT, path, org_fn))
-
-                usc_pos = org_fn.rfind("_")
-                if usc_pos != -1:
-                    org_fn_alt = "%s.%s" % (org_fn[0:usc_pos],
-                                            org_fn[usc_pos+1:])
-                    org_fn_alt_exists = os.path.isfile(
-                        os.path.join(settings.MEDIA_ROOT, path, org_fn_alt))
-                else:
-                    org_fn_alt_exists = False
-                if not org_fn_exists and not org_fn_alt_exists:
-                    del_me = os.path.join(settings.MEDIA_ROOT,
-                                          thumbnail_path, fn)
-                    os.remove(del_me)
-
-
-class Command(NoArgsCommand):
-    help = "Deletes thumbnails that no longer have an original file."
-    requires_model_validation = False
-
-    def handle_noargs(self, **options):
-        clean_up()
diff --git a/apps/sorl/thumbnail/models.py b/apps/sorl/thumbnail/models.py
deleted file mode 100644 (file)
index ec325fd..0000000
+++ /dev/null
@@ -1 +0,0 @@
-# Needs a models.py file so that tests are picked up.
diff --git a/apps/sorl/thumbnail/processors.py b/apps/sorl/thumbnail/processors.py
deleted file mode 100644 (file)
index a6c1741..0000000
+++ /dev/null
@@ -1,130 +0,0 @@
-from PIL import Image, ImageFilter, ImageChops
-from sorl.thumbnail import utils
-import re
-
-
-def dynamic_import(names):
-    imported = []
-    for name in names:
-        # Use rfind rather than rsplit for Python 2.3 compatibility.
-        lastdot = name.rfind('.')
-        modname, attrname = name[:lastdot], name[lastdot + 1:]
-        mod = __import__(modname, {}, {}, [''])
-        imported.append(getattr(mod, attrname))
-    return imported
-
-
-def get_valid_options(processors):
-    """
-    Returns a list containing unique valid options from a list of processors
-    in correct order.
-    """
-    valid_options = []
-    for processor in processors:
-        if hasattr(processor, 'valid_options'):
-            valid_options.extend([opt for opt in processor.valid_options
-                                  if opt not in valid_options])
-    return valid_options
-
-
-def colorspace(im, requested_size, opts):
-    if 'bw' in opts and im.mode != "L":
-        im = im.convert("L")
-    elif im.mode not in ("L", "RGB", "RGBA"):
-        im = im.convert("RGB")
-    return im
-colorspace.valid_options = ('bw',)
-
-
-def autocrop(im, requested_size, opts):
-    if 'autocrop' in opts:
-        bw = im.convert("1")
-        bw = bw.filter(ImageFilter.MedianFilter)
-        # white bg
-        bg = Image.new("1", im.size, 255)
-        diff = ImageChops.difference(bw, bg)
-        bbox = diff.getbbox()
-        if bbox:
-            im = im.crop(bbox)
-    return im
-autocrop.valid_options = ('autocrop',)
-
-
-def scale_and_crop(im, requested_size, opts):
-    x, y = [float(v) for v in im.size]
-    xr, yr = [float(v) for v in requested_size]
-
-    if 'crop' in opts or 'max' in opts:
-        r = max(xr / x, yr / y)
-    else:
-        r = min(xr / x, yr / y)
-
-    if r < 1.0 or (r > 1.0 and 'upscale' in opts):
-        im = im.resize((int(x * r), int(y * r)), resample=Image.ANTIALIAS)
-
-    crop = opts.get('crop') or 'crop' in opts
-    if crop:
-        # Difference (for x and y) between new image size and requested size.
-        x, y = [float(v) for v in im.size]
-        dx, dy = (x - min(x, xr)), (y - min(y, yr))
-        if dx or dy:
-            # Center cropping (default).
-            ex, ey = dx / 2, dy / 2
-            box = [ex, ey, x - ex, y - ey]
-            # See if an edge cropping argument was provided.
-            edge_crop = (isinstance(crop, basestring) and
-                           re.match(r'(?:(-?)(\d+))?,(?:(-?)(\d+))?$', crop))
-            if edge_crop and filter(None, edge_crop.groups()):
-                x_right, x_crop, y_bottom, y_crop = edge_crop.groups()
-                if x_crop:
-                    offset = min(x * int(x_crop) / 100, dx)
-                    if x_right:
-                        box[0] = dx - offset
-                        box[2] = x - offset
-                    else:
-                        box[0] = offset
-                        box[2] = x - (dx - offset)
-                if y_crop:
-                    offset = min(y * int(y_crop) / 100, dy)
-                    if y_bottom:
-                        box[1] = dy - offset
-                        box[3] = y - offset
-                    else:
-                        box[1] = offset
-                        box[3] = y - (dy - offset)
-            # See if the image should be "smart cropped".
-            elif crop == 'smart':
-                left = top = 0
-                right, bottom = x, y
-                while dx:
-                    slice = min(dx, 10)
-                    l_sl = im.crop((0, 0, slice, y))
-                    r_sl = im.crop((x - slice, 0, x, y))
-                    if utils.image_entropy(l_sl) >= utils.image_entropy(r_sl):
-                        right -= slice
-                    else:
-                        left += slice
-                    dx -= slice
-                while dy:
-                    slice = min(dy, 10)
-                    t_sl = im.crop((0, 0, x, slice))
-                    b_sl = im.crop((0, y - slice, x, y))
-                    if utils.image_entropy(t_sl) >= utils.image_entropy(b_sl):
-                        bottom -= slice
-                    else:
-                        top += slice
-                    dy -= slice
-                box = (left, top, right, bottom)
-            # Finally, crop the image!
-            im = im.crop([int(v) for v in box])
-    return im
-scale_and_crop.valid_options = ('crop', 'upscale', 'max')
-
-
-def filters(im, requested_size, opts):
-    if 'detail' in opts:
-        im = im.filter(ImageFilter.DETAIL)
-    if 'sharpen' in opts:
-        im = im.filter(ImageFilter.SHARPEN)
-    return im
-filters.valid_options = ('detail', 'sharpen')
diff --git a/apps/sorl/thumbnail/templatetags/__init__.py b/apps/sorl/thumbnail/templatetags/__init__.py
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/apps/sorl/thumbnail/templatetags/thumbnail.py b/apps/sorl/thumbnail/templatetags/thumbnail.py
deleted file mode 100755 (executable)
index e7c2177..0000000
+++ /dev/null
@@ -1,251 +0,0 @@
-import re
-import math
-from django.template import Library, Node, VariableDoesNotExist, \
-    TemplateSyntaxError
-from sorl.thumbnail.main import DjangoThumbnail, get_thumbnail_setting
-from sorl.thumbnail.processors import dynamic_import, get_valid_options
-from sorl.thumbnail.utils import split_args
-
-register = Library()
-
-size_pat = re.compile(r'(\d+)x(\d+)$')
-
-filesize_formats = ['k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y']
-filesize_long_formats = {
-    'k': 'kilo', 'M': 'mega', 'G': 'giga', 'T': 'tera', 'P': 'peta',
-    'E': 'exa', 'Z': 'zetta', 'Y': 'yotta',
-}
-
-try:
-    PROCESSORS = dynamic_import(get_thumbnail_setting('PROCESSORS'))
-    VALID_OPTIONS = get_valid_options(PROCESSORS)
-except:
-    if get_thumbnail_setting('DEBUG'):
-        raise
-    else:
-        PROCESSORS = []
-        VALID_OPTIONS = []
-TAG_SETTINGS = ['quality']
-
-
-class ThumbnailNode(Node):
-    def __init__(self, source_var, size_var, opts=None,
-                 context_name=None, **kwargs):
-        self.source_var = source_var
-        self.size_var = size_var
-        self.opts = opts
-        self.context_name = context_name
-        self.kwargs = kwargs
-
-    def render(self, context):
-        # Note that this isn't a global constant because we need to change the
-        # value for tests.
-        DEBUG = get_thumbnail_setting('DEBUG')
-        try:
-            # A file object will be allowed in DjangoThumbnail class
-            relative_source = self.source_var.resolve(context)
-        except VariableDoesNotExist:
-            if DEBUG:
-                raise VariableDoesNotExist("Variable '%s' does not exist." %
-                        self.source_var)
-            else:
-                relative_source = None
-        try:
-            requested_size = self.size_var.resolve(context)
-        except VariableDoesNotExist:
-            if DEBUG:
-                raise TemplateSyntaxError("Size argument '%s' is not a"
-                        " valid size nor a valid variable." % self.size_var)
-            else:
-                requested_size = None
-        # Size variable can be either a tuple/list of two integers or a valid
-        # string, only the string is checked.
-        else:
-            if isinstance(requested_size, basestring):
-                m = size_pat.match(requested_size)
-                if m:
-                    requested_size = (int(m.group(1)), int(m.group(2)))
-                elif DEBUG:
-                    raise TemplateSyntaxError("Variable '%s' was resolved but "
-                            "'%s' is not a valid size." %
-                            (self.size_var, requested_size))
-                else:
-                    requested_size = None
-        if relative_source is None or requested_size is None:
-            thumbnail = ''
-        else:
-            try:
-                kwargs = {}
-                for key, value in self.kwargs.items():
-                    kwargs[key] = value.resolve(context)
-                opts = dict([(k, v and v.resolve(context))
-                             for k, v in self.opts.items()])
-                thumbnail = DjangoThumbnail(relative_source, requested_size,
-                                opts=opts, processors=PROCESSORS, **kwargs)
-            except:
-                if DEBUG:
-                    raise
-                else:
-                    thumbnail = ''
-        # Return the thumbnail class, or put it on the context
-        if self.context_name is None:
-            return thumbnail
-        # We need to get here so we don't have old values in the context
-        # variable.
-        context[self.context_name] = thumbnail
-        return ''
-
-
-def thumbnail(parser, token):
-    """
-    Creates a thumbnail of for an ImageField.
-
-    To just output the absolute url to the thumbnail::
-
-        {% thumbnail image 80x80 %}
-
-    After the image path and dimensions, you can put any options::
-
-        {% thumbnail image 80x80 quality=95 crop %}
-
-    To put the DjangoThumbnail class on the context instead of just rendering
-    the absolute url, finish the tag with ``as [context_var_name]``::
-
-        {% thumbnail image 80x80 as thumb %}
-        {{ thumb.width }} x {{ thumb.height }}
-    """
-    args = token.split_contents()
-    tag = args[0]
-    # Check to see if we're setting to a context variable.
-    if len(args) > 4 and args[-2] == 'as':
-        context_name = args[-1]
-        args = args[:-2]
-    else:
-        context_name = None
-
-    if len(args) < 3:
-        raise TemplateSyntaxError("Invalid syntax. Expected "
-            "'{%% %s source size [option1 option2 ...] %%}' or "
-            "'{%% %s source size [option1 option2 ...] as variable %%}'" %
-            (tag, tag))
-
-    # Get the source image path and requested size.
-    source_var = parser.compile_filter(args[1])
-    # If the size argument was a correct static format, wrap it in quotes so
-    # that it is compiled correctly.
-    m = size_pat.match(args[2])
-    if m:
-        args[2] = '"%s"' % args[2]
-    size_var = parser.compile_filter(args[2])
-
-    # Get the options.
-    args_list = split_args(args[3:]).items()
-
-    # Check the options.
-    opts = {}
-    kwargs = {} # key,values here override settings and defaults
-
-    for arg, value in args_list:
-        value = value and parser.compile_filter(value)
-        if arg in TAG_SETTINGS and value is not None:
-            kwargs[str(arg)] = value
-            continue
-        if arg in VALID_OPTIONS:
-            opts[arg] = value
-        else:
-            raise TemplateSyntaxError("'%s' tag received a bad argument: "
-                                      "'%s'" % (tag, arg))
-    return ThumbnailNode(source_var, size_var, opts=opts,
-                         context_name=context_name, **kwargs)
-
-
-def filesize(bytes, format='auto1024'):
-    """
-    Returns the number of bytes in either the nearest unit or a specific unit
-    (depending on the chosen format method).
-
-    Acceptable formats are:
-
-    auto1024, auto1000
-      convert to the nearest unit, appending the abbreviated unit name to the
-      string (e.g. '2 KiB' or '2 kB').
-      auto1024 is the default format.
-    auto1024long, auto1000long
-      convert to the nearest multiple of 1024 or 1000, appending the correctly
-      pluralized unit name to the string (e.g. '2 kibibytes' or '2 kilobytes').
-    kB, MB, GB, TB, PB, EB, ZB or YB
-      convert to the exact unit (using multiples of 1000).
-    KiB, MiB, GiB, TiB, PiB, EiB, ZiB or YiB
-      convert to the exact unit (using multiples of 1024).
-
-    The auto1024 and auto1000 formats return a string, appending the correct
-    unit to the value. All other formats return the floating point value.
-
-    If an invalid format is specified, the bytes are returned unchanged.
-    """
-    format_len = len(format)
-    # Check for valid format
-    if format_len in (2, 3):
-        if format_len == 3 and format[0] == 'K':
-            format = 'k%s' % format[1:]
-        if not format[-1] == 'B' or format[0] not in filesize_formats:
-            return bytes
-        if format_len == 3 and format[1] != 'i':
-            return bytes
-    elif format not in ('auto1024', 'auto1000',
-                        'auto1024long', 'auto1000long'):
-        return bytes
-    # Check for valid bytes
-    try:
-        bytes = long(bytes)
-    except (ValueError, TypeError):
-        return bytes
-
-    # Auto multiple of 1000 or 1024
-    if format.startswith('auto'):
-        if format[4:8] == '1000':
-            base = 1000
-        else:
-            base = 1024
-        logarithm = bytes and math.log(bytes, base) or 0
-        index = min(int(logarithm) - 1, len(filesize_formats) - 1)
-        if index >= 0:
-            if base == 1000:
-                bytes = bytes and bytes / math.pow(1000, index + 1)
-            else:
-                bytes = bytes >> (10 * (index))
-                bytes = bytes and bytes / 1024.0
-            unit = filesize_formats[index]
-        else:
-            # Change the base to 1000 so the unit will just output 'B' not 'iB'
-            base = 1000
-            unit = ''
-        if bytes >= 10 or ('%.1f' % bytes).endswith('.0'):
-            bytes = '%.0f' % bytes
-        else:
-            bytes = '%.1f' % bytes
-        if format.endswith('long'):
-            unit = filesize_long_formats.get(unit, '')
-            if base == 1024 and unit:
-                unit = '%sbi' % unit[:2]
-            unit = '%sbyte%s' % (unit, bytes != '1' and 's' or '')
-        else:
-            unit = '%s%s' % (base == 1024 and unit.upper() or unit,
-                             base == 1024 and 'iB' or 'B')
-
-        return '%s %s' % (bytes, unit)
-
-    if bytes == 0:
-        return bytes
-    base = filesize_formats.index(format[0]) + 1
-    # Exact multiple of 1000
-    if format_len == 2:
-        return bytes / (1000.0 ** base)
-    # Exact multiple of 1024
-    elif format_len == 3:
-        bytes = bytes >> (10 * (base - 1))
-        return bytes / 1024.0
-
-
-register.tag(thumbnail)
-register.filter(filesize)
diff --git a/apps/sorl/thumbnail/tests/__init__.py b/apps/sorl/thumbnail/tests/__init__.py
deleted file mode 100644 (file)
index 98f1cbd..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-# For these tests to run successfully, two conditions must be met:
-# 1. MEDIA_URL and MEDIA_ROOT must be set in settings
-# 2. The user running the tests must have read/write access to MEDIA_ROOT
-
-# Unit tests:
-from sorl.thumbnail.tests.classes import ThumbnailTest, DjangoThumbnailTest
-from sorl.thumbnail.tests.templatetags import ThumbnailTagTest
-from sorl.thumbnail.tests.fields import FieldTest, \
-    ImageWithThumbnailsFieldTest, ThumbnailFieldTest
-# Doc tests:
-from sorl.thumbnail.tests.utils import utils_tests
-from sorl.thumbnail.tests.templatetags import filesize_tests
-__test__ = {
-    'utils_tests': utils_tests,
-    'filesize_tests': filesize_tests,
-}
diff --git a/apps/sorl/thumbnail/tests/base.py b/apps/sorl/thumbnail/tests/base.py
deleted file mode 100644 (file)
index 44a2fa2..0000000
+++ /dev/null
@@ -1,105 +0,0 @@
-import unittest
-import os
-from PIL import Image
-from django.conf import settings
-from sorl.thumbnail.base import Thumbnail
-
-try:
-    set
-except NameError:
-    from sets import Set as set     # For Python 2.3
-
-
-def get_default_settings():
-    from sorl.thumbnail import defaults
-    def_settings = {}
-    for key in dir(defaults):
-        if key == key.upper() and key not in ['WVPS', 'CONVERT']:
-            def_settings[key] = getattr(defaults, key)
-    return def_settings
-
-
-DEFAULT_THUMBNAIL_SETTINGS = get_default_settings()
-RELATIVE_PIC_NAME = "sorl-thumbnail-test_source.jpg"
-PIC_NAME = os.path.join(settings.MEDIA_ROOT, RELATIVE_PIC_NAME)
-THUMB_NAME = os.path.join(settings.MEDIA_ROOT, "sorl-thumbnail-test_%02d.jpg")
-PIC_SIZE = (800, 600)
-
-
-class ChangeSettings:
-    def __init__(self):
-        self.default_settings = DEFAULT_THUMBNAIL_SETTINGS.copy()
-
-    def change(self, override=None):
-        if override is not None:
-            self.default_settings.update(override)
-        for setting, default in self.default_settings.items():
-            settings_s = 'THUMBNAIL_%s' % setting
-            self_s = 'original_%s' % setting
-            if hasattr(settings, settings_s) and not hasattr(self, self_s):
-                setattr(self, self_s, getattr(settings, settings_s))
-            if hasattr(settings, settings_s) or \
-               default != DEFAULT_THUMBNAIL_SETTINGS[setting]:
-                setattr(settings, settings_s, default)
-
-    def revert(self):
-        for setting in self.default_settings:
-            settings_s = 'THUMBNAIL_%s' % setting
-            self_s = 'original_%s' % setting
-            if hasattr(self, self_s):
-                setattr(settings, settings_s, getattr(self, self_s))
-                delattr(self, self_s)
-
-
-class BaseTest(unittest.TestCase):
-    def setUp(self):
-        self.images_to_delete = set()
-        # Create the test image
-        Image.new('RGB', PIC_SIZE).save(PIC_NAME, 'JPEG')
-        self.images_to_delete.add(PIC_NAME)
-        # Change settings so we know they will be constant
-        self.change_settings = ChangeSettings()
-        self.change_settings.change()
-
-    def verify_thumbnail(self, expected_size, thumbnail=None,
-                         expected_filename=None, expected_mode=None):
-        assert thumbnail is not None or expected_filename is not None, \
-            'verify_thumbnail should be passed at least a thumbnail or an' \
-            'expected filename.'
-
-        if thumbnail is not None:
-            # Verify that the templatetag method returned a Thumbnail instance
-            self.assertTrue(isinstance(thumbnail, Thumbnail))
-            thumb_name = thumbnail.dest
-        else:
-            thumb_name = expected_filename
-
-        if isinstance(thumb_name, basestring):
-            # Verify that the thumbnail file exists
-            self.assert_(os.path.isfile(thumb_name),
-                         'Thumbnail file not found')
-
-            # Remember to delete the file
-            self.images_to_delete.add(thumb_name)
-
-            # If we got an expected_filename, check that it is right
-            if expected_filename is not None and thumbnail is not None:
-                self.assertEqual(thumbnail.dest, expected_filename)
-
-        image = Image.open(thumb_name)
-
-        # Verify the thumbnail has the expected dimensions
-        self.assertEqual(image.size, expected_size)
-
-        if expected_mode is not None:
-            self.assertEqual(image.mode, expected_mode)
-
-    def tearDown(self):
-        # Remove all the files that have been created
-        for image in self.images_to_delete:
-            try:
-                os.remove(image)
-            except:
-                pass
-        # Change settings back to original
-        self.change_settings.revert()
diff --git a/apps/sorl/thumbnail/tests/classes.py b/apps/sorl/thumbnail/tests/classes.py
deleted file mode 100644 (file)
index d15dd19..0000000
+++ /dev/null
@@ -1,175 +0,0 @@
-# -*- coding: utf-8 -*-
-import os
-import time
-from StringIO import StringIO
-
-from PIL import Image
-from django.conf import settings
-
-from sorl.thumbnail.base import Thumbnail
-from sorl.thumbnail.main import DjangoThumbnail, get_thumbnail_setting
-from sorl.thumbnail.processors import dynamic_import, get_valid_options
-from sorl.thumbnail.tests.base import BaseTest, RELATIVE_PIC_NAME, PIC_NAME,\
-    THUMB_NAME, PIC_SIZE
-
-
-class ThumbnailTest(BaseTest):
-    def testThumbnails(self):
-        # Thumbnail
-        thumb = Thumbnail(source=PIC_NAME, dest=THUMB_NAME % 1,
-                          requested_size=(240, 240))
-        self.verify_thumbnail((240, 180), thumb)
-
-        # Cropped thumbnail
-        thumb = Thumbnail(source=PIC_NAME, dest=THUMB_NAME % 2,
-                          requested_size=(240, 240), opts=['crop'])
-        self.verify_thumbnail((240, 240), thumb)
-
-        # Thumbnail with altered JPEG quality
-        thumb = Thumbnail(source=PIC_NAME, dest=THUMB_NAME % 3,
-                          requested_size=(240, 240), quality=95)
-        self.verify_thumbnail((240, 180), thumb)
-
-    def testRegeneration(self):
-        # Create thumbnail
-        thumb_name = THUMB_NAME % 4
-        thumb_size = (240, 240)
-        Thumbnail(source=PIC_NAME, dest=thumb_name, requested_size=thumb_size)
-        self.images_to_delete.add(thumb_name)
-        thumb_mtime = os.path.getmtime(thumb_name)
-        time.sleep(1)
-
-        # Create another instance, shouldn't generate a new thumb
-        Thumbnail(source=PIC_NAME, dest=thumb_name, requested_size=thumb_size)
-        self.assertEqual(os.path.getmtime(thumb_name), thumb_mtime)
-
-        # Recreate the source image, then see if a new thumb is generated
-        Image.new('RGB', PIC_SIZE).save(PIC_NAME, 'JPEG')
-        Thumbnail(source=PIC_NAME, dest=thumb_name, requested_size=thumb_size)
-        self.assertNotEqual(os.path.getmtime(thumb_name), thumb_mtime)
-
-    def testFilelikeDest(self):
-        # Thumbnail
-        filelike_dest = StringIO()
-        thumb = Thumbnail(source=PIC_NAME, dest=filelike_dest,
-                          requested_size=(240, 240))
-        self.verify_thumbnail((240, 180), thumb)
-
-    def testRGBA(self):
-        # RGBA image
-        rgba_pic_name = os.path.join(settings.MEDIA_ROOT,
-                                     'sorl-thumbnail-test_rgba_source.png')
-        Image.new('RGBA', PIC_SIZE).save(rgba_pic_name)
-        self.images_to_delete.add(rgba_pic_name)
-        # Create thumb and verify it's still RGBA
-        rgba_thumb_name = os.path.join(settings.MEDIA_ROOT,
-                                       'sorl-thumbnail-test_rgba_dest.png')
-        thumb = Thumbnail(source=rgba_pic_name, dest=rgba_thumb_name,
-                          requested_size=(240, 240))
-        self.verify_thumbnail((240, 180), thumb, expected_mode='RGBA')
-
-
-class DjangoThumbnailTest(BaseTest):
-    def setUp(self):
-        super(DjangoThumbnailTest, self).setUp()
-        # Add another source image in a sub-directory for testing subdir and
-        # basedir.
-        self.sub_dir = os.path.join(settings.MEDIA_ROOT, 'test_thumbnail')
-        try:
-            os.mkdir(self.sub_dir)
-        except OSError:
-            pass
-        self.pic_subdir = os.path.join(self.sub_dir, RELATIVE_PIC_NAME)
-        Image.new('RGB', PIC_SIZE).save(self.pic_subdir, 'JPEG')
-        self.images_to_delete.add(self.pic_subdir)
-
-    def testFilenameGeneration(self):
-        basename = RELATIVE_PIC_NAME.replace('.', '_')
-        # Basic filename
-        thumb = DjangoThumbnail(relative_source=RELATIVE_PIC_NAME,
-                                requested_size=(240, 120))
-        expected = os.path.join(settings.MEDIA_ROOT, basename)
-        expected += '_240x120_q85.jpg'
-        self.verify_thumbnail((160, 120), thumb, expected_filename=expected)
-
-        # Changed quality and cropped
-        thumb = DjangoThumbnail(relative_source=RELATIVE_PIC_NAME,
-                                requested_size=(240, 120), opts=['crop'],
-                                quality=95)
-        expected = os.path.join(settings.MEDIA_ROOT, basename)
-        expected += '_240x120_crop_q95.jpg'
-        self.verify_thumbnail((240, 120), thumb, expected_filename=expected)
-
-        # All options on
-        processors = dynamic_import(get_thumbnail_setting('PROCESSORS'))
-        valid_options = get_valid_options(processors)
-
-        thumb = DjangoThumbnail(relative_source=RELATIVE_PIC_NAME,
-                                requested_size=(240, 120), opts=valid_options)
-        expected = (os.path.join(settings.MEDIA_ROOT, basename) + '_240x120_'
-                    'autocrop_bw_crop_detail_max_sharpen_upscale_q85.jpg')
-        self.verify_thumbnail((240, 120), thumb, expected_filename=expected)
-
-        # Different basedir
-        basedir = 'sorl-thumbnail-test-basedir'
-        self.change_settings.change({'BASEDIR': basedir})
-        thumb = DjangoThumbnail(relative_source=self.pic_subdir,
-                                requested_size=(240, 120))
-        expected = os.path.join(basedir, self.sub_dir, basename)
-        expected += '_240x120_q85.jpg'
-        self.verify_thumbnail((160, 120), thumb, expected_filename=expected)
-        # Different subdir
-        self.change_settings.change({'BASEDIR': '', 'SUBDIR': 'subdir'})
-        thumb = DjangoThumbnail(relative_source=self.pic_subdir,
-                                requested_size=(240, 120))
-        expected = os.path.join(settings.MEDIA_ROOT,
-                                os.path.basename(self.sub_dir), 'subdir',
-                                basename)
-        expected += '_240x120_q85.jpg'
-        self.verify_thumbnail((160, 120), thumb, expected_filename=expected)
-        # Different prefix
-        self.change_settings.change({'SUBDIR': '', 'PREFIX': 'prefix-'})
-        thumb = DjangoThumbnail(relative_source=self.pic_subdir,
-                                requested_size=(240, 120))
-        expected = os.path.join(self.sub_dir, 'prefix-' + basename)
-        expected += '_240x120_q85.jpg'
-        self.verify_thumbnail((160, 120), thumb, expected_filename=expected)
-
-    def testAlternateExtension(self):
-        basename = RELATIVE_PIC_NAME.replace('.', '_')
-        # Control JPG
-        thumb = DjangoThumbnail(relative_source=RELATIVE_PIC_NAME,
-                                requested_size=(240, 120))
-        expected = os.path.join(settings.MEDIA_ROOT, basename)
-        expected += '_240x120_q85.jpg'
-        expected_jpg = expected
-        self.verify_thumbnail((160, 120), thumb, expected_filename=expected)
-        # Test PNG
-        thumb = DjangoThumbnail(relative_source=RELATIVE_PIC_NAME,
-                                requested_size=(240, 120), extension='png')
-        expected = os.path.join(settings.MEDIA_ROOT, basename)
-        expected += '_240x120_q85.png'
-        self.verify_thumbnail((160, 120), thumb, expected_filename=expected)
-        # Compare the file size to make sure it's not just saving as a JPG with
-        # a different extension.
-        self.assertNotEqual(os.path.getsize(expected_jpg),
-                            os.path.getsize(expected))
-
-    def testUnicodeName(self):
-        unicode_name = 'sorl-thumbnail-ążśź_source.jpg'
-        unicode_path = os.path.join(settings.MEDIA_ROOT, unicode_name)
-        Image.new('RGB', PIC_SIZE).save(unicode_path)
-        self.images_to_delete.add(unicode_path)
-        thumb = DjangoThumbnail(relative_source=unicode_name,
-                                requested_size=(240, 120))
-        base_name = unicode_name.replace('.', '_')
-        expected = os.path.join(settings.MEDIA_ROOT,
-                                base_name + '_240x120_q85.jpg')
-        self.verify_thumbnail((160, 120), thumb, expected_filename=expected)
-
-    def tearDown(self):
-        super(DjangoThumbnailTest, self).tearDown()
-        subdir = os.path.join(self.sub_dir, 'subdir')
-        if os.path.exists(subdir):
-            os.rmdir(subdir)
-        os.rmdir(self.sub_dir)
diff --git a/apps/sorl/thumbnail/tests/fields.py b/apps/sorl/thumbnail/tests/fields.py
deleted file mode 100644 (file)
index 425f555..0000000
+++ /dev/null
@@ -1,131 +0,0 @@
-import os.path
-
-from django.db import models
-from django.conf import settings
-from django.core.files.base import ContentFile
-
-from sorl.thumbnail.fields import ImageWithThumbnailsField, ThumbnailField
-from sorl.thumbnail.tests.base import BaseTest, RELATIVE_PIC_NAME, PIC_NAME
-
-thumbnail = {
-    'size': (50, 50),
-}
-extra_thumbnails = {
-    'admin': {
-        'size': (30, 30),
-        'options': ('crop',),
-    }
-}
-extension_thumbnail = thumbnail.copy()
-extension_thumbnail['extension'] = 'png'
-
-
-# Temporary models for field_tests
-class TestThumbnailFieldModel(models.Model):
-    avatar = ThumbnailField(upload_to='test', size=(300, 300))
-    photo = ImageWithThumbnailsField(upload_to='test', thumbnail=thumbnail,
-                                     extra_thumbnails=extra_thumbnails)
-
-
-class TestThumbnailFieldExtensionModel(models.Model):
-    photo = ImageWithThumbnailsField(upload_to='test',
-                                     thumbnail=extension_thumbnail,
-                                     extra_thumbnails=extra_thumbnails)
-
-
-class TestThumbnailFieldGenerateModel(models.Model):
-    photo = ImageWithThumbnailsField(upload_to='test', thumbnail=thumbnail,
-                                     extra_thumbnails=extra_thumbnails,
-                                     generate_on_save=True)
-
-
-class FieldTest(BaseTest):
-    """
-    Test the base field functionality. These use an ImageWithThumbnailsField
-    but all the functionality tested is from BaseThumbnailField.
-    """
-    def test_extra_thumbnails(self):
-        model = TestThumbnailFieldModel(photo=RELATIVE_PIC_NAME)
-        self.assertTrue('admin' in model.photo.extra_thumbnails)
-        thumb = model.photo.extra_thumbnails['admin']
-        tag = model.photo.extra_thumbnails_tag['admin']
-        expected_filename = os.path.join(settings.MEDIA_ROOT,
-            'sorl-thumbnail-test_source_jpg_30x30_crop_q85.jpg')
-        self.verify_thumbnail((30, 30), thumb, expected_filename)
-        expected_tag = '<img src="%s" width="30" height="30" alt="" />' % \
-            '/'.join((settings.MEDIA_URL.rstrip('/'),
-                      'sorl-thumbnail-test_source_jpg_30x30_crop_q85.jpg'))
-        self.assertEqual(tag, expected_tag)
-
-    def test_extension(self):
-        model = TestThumbnailFieldExtensionModel(photo=RELATIVE_PIC_NAME)
-        thumb = model.photo.thumbnail
-        tag = model.photo.thumbnail_tag
-        expected_filename = os.path.join(settings.MEDIA_ROOT,
-            'sorl-thumbnail-test_source_jpg_50x50_q85.png')
-        self.verify_thumbnail((50, 37), thumb, expected_filename)
-        expected_tag = '<img src="%s" width="50" height="37" alt="" />' % \
-            '/'.join((settings.MEDIA_URL.rstrip('/'),
-                      'sorl-thumbnail-test_source_jpg_50x50_q85.png'))
-        self.assertEqual(tag, expected_tag)
-
-    def test_delete_thumbnails(self):
-        model = TestThumbnailFieldModel(photo=RELATIVE_PIC_NAME)
-        thumb_file = model.photo.thumbnail.dest
-        open(thumb_file, 'wb').close()
-        self.assert_(os.path.exists(thumb_file))
-        model.photo.delete(save=False)
-        self.assertFalse(os.path.exists(thumb_file))
-
-    def test_generate_on_save(self):
-        main_thumb = os.path.join(settings.MEDIA_ROOT, 'test',
-                        'sorl-thumbnail-test_source_jpg_50x50_q85.jpg')
-        admin_thumb = os.path.join(settings.MEDIA_ROOT, 'test',
-                        'sorl-thumbnail-test_source_jpg_30x30_crop_q85.jpg')
-        self.images_to_delete.add(main_thumb)
-        self.images_to_delete.add(admin_thumb)
-        # Default setting is to only generate when the thumbnail is used.
-        model = TestThumbnailFieldModel()
-        source = ContentFile(open(PIC_NAME).read())
-        model.photo.save(RELATIVE_PIC_NAME, source, save=False)
-        self.images_to_delete.add(model.photo.path)
-        self.assertFalse(os.path.exists(main_thumb))
-        self.assertFalse(os.path.exists(admin_thumb))
-        os.remove(model.photo.path)
-        # But it's easy to set it up the other way...
-        model = TestThumbnailFieldGenerateModel()
-        source = ContentFile(open(PIC_NAME).read())
-        model.photo.save(RELATIVE_PIC_NAME, source, save=False)
-        self.assert_(os.path.exists(main_thumb))
-        self.assert_(os.path.exists(admin_thumb))
-
-
-class ImageWithThumbnailsFieldTest(BaseTest):
-    def test_thumbnail(self):
-        model = TestThumbnailFieldModel(photo=RELATIVE_PIC_NAME)
-        thumb = model.photo.thumbnail
-        tag = model.photo.thumbnail_tag
-        base_name = RELATIVE_PIC_NAME.replace('.', '_')
-        expected_filename = os.path.join(settings.MEDIA_ROOT,
-                                         '%s_50x50_q85.jpg' % base_name)
-        self.verify_thumbnail((50, 37), thumb, expected_filename)
-        expected_tag = ('<img src="%s" width="50" height="37" alt="" />' %
-                        '/'.join([settings.MEDIA_URL.rstrip('/'),
-                                  '%s_50x50_q85.jpg' % base_name]))
-        self.assertEqual(tag, expected_tag)
-
-
-class ThumbnailFieldTest(BaseTest):
-    def test_thumbnail(self):
-        model = TestThumbnailFieldModel()
-        source = ContentFile(open(PIC_NAME).read())
-        dest_name = 'sorl-thumbnail-test_dest.jpg'
-        model.avatar.save(dest_name, source, save=False)
-        expected_filename = os.path.join(model.avatar.path)
-        self.verify_thumbnail((300, 225), expected_filename=expected_filename)
-
-        tag = model.avatar.thumbnail_tag
-        expected_tag = ('<img src="%s" width="300" height="225" alt="" />' %
-                        '/'.join([settings.MEDIA_URL.rstrip('/'), 'test',
-                                  dest_name]))
-        self.assertEqual(tag, expected_tag)
diff --git a/apps/sorl/thumbnail/tests/templatetags.py b/apps/sorl/thumbnail/tests/templatetags.py
deleted file mode 100644 (file)
index 5d1a1cb..0000000
+++ /dev/null
@@ -1,312 +0,0 @@
-import os
-from django.conf import settings
-from django.template import Template, Context, TemplateSyntaxError
-from sorl.thumbnail.tests.classes import BaseTest, RELATIVE_PIC_NAME
-
-
-class ThumbnailTagTest(BaseTest):
-    def render_template(self, source):
-        context = Context({
-            'source': RELATIVE_PIC_NAME,
-            'invalid_source': 'not%s' % RELATIVE_PIC_NAME,
-            'size': (90, 100),
-            'invalid_size': (90, 'fish'),
-            'strsize': '80x90',
-            'invalid_strsize': ('1notasize2'),
-            'invalid_q': 'notanumber'})
-        source = '{% load thumbnail %}' + source
-        return Template(source).render(context)
-
-    def testTagInvalid(self):
-        # No args, or wrong number of args
-        src = '{% thumbnail %}'
-        self.assertRaises(TemplateSyntaxError, self.render_template, src)
-        src = '{% thumbnail source %}'
-        self.assertRaises(TemplateSyntaxError, self.render_template, src)
-        src = '{% thumbnail source 80x80 as variable crop %}'
-        self.assertRaises(TemplateSyntaxError, self.render_template, src)
-
-        # Invalid option
-        src = '{% thumbnail source 240x200 invalid %}'
-        self.assertRaises(TemplateSyntaxError, self.render_template, src)
-
-        # Old comma separated options format can only have an = for quality
-        src = '{% thumbnail source 80x80 crop=1,quality=1 %}'
-        self.assertRaises(TemplateSyntaxError, self.render_template, src)
-
-        # Invalid quality
-        src_invalid = '{% thumbnail source 240x200 quality=invalid_q %}'
-        src_missing = '{% thumbnail source 240x200 quality=missing_q %}'
-        # ...with THUMBNAIL_DEBUG = False
-        self.assertEqual(self.render_template(src_invalid), '')
-        self.assertEqual(self.render_template(src_missing), '')
-        # ...and with THUMBNAIL_DEBUG = True
-        self.change_settings.change({'DEBUG': True})
-        self.assertRaises(TemplateSyntaxError, self.render_template,
-                          src_invalid)
-        self.assertRaises(TemplateSyntaxError, self.render_template,
-                          src_missing)
-
-        # Invalid source
-        src = '{% thumbnail invalid_source 80x80 %}'
-        src_on_context = '{% thumbnail invalid_source 80x80 as thumb %}'
-        # ...with THUMBNAIL_DEBUG = False
-        self.change_settings.change({'DEBUG': False})
-        self.assertEqual(self.render_template(src), '')
-        # ...and with THUMBNAIL_DEBUG = True
-        self.change_settings.change({'DEBUG': True})
-        self.assertRaises(TemplateSyntaxError, self.render_template, src)
-        self.assertRaises(TemplateSyntaxError, self.render_template,
-                          src_on_context)
-
-        # Non-existant source
-        src = '{% thumbnail non_existant_source 80x80 %}'
-        src_on_context = '{% thumbnail non_existant_source 80x80 as thumb %}'
-        # ...with THUMBNAIL_DEBUG = False
-        self.change_settings.change({'DEBUG': False})
-        self.assertEqual(self.render_template(src), '')
-        # ...and with THUMBNAIL_DEBUG = True
-        self.change_settings.change({'DEBUG': True})
-        self.assertRaises(TemplateSyntaxError, self.render_template, src)
-
-        # Invalid size as a tuple:
-        src = '{% thumbnail source invalid_size %}'
-        # ...with THUMBNAIL_DEBUG = False
-        self.change_settings.change({'DEBUG': False})
-        self.assertEqual(self.render_template(src), '')
-        # ...and THUMBNAIL_DEBUG = True
-        self.change_settings.change({'DEBUG': True})
-        self.assertRaises(TemplateSyntaxError, self.render_template, src)
-        # Invalid size as a string:
-        src = '{% thumbnail source invalid_strsize %}'
-        # ...with THUMBNAIL_DEBUG = False
-        self.change_settings.change({'DEBUG': False})
-        self.assertEqual(self.render_template(src), '')
-        # ...and THUMBNAIL_DEBUG = True
-        self.change_settings.change({'DEBUG': True})
-        self.assertRaises(TemplateSyntaxError, self.render_template, src)
-
-        # Non-existant size
-        src = '{% thumbnail source non_existant_size %}'
-        # ...with THUMBNAIL_DEBUG = False
-        self.change_settings.change({'DEBUG': False})
-        self.assertEqual(self.render_template(src), '')
-        # ...and THUMBNAIL_DEBUG = True
-        self.change_settings.change({'DEBUG': True})
-        self.assertRaises(TemplateSyntaxError, self.render_template, src)
-
-    def testTag(self):
-        expected_base = RELATIVE_PIC_NAME.replace('.', '_')
-        # Set DEBUG = True to make it easier to trace any failures
-        self.change_settings.change({'DEBUG': True})
-
-        # Basic
-        output = self.render_template('src="'
-            '{% thumbnail source 240x240 %}"')
-        expected = '%s_240x240_q85.jpg' % expected_base
-        expected_fn = os.path.join(settings.MEDIA_ROOT, expected)
-        self.verify_thumbnail((240, 180), expected_filename=expected_fn)
-        expected_url = ''.join((settings.MEDIA_URL, expected))
-        self.assertEqual(output, 'src="%s"' % expected_url)
-
-        # Size from context variable
-        # as a tuple:
-        output = self.render_template('src="'
-            '{% thumbnail source size %}"')
-        expected = '%s_90x100_q85.jpg' % expected_base
-        expected_fn = os.path.join(settings.MEDIA_ROOT, expected)
-        self.verify_thumbnail((90, 67), expected_filename=expected_fn)
-        expected_url = ''.join((settings.MEDIA_URL, expected))
-        self.assertEqual(output, 'src="%s"' % expected_url)
-        # as a string:
-        output = self.render_template('src="'
-            '{% thumbnail source strsize %}"')
-        expected = '%s_80x90_q85.jpg' % expected_base
-        expected_fn = os.path.join(settings.MEDIA_ROOT, expected)
-        self.verify_thumbnail((80, 60), expected_filename=expected_fn)
-        expected_url = ''.join((settings.MEDIA_URL, expected))
-        self.assertEqual(output, 'src="%s"' % expected_url)
-
-        # On context
-        output = self.render_template('height:'
-            '{% thumbnail source 240x240 as thumb %}{{ thumb.height }}')
-        self.assertEqual(output, 'height:180')
-
-        # With options and quality
-        output = self.render_template('src="'
-            '{% thumbnail source 240x240 sharpen crop quality=95 %}"')
-        # Note that the opts are sorted to ensure a consistent filename.
-        expected = '%s_240x240_crop_sharpen_q95.jpg' % expected_base
-        expected_fn = os.path.join(settings.MEDIA_ROOT, expected)
-        self.verify_thumbnail((240, 240), expected_filename=expected_fn)
-        expected_url = ''.join((settings.MEDIA_URL, expected))
-        self.assertEqual(output, 'src="%s"' % expected_url)
-
-        # With option and quality on context (also using its unicode method to
-        # display the url)
-        output = self.render_template(
-            '{% thumbnail source 240x240 sharpen crop quality=95 as thumb %}'
-            'width:{{ thumb.width }}, url:{{ thumb }}')
-        self.assertEqual(output, 'width:240, url:%s' % expected_url)
-
-        # Old comma separated format for options is still supported.
-        output = self.render_template(
-            '{% thumbnail source 240x240 sharpen,crop,quality=95 as thumb %}'
-            'width:{{ thumb.width }}, url:{{ thumb }}')
-        self.assertEqual(output, 'width:240, url:%s' % expected_url)
-
-filesize_tests = r"""
->>> from sorl.thumbnail.templatetags.thumbnail import filesize
-
->>> filesize('abc')
-'abc'
->>> filesize(100, 'invalid')
-100
-
->>> bytes = 20
->>> filesize(bytes)
-'20 B'
->>> filesize(bytes, 'auto1000')
-'20 B'
-
->>> bytes = 1001
->>> filesize(bytes)
-'1001 B'
->>> filesize(bytes, 'auto1000')
-'1 kB'
-
->>> bytes = 10100
->>> filesize(bytes)
-'9.9 KiB'
-
-# Note that the decimal place is only used if < 10
->>> filesize(bytes, 'auto1000')
-'10 kB'
-
->>> bytes = 190000000
->>> filesize(bytes)
-'181 MiB'
->>> filesize(bytes, 'auto1000')
-'190 MB'
-
-# 'auto*long' methods use pluralisation:
->>> filesize(1, 'auto1024long')
-'1 byte'
->>> filesize(1, 'auto1000long')
-'1 byte'
->>> filesize(2, 'auto1024long')
-'2 bytes'
->>> filesize(0, 'auto1000long')
-'0 bytes'
-
-# Test all 'auto*long' output:
->>> for i in range(1,10):
-...     print '%s, %s' % (filesize(1024**i, 'auto1024long'),
-...                       filesize(1000**i, 'auto1000long'))
-1 kibibyte, 1 kilobyte
-1 mebibyte, 1 megabyte
-1 gibibyte, 1 gigabyte
-1 tebibyte, 1 terabyte
-1 pebibyte, 1 petabyte
-1 exbibyte, 1 exabyte
-1 zebibyte, 1 zettabyte
-1 yobibyte, 1 yottabyte
-1024 yobibytes, 1000 yottabytes
-
-# Test all fixed outputs (eg 'kB' or 'MiB')
->>> from sorl.thumbnail.templatetags.thumbnail import filesize_formats,\
-...    filesize_long_formats
->>> for f in filesize_formats:
-...     print '%s (%siB, %sB):' % (filesize_long_formats[f], f.upper(), f)
-...     for i in range(0, 10):
-...         print ' %s, %s' % (filesize(1024**i, '%siB' % f.upper()),
-...                            filesize(1000**i, '%sB' % f))
-kilo (KiB, kB):
- 0.0009765625, 0.001
- 1.0, 1.0
- 1024.0, 1000.0
- 1048576.0, 1000000.0
- 1073741824.0, 1000000000.0
- 1.09951162778e+12, 1e+12
- 1.12589990684e+15, 1e+15
- 1.15292150461e+18, 1e+18
- 1.18059162072e+21, 1e+21
- 1.20892581961e+24, 1e+24
-mega (MiB, MB):
- 0.0, 1e-06
- 0.0009765625, 0.001
- 1.0, 1.0
- 1024.0, 1000.0
- 1048576.0, 1000000.0
- 1073741824.0, 1000000000.0
- 1.09951162778e+12, 1e+12
- 1.12589990684e+15, 1e+15
- 1.15292150461e+18, 1e+18
- 1.18059162072e+21, 1e+21
-giga (GiB, GB):
- 0.0, 1e-09
- 0.0, 1e-06
- 0.0009765625, 0.001
- 1.0, 1.0
- 1024.0, 1000.0
- 1048576.0, 1000000.0
- 1073741824.0, 1000000000.0
- 1.09951162778e+12, 1e+12
- 1.12589990684e+15, 1e+15
- 1.15292150461e+18, 1e+18
-tera (TiB, TB):
- 0.0, 1e-12
- 0.0, 1e-09
- 0.0, 1e-06
- 0.0009765625, 0.001
- 1.0, 1.0
- 1024.0, 1000.0
- 1048576.0, 1000000.0
- 1073741824.0, 1000000000.0
- 1.09951162778e+12, 1e+12
- 1.12589990684e+15, 1e+15
-peta (PiB, PB):
- 0.0, 1e-15
- 0.0, 1e-12
- 0.0, 1e-09
- 0.0, 1e-06
- 0.0009765625, 0.001
- 1.0, 1.0
- 1024.0, 1000.0
- 1048576.0, 1000000.0
- 1073741824.0, 1000000000.0
- 1.09951162778e+12, 1e+12
-exa (EiB, EB):
- 0.0, 1e-18
- 0.0, 1e-15
- 0.0, 1e-12
- 0.0, 1e-09
- 0.0, 1e-06
- 0.0009765625, 0.001
- 1.0, 1.0
- 1024.0, 1000.0
- 1048576.0, 1000000.0
- 1073741824.0, 1000000000.0
-zetta (ZiB, ZB):
- 0.0, 1e-21
- 0.0, 1e-18
- 0.0, 1e-15
- 0.0, 1e-12
- 0.0, 1e-09
- 0.0, 1e-06
- 0.0009765625, 0.001
- 1.0, 1.0
- 1024.0, 1000.0
- 1048576.0, 1000000.0
-yotta (YiB, YB):
- 0.0, 1e-24
- 0.0, 1e-21
- 0.0, 1e-18
- 0.0, 1e-15
- 0.0, 1e-12
- 0.0, 1e-09
- 0.0, 1e-06
- 0.0009765625, 0.001
- 1.0, 1.0
- 1024.0, 1000.0
-"""
diff --git a/apps/sorl/thumbnail/tests/utils.py b/apps/sorl/thumbnail/tests/utils.py
deleted file mode 100644 (file)
index 3a20cbb..0000000
+++ /dev/null
@@ -1,149 +0,0 @@
-from django.conf import settings
-from sorl.thumbnail.utils import *
-
-try:
-    set
-except NameError:
-    from sets import Set as set     # For Python 2.3
-
-MEDIA_ROOT_LENGTH = len(os.path.normpath(settings.MEDIA_ROOT))
-
-utils_tests = r"""
->>> from sorl.thumbnail.tests.utils import *
->>> from sorl.thumbnail.tests.base import ChangeSettings
->>> from django.conf import settings
-
->>> change_settings = ChangeSettings()
->>> change_settings.change()
-
->>> media_root = settings.MEDIA_ROOT.rstrip('/')
-
-#==============================================================================
-# Set up test images
-#==============================================================================
-
->>> make_image('test-thumbnail-utils/subdir/test_jpg_110x110_q85.jpg')
->>> make_image('test-thumbnail-utils/test_jpg_80x80_q85.jpg')
->>> make_image('test-thumbnail-utils/test_jpg_80x80_q95.jpg')
->>> make_image('test-thumbnail-utils/another_test_jpg_80x80_q85.jpg')
->>> make_image('test-thumbnail-utils/test_with_opts_jpg_80x80_crop_bw_q85.jpg')
->>> make_image('test-thumbnail-basedir/test-thumbnail-utils/test_jpg_100x100_'
-...            'q85.jpg')
->>> make_image('test-thumbnail-utils/prefix-test_jpg_120x120_q85.jpg')
-
-#==============================================================================
-# all_thumbnails()
-#==============================================================================
-
-# Find all thumbs
->>> thumb_dir = os.path.join(settings.MEDIA_ROOT, 'test-thumbnail-utils')
->>> thumbs = all_thumbnails(thumb_dir)
->>> k = thumbs.keys()
->>> k.sort()
->>> [consistent_slash(path) for path in k]
-['another_test.jpg', 'prefix-test.jpg', 'subdir/test.jpg', 'test.jpg',
- 'test_with_opts.jpg']
-
-# Find all thumbs, no recurse
->>> thumbs = all_thumbnails(thumb_dir, recursive=False)
->>> k = thumbs.keys()
->>> k.sort()
->>> k
-['another_test.jpg', 'prefix-test.jpg', 'test.jpg', 'test_with_opts.jpg']
-
-#==============================================================================
-# thumbnails_for_file()
-#==============================================================================
-
->>> output = []
->>> for thumb in thumbs['test.jpg']:
-...     thumb['rel_fn'] = strip_media_root(thumb['filename'])
-...     output.append('%(x)sx%(y)s %(quality)s %(rel_fn)s' % thumb)
->>> output.sort()
->>> output
-['80x80 85 test-thumbnail-utils/test_jpg_80x80_q85.jpg',
- '80x80 95 test-thumbnail-utils/test_jpg_80x80_q95.jpg']
-
-# Thumbnails for file
->>> output = []
->>> for thumb in thumbnails_for_file('test-thumbnail-utils/test.jpg'):
-...    output.append(strip_media_root(thumb['filename']))
->>> output.sort()
->>> output
-['test-thumbnail-utils/test_jpg_80x80_q85.jpg',
- 'test-thumbnail-utils/test_jpg_80x80_q95.jpg']
-
-# Thumbnails for file - shouldn't choke on non-existant file
->>> thumbnails_for_file('test-thumbnail-utils/non-existant.jpg')
-[]
-
-# Thumbnails for file, with basedir setting
->>> change_settings.change({'BASEDIR': 'test-thumbnail-basedir'})
->>> for thumb in thumbnails_for_file('test-thumbnail-utils/test.jpg'):
-...    print strip_media_root(thumb['filename'])
-test-thumbnail-basedir/test-thumbnail-utils/test_jpg_100x100_q85.jpg
-
-# Thumbnails for file, with subdir setting
->>> change_settings.change({'SUBDIR': 'subdir', 'BASEDIR': ''})
->>> for thumb in thumbnails_for_file('test-thumbnail-utils/test.jpg'):
-...    print strip_media_root(thumb['filename'])
-test-thumbnail-utils/subdir/test_jpg_110x110_q85.jpg
-
-# Thumbnails for file, with prefix setting
->>> change_settings.change({'PREFIX': 'prefix-', 'SUBDIR': ''})
->>> for thumb in thumbnails_for_file('test-thumbnail-utils/test.jpg'):
-...    print strip_media_root(thumb['filename'])
-test-thumbnail-utils/prefix-test_jpg_120x120_q85.jpg
-
-#==============================================================================
-# Clean up images / directories
-#==============================================================================
-
->>> clean_up()
-"""
-
-images_to_delete = set()
-dirs_to_delete = []
-
-
-def make_image(relative_image):
-    absolute_image = os.path.join(settings.MEDIA_ROOT, relative_image)
-    make_dirs(os.path.dirname(relative_image))
-    open(absolute_image, 'w').close()
-    images_to_delete.add(absolute_image)
-
-
-def make_dirs(relative_path):
-    if not relative_path:
-        return
-    absolute_path = os.path.join(settings.MEDIA_ROOT, relative_path)
-    if os.path.isdir(absolute_path):
-        return
-    if absolute_path not in dirs_to_delete:
-        dirs_to_delete.append(absolute_path)
-    make_dirs(os.path.dirname(relative_path))
-    os.mkdir(absolute_path)
-
-
-def clean_up():
-    for image in images_to_delete:
-        os.remove(image)
-    for path in dirs_to_delete:
-        os.rmdir(path)
-
-
-def strip_media_root(path):
-    path = os.path.normpath(path)
-    # chop off the MEDIA_ROOT and strip any leading os.sep
-    path = path[MEDIA_ROOT_LENGTH:].lstrip(os.sep)
-    return consistent_slash(path)
-
-
-def consistent_slash(path):
-    """
-    Ensure we're always testing against the '/' os separator (otherwise tests
-    fail against Windows).
-    """
-    if os.sep != '/':
-        path = path.replace(os.sep, '/')
-    return path
diff --git a/apps/sorl/thumbnail/utils.py b/apps/sorl/thumbnail/utils.py
deleted file mode 100644 (file)
index 18b18b0..0000000
+++ /dev/null
@@ -1,170 +0,0 @@
-import math
-import os
-import re
-
-
-re_thumbnail_file = re.compile(r'(?P<source_filename>.+)_(?P<x>\d+)x(?P<y>\d+)'
-                               r'(?:_(?P<options>\w+))?_q(?P<quality>\d+)'
-                               r'(?:.[^.]+)?$')
-re_new_args = re.compile('(?<!quality)=')
-
-
-def all_thumbnails(path, recursive=True, prefix=None, subdir=None):
-    """
-    Return a dictionary referencing all files which match the thumbnail format.
-
-    Each key is a source image filename, relative to path.
-    Each value is a list of dictionaries as explained in `thumbnails_for_file`.
-    """
-    # Fall back to using thumbnail settings. These are local imports so that
-    # there is no requirement of Django to use the utils module.
-    if prefix is None:
-        from sorl.thumbnail.main import get_thumbnail_setting
-        prefix = get_thumbnail_setting('PREFIX')
-    if subdir is None:
-        from sorl.thumbnail.main import get_thumbnail_setting
-        subdir = get_thumbnail_setting('SUBDIR')
-    thumbnail_files = {}
-    if not path.endswith('/'):
-        path = '%s/' % path
-    len_path = len(path)
-    if recursive:
-        all = os.walk(path)
-    else:
-        files = []
-        for file in os.listdir(path):
-            if os.path.isfile(os.path.join(path, file)):
-                files.append(file)
-        all = [(path, [], files)]
-    for dir_, subdirs, files in all:
-        rel_dir = dir_[len_path:]
-        for file in files:
-            thumb = re_thumbnail_file.match(file)
-            if not thumb:
-                continue
-            d = thumb.groupdict()
-            source_filename = d.pop('source_filename')
-            if prefix:
-                source_path, source_filename = os.path.split(source_filename)
-                if not source_filename.startswith(prefix):
-                    continue
-                source_filename = os.path.join(source_path,
-                    source_filename[len(prefix):])
-            d['options'] = d['options'] and d['options'].split('_') or []
-            if subdir and rel_dir.endswith(subdir):
-                rel_dir = rel_dir[:-len(subdir)]
-            # Corner-case bug: if the filename didn't have an extension but did
-            # have an underscore, the last underscore will get converted to a
-            # '.'.
-            m = re.match(r'(.*)_(.*)', source_filename)
-            if m:
-                source_filename = '%s.%s' % m.groups()
-            filename = os.path.join(rel_dir, source_filename)
-            thumbnail_file = thumbnail_files.setdefault(filename, [])
-            d['filename'] = os.path.join(dir_, file)
-            thumbnail_file.append(d)
-    return thumbnail_files
-
-
-def thumbnails_for_file(relative_source_path, root=None, basedir=None,
-                        subdir=None, prefix=None):
-    """
-    Return a list of dictionaries, one for each thumbnail belonging to the
-    source image.
-
-    The following list explains each key of the dictionary:
-
-      `filename`  -- absolute thumbnail path
-      `x` and `y` -- the size of the thumbnail
-      `options`   -- list of options for this thumbnail
-      `quality`   -- quality setting for this thumbnail
-    """
-    # Fall back to using thumbnail settings. These are local imports so that
-    # there is no requirement of Django to use the utils module.
-    if root is None:
-        from django.conf import settings
-        root = settings.MEDIA_ROOT
-    if prefix is None:
-        from sorl.thumbnail.main import get_thumbnail_setting
-        prefix = get_thumbnail_setting('PREFIX')
-    if subdir is None:
-        from sorl.thumbnail.main import get_thumbnail_setting
-        subdir = get_thumbnail_setting('SUBDIR')
-    if basedir is None:
-        from sorl.thumbnail.main import get_thumbnail_setting
-        basedir = get_thumbnail_setting('BASEDIR')
-    source_dir, filename = os.path.split(relative_source_path)
-    thumbs_path = os.path.join(root, basedir, source_dir, subdir)
-    if not os.path.isdir(thumbs_path):
-        return []
-    files = all_thumbnails(thumbs_path, recursive=False, prefix=prefix,
-                           subdir='')
-    return files.get(filename, [])
-
-
-def delete_thumbnails(relative_source_path, root=None, basedir=None,
-                      subdir=None, prefix=None):
-    """
-    Delete all thumbnails for a source image.
-    """
-    thumbs = thumbnails_for_file(relative_source_path, root, basedir, subdir,
-                                 prefix)
-    return _delete_using_thumbs_list(thumbs)
-
-
-def _delete_using_thumbs_list(thumbs):
-    deleted = 0
-    for thumb_dict in thumbs:
-        filename = thumb_dict['filename']
-        try:
-            os.remove(filename)
-        except:
-            pass
-        else:
-            deleted += 1
-    return deleted
-
-
-def delete_all_thumbnails(path, recursive=True):
-    """
-    Delete all files within a path which match the thumbnails pattern.
-
-    By default, matching files from all sub-directories are also removed. To
-    only remove from the path directory, set recursive=False.
-    """
-    total = 0
-    for thumbs in all_thumbnails(path, recursive=recursive).values():
-        total += _delete_using_thumbs_list(thumbs)
-    return total
-
-
-def split_args(args):
-    """
-    Split a list of argument strings into a dictionary where each key is an
-    argument name.
-
-    An argument looks like ``crop``, ``crop="some option"`` or ``crop=my_var``.
-    Arguments which provide no value get a value of ``None``.
-    """
-    if not args:
-        return {}
-    # Handle the old comma separated argument format.
-    if len(args) == 1 and not re_new_args.search(args[0]):
-        args = args[0].split(',')
-    # Separate out the key and value for each argument.
-    args_dict = {}
-    for arg in args:
-        split_arg = arg.split('=', 1)
-        value = len(split_arg) > 1 and split_arg[1] or None
-        args_dict[split_arg[0]] = value
-    return args_dict
-
-
-def image_entropy(im):
-    """
-    Calculate the entropy of an image. Used for "smart cropping".
-    """
-    hist = im.histogram()
-    hist_size = float(sum(hist))
-    hist = [h / hist_size for h in hist]
-    return -sum([p * math.log(p, 2) for p in hist if p != 0])
diff --git a/apps/south/__init__.py b/apps/south/__init__.py
deleted file mode 100644 (file)
index 3e5972e..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-"""
-South - Useable migrations for Django apps
-"""
-
-__version__ = "0.4"
-__authors__ = ["Andrew Godwin <andrew@aeracode.org>", "Andy McCurdy <andy@andymccurdy.com>"]
diff --git a/apps/south/db/__init__.py b/apps/south/db/__init__.py
deleted file mode 100644 (file)
index 8e4d773..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-
-# Establish the common DatabaseOperations instance, which we call 'db'.
-# This code somewhat lifted from django evolution
-from django.conf import settings
-import sys
-module_name = ['south.db', settings.DATABASE_ENGINE]
-try:
-    module = __import__('.'.join(module_name),{},{},[''])
-except ImportError:
-    sys.stderr.write("There is no South database module for the engine '%s'. Please either choose a supported one, or remove South from INSTALLED_APPS.\n" % settings.DATABASE_ENGINE)
-    sys.exit(1)
-db = module.DatabaseOperations()
\ No newline at end of file
diff --git a/apps/south/db/generic.py b/apps/south/db/generic.py
deleted file mode 100644 (file)
index 4a5b512..0000000
+++ /dev/null
@@ -1,543 +0,0 @@
-
-import datetime
-from django.core.management.color import no_style
-from django.db import connection, transaction, models
-from django.db.backends.util import truncate_name
-from django.db.models.fields import NOT_PROVIDED
-from django.dispatch import dispatcher
-from django.conf import settings
-
-
-def alias(attrname):
-    """
-    Returns a function which calls 'attrname' - for function aliasing.
-    We can't just use foo = bar, as this breaks subclassing.
-    """
-    def func(self, *args, **kwds):
-        return getattr(self, attrname)(*args, **kwds)
-    return func
-
-
-class DatabaseOperations(object):
-
-    """
-    Generic SQL implementation of the DatabaseOperations.
-    Some of this code comes from Django Evolution.
-    """
-
-    # We assume the generic DB can handle DDL transactions. MySQL wil change this.
-    has_ddl_transactions = True
-
-    def __init__(self):
-        self.debug = False
-        self.deferred_sql = []
-        self.dry_run = False
-        self.pending_create_signals = []
-
-    def execute(self, sql, params=[]):
-        """
-        Executes the given SQL statement, with optional parameters.
-        If the instance's debug attribute is True, prints out what it executes.
-        """
-        cursor = connection.cursor()
-        if self.debug:
-            print "   = %s" % sql, params
-
-        if self.dry_run:
-            return []
-
-        cursor.execute(sql, params)
-        try:
-            return cursor.fetchall()
-        except:
-            return []
-
-
-    def add_deferred_sql(self, sql):
-        """
-        Add a SQL statement to the deferred list, that won't be executed until
-        this instance's execute_deferred_sql method is run.
-        """
-        self.deferred_sql.append(sql)
-
-
-    def execute_deferred_sql(self):
-        """
-        Executes all deferred SQL, resetting the deferred_sql list
-        """
-        for sql in self.deferred_sql:
-            self.execute(sql)
-
-        self.deferred_sql = []
-
-
-    def clear_deferred_sql(self):
-        """
-        Resets the deferred_sql list to empty.
-        """
-        self.deferred_sql = []
-    
-    
-    def clear_run_data(self):
-        """
-        Resets variables to how they should be before a run. Used for dry runs.
-        """
-        self.clear_deferred_sql()
-        self.pending_create_signals = []
-
-
-    def create_table(self, table_name, fields):
-        """
-        Creates the table 'table_name'. 'fields' is a tuple of fields,
-        each repsented by a 2-part tuple of field name and a
-        django.db.models.fields.Field object
-        """
-        qn = connection.ops.quote_name
-
-        # allow fields to be a dictionary
-        # removed for now - philosophical reasons (this is almost certainly not what you want)
-        #try:
-        #    fields = fields.items()
-        #except AttributeError:
-        #    pass
-
-        columns = [
-            self.column_sql(table_name, field_name, field)
-            for field_name, field in fields
-        ]
-
-        self.execute('CREATE TABLE %s (%s);' % (qn(table_name), ', '.join([col for col in columns if col])))
-
-    add_table = alias('create_table') # Alias for consistency's sake
-
-
-    def rename_table(self, old_table_name, table_name):
-        """
-        Renames the table 'old_table_name' to 'table_name'.
-        """
-        if old_table_name == table_name:
-            # No Operation
-            return
-        qn = connection.ops.quote_name
-        params = (qn(old_table_name), qn(table_name))
-        self.execute('ALTER TABLE %s RENAME TO %s;' % params)
-
-
-    def delete_table(self, table_name):
-        """
-        Deletes the table 'table_name'.
-        """
-        qn = connection.ops.quote_name
-        params = (qn(table_name), )
-        self.execute('DROP TABLE %s;' % params)
-
-    drop_table = alias('delete_table')
-
-
-    def clear_table(self, table_name):
-        """
-        Deletes all rows from 'table_name'.
-        """
-        qn = connection.ops.quote_name
-        params = (qn(table_name), )
-        self.execute('DELETE FROM %s;' % params)
-
-    add_column_string = 'ALTER TABLE %s ADD COLUMN %s;'
-
-    def add_column(self, table_name, name, field, keep_default=True):
-        """
-        Adds the column 'name' to the table 'table_name'.
-        Uses the 'field' paramater, a django.db.models.fields.Field instance,
-        to generate the necessary sql
-
-        @param table_name: The name of the table to add the column to
-        @param name: The name of the column to add
-        @param field: The field to use
-        """
-        qn = connection.ops.quote_name
-        sql = self.column_sql(table_name, name, field)
-        if sql:
-            params = (
-                qn(table_name),
-                sql,
-            )
-            sql = self.add_column_string % params
-            self.execute(sql)
-
-            # Now, drop the default if we need to
-            if not keep_default and field.default:
-                field.default = NOT_PROVIDED
-                self.alter_column(table_name, name, field, explicit_name=False)
-
-    alter_string_set_type = 'ALTER COLUMN %(column)s TYPE %(type)s'
-    alter_string_set_null = 'ALTER COLUMN %(column)s DROP NOT NULL'
-    alter_string_drop_null = 'ALTER COLUMN %(column)s SET NOT NULL'
-    allows_combined_alters = True
-
-    def alter_column(self, table_name, name, field, explicit_name=True):
-        """
-        Alters the given column name so it will match the given field.
-        Note that conversion between the two by the database must be possible.
-        Will not automatically add _id by default; to have this behavour, pass
-        explicit_name=False.
-
-        @param table_name: The name of the table to add the column to
-        @param name: The name of the column to alter
-        @param field: The new field definition to use
-        """
-
-        # hook for the field to do any resolution prior to it's attributes being queried
-        if hasattr(field, 'south_init'):
-            field.south_init()
-
-        qn = connection.ops.quote_name
-        
-        # Add _id or whatever if we need to
-        if not explicit_name:
-            field.set_attributes_from_name(name)
-            name = field.column
-
-        # First, change the type
-        params = {
-            "column": qn(name),
-            "type": field.db_type(),
-        }
-
-        # SQLs is a list of (SQL, values) pairs.
-        sqls = [(self.alter_string_set_type % params, [])]
-
-        # Next, set any default
-        if not field.null and field.has_default():
-            default = field.get_default()
-            sqls.append(('ALTER COLUMN %s SET DEFAULT %%s ' % (qn(name),), [default]))
-        else:
-            sqls.append(('ALTER COLUMN %s DROP DEFAULT' % (qn(name),), []))
-
-
-        # Next, nullity
-        params = {
-            "column": qn(name),
-            "type": field.db_type(),
-        }
-        if field.null:
-            sqls.append((self.alter_string_set_null % params, []))
-        else:
-            sqls.append((self.alter_string_drop_null % params, []))
-
-
-        # TODO: Unique
-
-        if self.allows_combined_alters:
-            sqls, values = zip(*sqls)
-            self.execute(
-                "ALTER TABLE %s %s;" % (qn(table_name), ", ".join(sqls)),
-                flatten(values),
-            )
-        else:
-            # Databases like e.g. MySQL don't like more than one alter at once.
-            for sql, values in sqls:
-                self.execute("ALTER TABLE %s %s;" % (qn(table_name), sql), values)
-
-
-    def column_sql(self, table_name, field_name, field, tablespace=''):
-        """
-        Creates the SQL snippet for a column. Used by add_column and add_table.
-        """
-        qn = connection.ops.quote_name
-
-        field.set_attributes_from_name(field_name)
-
-        # hook for the field to do any resolution prior to it's attributes being queried
-        if hasattr(field, 'south_init'):
-            field.south_init()
-
-        sql = field.db_type()
-        if sql:        
-            field_output = [qn(field.column), sql]
-            field_output.append('%sNULL' % (not field.null and 'NOT ' or ''))
-            if field.primary_key:
-                field_output.append('PRIMARY KEY')
-            elif field.unique:
-                # Instead of using UNIQUE, add a unique index with a predictable name
-                self.add_deferred_sql(
-                    self.create_index_sql(
-                        table_name,
-                        [field.column],
-                        unique = True,
-                        db_tablespace = tablespace,
-                    )
-                )
-
-            tablespace = field.db_tablespace or tablespace
-            if tablespace and connection.features.supports_tablespaces and field.unique:
-                # We must specify the index tablespace inline, because we
-                # won't be generating a CREATE INDEX statement for this field.
-                field_output.append(connection.ops.tablespace_sql(tablespace, inline=True))
-
-            sql = ' '.join(field_output)
-            sqlparams = ()
-            # if the field is "NOT NULL" and a default value is provided, create the column with it
-            # this allows the addition of a NOT NULL field to a table with existing rows
-            if not field.null and field.has_default():
-                default = field.get_default()
-                # If the default is a callable, then call it!
-                if callable(default):
-                    default = default()
-                # Now do some very cheap quoting. TODO: Redesign return values to avoid this.
-                if isinstance(default, basestring):
-                    default = "'%s'" % default.replace("'", "''")
-                elif isinstance(default, datetime.date):
-                    default = "'%s'" % default
-                sql += " DEFAULT %s"
-                sqlparams = (default)
-
-            if field.rel and self.supports_foreign_keys:
-                self.add_deferred_sql(
-                    self.foreign_key_sql(
-                        table_name,
-                        field.column,
-                        field.rel.to._meta.db_table,
-                        field.rel.to._meta.get_field(field.rel.field_name).column
-                    )
-                )
-
-            if field.db_index and not field.unique:
-                self.add_deferred_sql(self.create_index_sql(table_name, [field.column]))
-
-        if hasattr(field, 'post_create_sql'):
-            style = no_style()
-            for stmt in field.post_create_sql(style, table_name):
-                self.add_deferred_sql(stmt)
-
-        if sql:
-            return sql % sqlparams
-        else:
-            return None
-
-
-    supports_foreign_keys = True
-
-    def foreign_key_sql(self, from_table_name, from_column_name, to_table_name, to_column_name):
-        """
-        Generates a full SQL statement to add a foreign key constraint
-        """
-        qn = connection.ops.quote_name
-        constraint_name = '%s_refs_%s_%x' % (from_column_name, to_column_name, abs(hash((from_table_name, to_table_name))))
-        return 'ALTER TABLE %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s)%s;' % (
-            qn(from_table_name),
-            qn(truncate_name(constraint_name, connection.ops.max_name_length())),
-            qn(from_column_name),
-            qn(to_table_name),
-            qn(to_column_name),
-            connection.ops.deferrable_sql() # Django knows this
-        )
-
-
-    def create_index_name(self, table_name, column_names):
-        """
-        Generate a unique name for the index
-        """
-        index_unique_name = ''
-        if len(column_names) > 1:
-            index_unique_name = '_%x' % abs(hash((table_name, ','.join(column_names))))
-
-        return '%s_%s%s' % (table_name, column_names[0], index_unique_name)
-
-
-    def create_index_sql(self, table_name, column_names, unique=False, db_tablespace=''):
-        """
-        Generates a create index statement on 'table_name' for a list of 'column_names'
-        """
-        qn = connection.ops.quote_name
-        if not column_names:
-            print "No column names supplied on which to create an index"
-            return ''
-
-        if db_tablespace and connection.features.supports_tablespaces:
-            tablespace_sql = ' ' + connection.ops.tablespace_sql(db_tablespace)
-        else:
-            tablespace_sql = ''
-
-        index_name = self.create_index_name(table_name, column_names)
-        qn = connection.ops.quote_name
-        return 'CREATE %sINDEX %s ON %s (%s)%s;' % (
-            unique and 'UNIQUE ' or '',
-            qn(index_name),
-            qn(table_name),
-            ','.join([qn(field) for field in column_names]),
-            tablespace_sql
-        )
-
-    def create_index(self, table_name, column_names, unique=False, db_tablespace=''):
-        """ Executes a create index statement """
-        sql = self.create_index_sql(table_name, column_names, unique, db_tablespace)
-        self.execute(sql)
-
-
-    drop_index_string = 'DROP INDEX %(index_name)s'
-
-    def delete_index(self, table_name, column_names, db_tablespace=''):
-        """
-        Deletes an index created with create_index.
-        This is possible using only columns due to the deterministic
-        index naming function which relies on column names.
-        """
-        if isinstance(column_names, (str, unicode)):
-            column_names = [column_names]
-        name = self.create_index_name(table_name, column_names)
-        qn = connection.ops.quote_name
-        sql = self.drop_index_string % {"index_name": qn(name), "table_name": qn(table_name)}
-        self.execute(sql)
-
-    drop_index = alias('delete_index')
-
-    delete_column_string = 'ALTER TABLE %s DROP COLUMN %s CASCADE;'
-
-    def delete_column(self, table_name, name):
-        """
-        Deletes the column 'column_name' from the table 'table_name'.
-        """
-        qn = connection.ops.quote_name
-        params = (qn(table_name), qn(name))
-        self.execute(self.delete_column_string % params, [])
-
-    drop_column = alias('delete_column')
-
-
-    def rename_column(self, table_name, old, new):
-        """
-        Renames the column 'old' from the table 'table_name' to 'new'.
-        """
-        raise NotImplementedError("rename_column has no generic SQL syntax")
-
-
-    def start_transaction(self):
-        """
-        Makes sure the following commands are inside a transaction.
-        Must be followed by a (commit|rollback)_transaction call.
-        """
-        if self.dry_run:
-            return
-        transaction.commit_unless_managed()
-        transaction.enter_transaction_management()
-        transaction.managed(True)
-
-
-    def commit_transaction(self):
-        """
-        Commits the current transaction.
-        Must be preceded by a start_transaction call.
-        """
-        if self.dry_run:
-            return
-        transaction.commit()
-        transaction.leave_transaction_management()
-
-
-    def rollback_transaction(self):
-        """
-        Rolls back the current transaction.
-        Must be preceded by a start_transaction call.
-        """
-        if self.dry_run:
-            return
-        transaction.rollback()
-        transaction.leave_transaction_management()
-
-
-    def send_create_signal(self, app_label, model_names):
-        self.pending_create_signals.append((app_label, model_names))
-
-
-    def send_pending_create_signals(self):
-        for (app_label, model_names) in self.pending_create_signals:
-            self.really_send_create_signal(app_label, model_names)
-        self.pending_create_signals = []
-
-
-    def really_send_create_signal(self, app_label, model_names):
-        """
-        Sends a post_syncdb signal for the model specified.
-
-        If the model is not found (perhaps it's been deleted?),
-        no signal is sent.
-
-        TODO: The behavior of django.contrib.* apps seems flawed in that
-        they don't respect created_models.  Rather, they blindly execute
-        over all models within the app sending the signal.  This is a
-        patch we should push Django to make  For now, this should work.
-        """
-        if self.debug:
-            print " - Sending post_syncdb signal for %s: %s" % (app_label, model_names)
-        app = models.get_app(app_label)
-        if not app:
-            return
-
-        created_models = []
-        for model_name in model_names:
-            model = models.get_model(app_label, model_name)
-            if model:
-                created_models.append(model)
-
-        if created_models:
-            # syncdb defaults -- perhaps take these as options?
-            verbosity = 1
-            interactive = True
-
-            if hasattr(dispatcher, "send"):
-                dispatcher.send(signal=models.signals.post_syncdb, sender=app,
-                                app=app, created_models=created_models,
-                                verbosity=verbosity, interactive=interactive)
-            else:
-                models.signals.post_syncdb.send(sender=app,
-                                                app=app, created_models=created_models,
-                                                verbosity=verbosity, interactive=interactive)
-
-    def mock_model(self, model_name, db_table, db_tablespace='', 
-                   pk_field_name='id', pk_field_type=models.AutoField,
-                   pk_field_args=[], pk_field_kwargs={}):
-        """
-        Generates a MockModel class that provides enough information
-        to be used by a foreign key/many-to-many relationship.
-
-        Migrations should prefer to use these rather than actual models
-        as models could get deleted over time, but these can remain in
-        migration files forever.
-        """
-        class MockOptions(object):
-            def __init__(self):
-                self.db_table = db_table
-                self.db_tablespace = db_tablespace or settings.DEFAULT_TABLESPACE
-                self.object_name = model_name
-                self.module_name = model_name.lower()
-
-                if pk_field_type == models.AutoField:
-                    pk_field_kwargs['primary_key'] = True
-
-                self.pk = pk_field_type(*pk_field_args, **pk_field_kwargs)
-                self.pk.set_attributes_from_name(pk_field_name)
-                self.abstract = False
-
-            def get_field_by_name(self, field_name):
-                # we only care about the pk field
-                return (self.pk, self.model, True, False)
-
-            def get_field(self, name):
-                # we only care about the pk field
-                return self.pk
-
-        class MockModel(object):
-            _meta = None
-
-        # We need to return an actual class object here, not an instance
-        MockModel._meta = MockOptions()
-        MockModel._meta.model = MockModel
-        return MockModel
-
-# Single-level flattening of lists
-def flatten(ls):
-    nl = []
-    for l in ls:
-        nl += l
-    return nl
-
diff --git a/apps/south/db/mysql.py b/apps/south/db/mysql.py
deleted file mode 100644 (file)
index a05c071..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-
-from django.db import connection
-from django.conf import settings
-from south.db import generic
-
-class DatabaseOperations(generic.DatabaseOperations):
-
-    """
-    MySQL implementation of database operations.
-    """
-    
-    alter_string_set_type = ''
-    alter_string_set_null = 'MODIFY %(column)s %(type)s NULL;'
-    alter_string_drop_null = 'MODIFY %(column)s %(type)s NOT NULL;'
-    drop_index_string = 'DROP INDEX %(index_name)s ON %(table_name)s'
-    allows_combined_alters = False
-    has_ddl_transactions = False
-    
-    def execute(self, sql, params=[]):
-        if hasattr(settings, "DATABASE_STORAGE_ENGINE") and \
-           settings.DATABASE_STORAGE_ENGINE:
-            generic.DatabaseOperations.execute(self, "SET storage_engine=%s;" %
-                settings.DATABASE_STORAGE_ENGINE)
-        return generic.DatabaseOperations.execute(self, sql, params)
-    execute.__doc__ = generic.DatabaseOperations.execute.__doc__
-
-    def rename_column(self, table_name, old, new):
-        if old == new or self.dry_run:
-            return []
-        
-        qn = connection.ops.quote_name
-        
-        rows = [x for x in self.execute('DESCRIBE %s' % (qn(table_name),)) if x[0] == old]
-        
-        if not rows:
-            raise ValueError("No column '%s' in '%s'." % (old, table_name))
-        
-        params = (
-            qn(table_name),
-            qn(old),
-            qn(new),
-            rows[0][1],
-            rows[0][2] == "YES" and "NULL" or "NOT NULL",
-            rows[0][3] == "PRI" and "PRIMARY KEY" or "",
-            rows[0][4] and "DEFAULT " or "",
-            rows[0][4] and "%s" or "",
-            rows[0][5] or "",
-        )
-        
-        sql = 'ALTER TABLE %s CHANGE COLUMN %s %s %s %s %s %s %s %s;' % params
-        
-        if rows[0][4]:
-            self.execute(sql, (rows[0][4],))
-        else:
-            self.execute(sql)
-    
-    
-    def rename_table(self, old_table_name, table_name):
-        """
-        Renames the table 'old_table_name' to 'table_name'.
-        """
-        if old_table_name == table_name:
-            # No Operation
-            return
-        qn = connection.ops.quote_name
-        params = (qn(old_table_name), qn(table_name))
-        self.execute('RENAME TABLE %s TO %s;' % params)
diff --git a/apps/south/db/postgresql_psycopg2.py b/apps/south/db/postgresql_psycopg2.py
deleted file mode 100644 (file)
index 839b4b1..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-
-from django.db import connection
-from south.db import generic
-
-class DatabaseOperations(generic.DatabaseOperations):
-
-    """
-    PsycoPG2 implementation of database operations.
-    """
-
-    def rename_column(self, table_name, old, new):
-        if old == new:
-            return []
-        qn = connection.ops.quote_name
-        params = (qn(table_name), qn(old), qn(new))
-        self.execute('ALTER TABLE %s RENAME COLUMN %s TO %s;' % params)
-    
-    def rename_table(self, old_table_name, table_name):
-        "will rename the table and an associated ID sequence and primary key index"
-        # First, rename the table
-        generic.DatabaseOperations.rename_table(self, old_table_name, table_name)
-        # Then, try renaming the ID sequence
-        # (if you're using other AutoFields... your problem, unfortunately)
-        self.commit_transaction()
-        self.start_transaction()
-        try:
-            generic.DatabaseOperations.rename_table(self, old_table_name+"_id_seq", table_name+"_id_seq")
-        except:
-            if self.debug:
-                print "   ~ No such sequence (ignoring error)"
-            self.rollback_transaction()
-        else:
-            self.commit_transaction()
-        self.start_transaction()
-
-        # Rename primary key index, will not rename other indices on
-        # the table that are used by django (e.g. foreign keys). Until
-        # figure out how, you need to do this yourself.
-        try:
-            generic.DatabaseOperations.rename_table(self, old_table_name+"_pkey", table_name+ "_pkey")
-        except:
-            if self.debug:
-                print "   ~ No such primary key (ignoring error)"
-            self.rollback_transaction()
-        else:
-            self.commit_transaction()
-        self.start_transaction()
-
-
-    def rename_index(self, old_index_name, index_name):
-        "Rename an index individually"
-        generic.DatabaseOperations.rename_table(self, old_index_name, index_name)
diff --git a/apps/south/db/sql_server/__init__.py b/apps/south/db/sql_server/__init__.py
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/apps/south/db/sql_server/pyodbc.py b/apps/south/db/sql_server/pyodbc.py
deleted file mode 100644 (file)
index 58c5166..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-from django.db import connection
-from django.db.models.fields import *
-from south.db import generic
-
-class DatabaseOperations(generic.DatabaseOperations):
-    """
-    django-pyodbc (sql_server.pyodbc) implementation of database operations.
-    """
-    
-    add_column_string = 'ALTER TABLE %s ADD %s;'
-    alter_string_set_type = 'ALTER COLUMN %(column)s %(type)s'
-    allows_combined_alters = False
-    delete_column_string = 'ALTER TABLE %s DROP COLUMN %s;'
-
-    def create_table(self, table_name, fields):
-        # Tweak stuff as needed
-        for name,f in fields:
-            if isinstance(f, BooleanField):
-                if f.default == True:
-                    f.default = 1
-                if f.default == False:
-                    f.default = 0
-
-        # Run
-        generic.DatabaseOperations.create_table(self, table_name, fields)
diff --git a/apps/south/db/sqlite3.py b/apps/south/db/sqlite3.py
deleted file mode 100644 (file)
index 1fac1b8..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-
-from django.db import connection
-from south.db import generic
-
-class DatabaseOperations(generic.DatabaseOperations):
-
-    """
-    SQLite3 implementation of database operations.
-    """
-
-    # SQLite ignores foreign key constraints. I wish I could.
-    supports_foreign_keys = False
-    
-    # You can't add UNIQUE columns with an ALTER TABLE.
-    def add_column(self, table_name, name, field, *args, **kwds):
-        # Run ALTER TABLE with no unique column
-        unique, field._unique, field.db_index = field.unique, False, False
-        generic.DatabaseOperations.add_column(self, table_name, name, field, *args, **kwds)
-        # If it _was_ unique, make an index on it.
-        if unique:
-            self.create_index(table_name, [name], unique=True)
-    
-    # SQLite doesn't have ALTER COLUMN
-    def alter_column(self, table_name, name, field, explicit_name=True):
-        """
-        Not supported under SQLite.
-        """
-        raise NotImplementedError("SQLite does not support altering columns.")
-    
-    # Nor DROP COLUMN
-    def delete_column(self, table_name, name, field):
-        """
-        Not supported under SQLite.
-        """
-        raise NotImplementedError("SQLite does not support deleting columns.")
-    
-    # Nor RENAME COLUMN
-    def rename_column(self, table_name, old, new):
-        """
-        Not supported under SQLite.
-        """
-        raise NotImplementedError("SQLite does not support renaming columns.")
\ No newline at end of file
diff --git a/apps/south/docs/CHANGELOG b/apps/south/docs/CHANGELOG
deleted file mode 100644 (file)
index fa106f9..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-0.3
-===
-
-The yay-dependencies release.
-
-New features:
-
- - Dependency support for migrations
- - Fields are now used for declaring columns and tables
-
-
-0.2
-===
-
-The oh-i'm-sorry-mysql-users release.
-
-New features:
-
- - MySQL support up to the same level as PostgreSQL
- - New --all option to ./manage.py startmigration, which creates a migration
-   for every model in the given app. For project starts.
- - Project status upgraded to 'beta'. Next up, a colour-coded
-   release level system.
-
-Fixed bugs:
-
- - A few typos in various column methods
- - ManyToMany tables weren't created by startmigration migrations.
-
-Known bugs:
-
- - None
-
-
-0.1
-===
-
-Initial release.
diff --git a/apps/south/docs/CONTRIBUTING b/apps/south/docs/CONTRIBUTING
deleted file mode 100644 (file)
index 56dd525..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-
-Contributing to South
----------------------
-
-Contributions to South are very welcome.
-
-
-You can find more info on our site at http://south.aeracode.org/wiki/Contributing
diff --git a/apps/south/docs/LICENSE b/apps/south/docs/LICENSE
deleted file mode 100644 (file)
index 1914f85..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-Apache License
-Version 2.0, January 2004
-http://www.apache.org/licenses/
-
-TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
-1. Definitions.
-
-"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
-
-"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
-
-"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
-
-"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
-
-"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
-
-"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
-
-"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
-
-"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
-
-"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
-
-"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
-
-2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
-
-3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
-
-4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
-
-   1. You must give any other recipients of the Work or Derivative Works a copy of this License; and
-
-   2. You must cause any modified files to carry prominent notices stating that You changed the files; and
-
-   3. You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
-
-   4. If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.
-
-You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
-
-5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
-
-6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
-
-7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
-
-8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
-
-9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
-
-END OF TERMS AND CONDITIONS
diff --git a/apps/south/docs/README b/apps/south/docs/README
deleted file mode 100644 (file)
index 99d3f20..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-This is South, a Django application to provide migrations in a sane way.
-
-By sane, we mean that the status of every migration is tracked individually,
-rather than just the number of the top migration reached; this means South
-can detect when you have an unapplied migration that's sitting in the middle
-of a whole load of applied ones, and will let you apply it straight off,
-or let you roll back to it, and apply from there forward.
-
-Documentation on South is currently available on our project site;
-you can find it at http://south.aeracode.org/wiki/Documentation
diff --git a/apps/south/management/__init__.py b/apps/south/management/__init__.py
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/apps/south/management/commands/__init__.py b/apps/south/management/commands/__init__.py
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/apps/south/management/commands/migrate.py b/apps/south/management/commands/migrate.py
deleted file mode 100644 (file)
index 1cc2a29..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-from django.core.management.base import BaseCommand
-from django.core.management.color import no_style
-from django.conf import settings
-from django.db import models
-from optparse import make_option
-from south import migration
-import sys
-
-class Command(BaseCommand):
-    option_list = BaseCommand.option_list + (
-        make_option('--skip', action='store_true', dest='skip', default=False,
-            help='Will skip over out-of-order missing migrations'),
-        make_option('--merge', action='store_true', dest='merge', default=False,
-            help='Will run out-of-order missing migrations as they are - no rollbacks.'),
-        make_option('--only', action='store_true', dest='only', default=False,
-            help='Only runs or rolls back the migration specified, and none around it.'),
-        make_option('--fake', action='store_true', dest='fake', default=False,
-            help="Pretends to do the migrations, but doesn't actually execute them."),
-
-        make_option('--db-dry-run', action='store_true', dest='db_dry_run', default=False,
-            help="Doesn't execute the SQL generated by the db methods, and doesn't store a record that the migration(s) occurred. Useful to test migrations before applying them."),
-    )
-    if '--verbosity' not in [opt.get_opt_string() for opt in BaseCommand.option_list]:
-        option_list += (
-            make_option('--verbosity', action='store', dest='verbosity', default='1',
-            type='choice', choices=['0', '1', '2'],
-            help='Verbosity level; 0=minimal output, 1=normal output, 2=all output'),
-        )
-    help = "Runs migrations for all apps."
-
-    def handle(self, app=None, target=None, skip=False, merge=False, only=False, backwards=False, fake=False, db_dry_run=False, **options):
-
-        # Work out what the resolve mode is
-        resolve_mode = merge and "merge" or (skip and "skip" or None)
-        # Turn on db debugging
-        from south.db import db
-        db.debug = True
-        
-        # NOTE: THIS IS DUPLICATED FROM django.core.management.commands.syncdb
-        # This code imports any module named 'management' in INSTALLED_APPS.
-        # The 'management' module is the preferred way of listening to post_syncdb
-        # signals, and since we're sending those out with create_table migrations,
-        # we need apps to behave correctly.
-        for app_name in settings.INSTALLED_APPS:
-            try:
-                __import__(app_name + '.management', {}, {}, [''])
-            except ImportError, exc:
-                msg = exc.args[0]
-                if not msg.startswith('No module named') or 'management' not in msg:
-                    raise
-        # END DJANGO DUPE CODE
-        
-        # Migrate each app
-        if app:
-            apps = [migration.get_app(app)]
-        else:
-            apps = migration.get_migrated_apps()
-        silent = options.get('verbosity', 0) == 0
-        for app in apps:
-            migration.migrate_app(
-                app,
-                resolve_mode = resolve_mode,
-                target_name = target,
-                fake = fake,
-                db_dry_run = db_dry_run,
-                silent = silent,
-                load_inital_data = True,
-            )
diff --git a/apps/south/management/commands/startmigration.py b/apps/south/management/commands/startmigration.py
deleted file mode 100644 (file)
index 1a8da99..0000000
+++ /dev/null
@@ -1,491 +0,0 @@
-from django.core.management.base import BaseCommand
-from django.core.management.color import no_style
-from django.db import models
-from django.db.models.fields.related import RECURSIVE_RELATIONSHIP_CONSTANT
-from django.contrib.contenttypes.generic import GenericRelation
-from django.db.models.fields import FieldDoesNotExist
-from optparse import make_option
-from south import migration
-import sys
-import os
-import re
-import string
-import random
-import inspect
-import parser
-
-class Command(BaseCommand):
-    option_list = BaseCommand.option_list + (
-        make_option('--model', action='append', dest='model_list', type='string',
-            help='Generate a Create Table migration for the specified model.  Add multiple models to this migration with subsequent --model parameters.'),
-        make_option('--add-field', action='append', dest='field_list', type='string',
-            help='Generate an Add Column migration for the specified modelname.fieldname - you can use this multiple times to add more than one column.'),
-        make_option('--initial', action='store_true', dest='initial', default=False,
-            help='Generate the initial schema for the app.'),
-    )
-    help = "Creates a new template migration for the given app"
-    
-    def handle(self, app=None, name="", model_list=None, field_list=None, initial=False, **options):
-        
-        # If model_list is None, then it's an empty list
-        model_list = model_list or []
-        
-        # If field_list is None, then it's an empty list
-        field_list = field_list or []
-        
-        # make sure --model and --all aren't both specified
-        if initial and (model_list or field_list):
-            print "You cannot use --initial and other options together"
-            return
-            
-        # specify the default name 'initial' if a name wasn't specified and we're
-        # doing a migration for an entire app
-        if not name and initial:
-            name = 'initial'
-            
-        # if not name, there's an error
-        if not name:
-            print "You must name this migration"
-            return
-        
-        if not app:
-            print "Please provide an app in which to create the migration."
-            return
-            
-        # See if the app exists
-        app_models_module = models.get_app(app)
-        if not app_models_module:
-            print "App '%s' doesn't seem to exist, isn't in INSTALLED_APPS, or has no models." % app
-            return
-            
-        # Determine what models should be included in this migration.
-        models_to_migrate = []
-        if initial:
-            models_to_migrate = models.get_models(app_models_module)
-            if not models_to_migrate:
-                print "No models found in app '%s'" % (app)
-                return
-        else:
-            for model_name in model_list:
-                model = models.get_model(app, model_name)
-                if not model:
-                    print "Couldn't find model '%s' in app '%s'" % (model_name, app)
-                    return
-                    
-                models_to_migrate.append(model)
-        
-        # See what fields need to be included
-        fields_to_add = []
-        for field_spec in field_list:
-            model_name, field_name = field_spec.split(".", 1)
-            model = models.get_model(app, model_name)
-            if not model:
-                print "Couldn't find model '%s' in app '%s'" % (model_name, app)
-                return
-            try:
-                field = model._meta.get_field(field_name)
-            except FieldDoesNotExist:
-                print "Model '%s' doesn't have a field '%s'" % (model_name, field_name)
-                return
-            fields_to_add.append((model, field_name, field))
-        
-        # Make the migrations directory if it's not there
-        app_module_path = app_models_module.__name__.split('.')[0:-1]
-        try:
-            app_module = __import__('.'.join(app_module_path), {}, {}, [''])
-        except ImportError:
-            print "Couldn't find path to App '%s'." % app
-            return
-            
-        migrations_dir = os.path.join(
-            os.path.dirname(app_module.__file__),
-            "migrations",
-        )
-        # Make sure there's a migrations directory and __init__.py
-        if not os.path.isdir(migrations_dir):
-            print "Creating migrations directory at '%s'..." % migrations_dir
-            os.mkdir(migrations_dir)
-        init_path = os.path.join(migrations_dir, "__init__.py")
-        if not os.path.isfile(init_path):
-            # Touch the init py file
-            print "Creating __init__.py in '%s'..." % migrations_dir
-            open(init_path, "w").close()
-        # See what filename is next in line. We assume they use numbers.
-        migrations = migration.get_migration_names(migration.get_app(app))
-        highest_number = 0
-        for migration_name in migrations:
-            try:
-                number = int(migration_name.split("_")[0])
-                highest_number = max(highest_number, number)
-            except ValueError:
-                pass
-        # Make the new filename
-        new_filename = "%04i%s_%s.py" % (
-            highest_number + 1,
-            "".join([random.choice(string.letters.lower()) for i in range(0)]), # Possible random stuff insertion
-            name,
-        )
-        # If there's a model, make the migration skeleton, else leave it bare
-        forwards, backwards = '', ''
-        if fields_to_add:
-            # First, do the added fields
-            for model, field_name, field in fields_to_add:
-                field_definition = generate_field_definition(model, field)
-                
-                if isinstance(field, models.ManyToManyField):
-                    # Make a mock model for each side
-                    mock_model = "\n".join([
-                        create_mock_model(model, "        "), 
-                        create_mock_model(field.rel.to, "        ")
-                    ])
-                    # And a field defn, that's actually a table creation
-                    forwards += '''
-        # Mock Model
-%s
-        # Adding ManyToManyField '%s.%s'
-        db.create_table('%s', (
-            ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
-            ('%s', models.ForeignKey(%s, null=False)),
-            ('%s', models.ForeignKey(%s, null=False))
-        )) ''' % (
-                mock_model,
-                model._meta.object_name,
-                field.name,
-                field.m2m_db_table(),
-                field.m2m_column_name()[:-3], # strip off the '_id' at the end
-                model._meta.object_name,
-                field.m2m_reverse_name()[:-3], # strip off the '_id' at the ned
-                field.rel.to._meta.object_name
-                )
-                    backwards += '''
-        # Dropping ManyToManyField '%s.%s'
-        db.drop_table('%s')''' % (
-                        model._meta.object_name,
-                        field.name,
-                        field.m2m_db_table()
-                    )
-                    continue
-                elif field.rel: # ForeignKey, etc.
-                    mock_model = create_mock_model(field.rel.to, "        ")
-                    field_definition = related_field_definition(field, field_definition)
-                else:
-                    mock_model = None
-                
-                # If we can't get it (inspect madness?) then insert placeholder
-                if not field_definition:
-                    print "Warning: Could not generate field definition for %s.%s, manual editing of migration required." % \
-                                (model._meta.object_name, field.name)
-                    field_definition = '<<< REPLACE THIS WITH FIELD DEFINITION FOR %s.%s >>>' % (model._meta.object_name, f.name)
-                
-                if mock_model:
-                    forwards += '''
-        # Mock model
-%s
-        ''' % (mock_model)
-                
-                forwards += '''
-        # Adding field '%s.%s'
-        db.add_column(%r, %r, %s)
-        ''' % (
-            model._meta.object_name,
-            field.name,
-            model._meta.db_table,
-            field.name,
-            field_definition,
-        )
-                backwards += '''
-        # Deleting field '%s.%s'
-        db.delete_column(%r, %r)
-        ''' % (
-            model._meta.object_name,
-            field.name,
-            model._meta.db_table,
-            field.column,
-        )
-        
-        if models_to_migrate:
-            # Now, do the added models
-            for model in models_to_migrate:
-                table_name = model._meta.db_table
-                mock_models = []
-                fields = []
-                for f in model._meta.local_fields:
-                    
-                    # Look up the field definition to see how this was created
-                    field_definition = generate_field_definition(model, f)
-                    
-                    # If it's a OneToOneField, and ends in _ptr, just use it
-                    if isinstance(f, models.OneToOneField) and f.name.endswith("_ptr"):
-                        mock_models.append(create_mock_model(f.rel.to, "        "))
-                        field_definition = "models.OneToOneField(%s)" % f.rel.to.__name__
-                    
-                    # It's probably normal then
-                    elif field_definition:
-                        
-                        if isinstance(f, models.ForeignKey):
-                            mock_models.append(create_mock_model(f.rel.to, "        "))
-                            field_definition = related_field_definition(f, field_definition)
-                    
-                    # Oh noes, no defn found
-                    else:
-                        print "Warning: Could not generate field definition for %s.%s, manual editing of migration required." % \
-                                (model._meta.object_name, f.name)
-                        print f, type(f)
-                                
-                        field_definition = '<<< REPLACE THIS WITH FIELD DEFINITION FOR %s.%s >>>' % (model._meta.object_name, f.name)
-                                                
-                    fields.append((f.name, field_definition))
-                    
-                if mock_models:
-                    forwards += '''
-        
-        # Mock Models
-%s
-        ''' % "\n".join(mock_models)
-        
-                forwards += '''
-        # Model '%s'
-        db.create_table(%r, (
-            %s
-        ))''' % (
-                    model._meta.object_name,
-                    table_name,
-                    "\n            ".join(["('%s', %s)," % (f[0], f[1]) for f in fields]),
-                )
-
-                backwards = ('''db.delete_table('%s')
-        ''' % table_name) + backwards
-        
-                # Now go through local M2Ms and add extra stuff for them
-                for m in model._meta.local_many_to_many:
-                    # ignore generic relations
-                    if isinstance(m, GenericRelation):
-                        continue
-
-                    # if the 'through' option is specified, the table will
-                    # be created through the normal model creation above.
-                    if m.rel.through:
-                        continue
-                        
-                    mock_models = [create_mock_model(model, "        "), create_mock_model(m.rel.to, "        ")]
-                    
-                    forwards += '''
-        # Mock Models
-%s
-        
-        # M2M field '%s.%s'
-        db.create_table('%s', (
-            ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
-            ('%s', models.ForeignKey(%s, null=False)),
-            ('%s', models.ForeignKey(%s, null=False))
-        )) ''' % (
-                        "\n".join(mock_models),
-                        model._meta.object_name,
-                        m.name,
-                        m.m2m_db_table(),
-                        m.m2m_column_name()[:-3], # strip off the '_id' at the end
-                        model._meta.object_name,
-                        m.m2m_reverse_name()[:-3], # strip off the '_id' at the ned
-                        m.rel.to._meta.object_name
-                )
-                
-                    backwards = '''db.delete_table('%s')
-        ''' % m.m2m_db_table() + backwards
-                
-                if model._meta.unique_together:
-                    ut = model._meta.unique_together
-                    if not isinstance(ut[0], (list, tuple)):
-                        ut = (ut,)
-                        
-                    for unique in ut:
-                        columns = ["'%s'" % model._meta.get_field(f).column for f in unique]
-                        
-                        forwards += '''
-        db.create_index('%s', [%s], unique=True, db_tablespace='%s')
-        ''' %   (
-                        table_name,
-                        ','.join(columns),
-                        model._meta.db_tablespace
-                )
-                
-                
-            forwards += '''
-        
-        db.send_create_signal('%s', ['%s'])''' % (
-                app, 
-                "','".join(model._meta.object_name for model in models_to_migrate)
-                )
-        
-        # Try sniffing the encoding using PEP 0263's method
-        encoding = None
-        first_two_lines = inspect.getsourcelines(app_models_module)[0][:2]
-        for line in first_two_lines:
-            if re.search("coding[:=]\s*([-\w.]+)", line):
-                encoding = line
-        
-        if (not forwards) and (not backwards):
-            forwards = '"Write your forwards migration here"'
-            backwards = '"Write your backwards migration here"'
-        fp = open(os.path.join(migrations_dir, new_filename), "w")
-        fp.write("""%s
-from south.db import db
-from django.db import models
-from %s.models import *
-
-class Migration:
-    
-    def forwards(self):
-        %s
-    
-    def backwards(self):
-        %s
-""" % (encoding or "", '.'.join(app_module_path), forwards, backwards))
-        fp.close()
-        print "Created %s." % new_filename
-
-
-def generate_field_definition(model, field):
-    """
-    Inspects the source code of 'model' to find the code used to generate 'field'
-    """
-    def test_field(field_definition):
-        try:
-            parser.suite(field_definition)
-            return True
-        except SyntaxError:
-            return False
-            
-    def strip_comments(field_definition):
-        # remove any comments at the end of the field definition string.
-        field_definition = field_definition.strip()
-        if '#' not in field_definition:
-            return field_definition
-            
-        index = field_definition.index('#')
-        while index:
-            stripped_definition = field_definition[:index].strip()
-            # if the stripped definition is parsable, then we've removed
-            # the correct comment.
-            if test_field(stripped_definition):
-                return stripped_definition
-            
-            try:    
-                index = field_definition.index('#', index+1)
-            except ValueError:
-                break
-            
-        return field_definition
-        
-    # give field subclasses a chance to do anything tricky
-    # with the field definition
-    if hasattr(field, 'south_field_definition'):
-        return field.south_field_definition()
-    
-    field_pieces = []
-    found_field = False
-    source = inspect.getsourcelines(model)
-    if not source:
-        raise Exception("Could not find source to model: '%s'" % (model.__name__))
-    
-    # look for a line starting with the field name
-    start_field_re = re.compile(r'\s*%s\s*=\s*(.*)' % field.name)
-    for line in source[0]:
-        # if the field was found during a previous iteration, 
-        # we're here because the field spans across multiple lines
-        # append the current line and try again
-        if found_field:
-            field_pieces.append(line.strip())
-            if test_field(' '.join(field_pieces)):
-                return strip_comments(' '.join(field_pieces))
-            continue
-        
-        match = start_field_re.match(line)
-        if match:
-            found_field = True
-            field_pieces.append(match.groups()[0].strip())
-            if test_field(' '.join(field_pieces)):
-                return strip_comments(' '.join(field_pieces))
-    
-    # the 'id' field never gets defined, so return what django does by default
-    # django.db.models.options::_prepare
-    if field.name == 'id' and field.__class__ == models.AutoField:
-        return "models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)"
-    
-    # search this classes parents
-    for base in model.__bases__:
-        # we don't want to scan the django base model
-        if base == models.Model:
-            continue
-            
-        field_definition = generate_field_definition(base, field)
-        if field_definition:
-            return field_definition
-            
-    return None
-    
-def replace_model_string(field_definition, search_string, model_name):
-    # wrap 'search_string' in both ' and " chars when searching
-    quotes = ["'", '"']
-    for quote in quotes:
-        test = "%s%s%s" % (quote, search_string, quote)
-        if test in field_definition:
-            return field_definition.replace(test, model_name)
-            
-    return None
-        
-def related_field_definition(field, field_definition):
-    # if the field definition contains any of the following strings,
-    # replace them with the model definition:
-    #   applabel.modelname
-    #   modelname
-    #   django.db.models.fields.related.RECURSIVE_RELATIONSHIP_CONSTANT
-    strings = [
-        '%s.%s' % (field.rel.to._meta.app_label, field.rel.to._meta.object_name),
-        '%s' % field.rel.to._meta.object_name,
-        RECURSIVE_RELATIONSHIP_CONSTANT
-    ]
-    
-    for test in strings:
-        fd = replace_model_string(field_definition, test, field.rel.to._meta.object_name)
-        if fd:
-            return fd
-    
-    return field_definition
-
-def create_mock_model(model, indent="        "):
-    # produce a string representing the python syntax necessary for creating
-    # a mock model using the supplied real model
-    if not model._meta.pk.__class__.__module__.startswith('django.db.models.fields'):
-        # we can fix this with some clever imports, but it doesn't seem necessary to
-        # spend time on just yet
-        print "Can't generate a mock model for %s because it's primary key isn't a default django field; it's type %s." % (model, model._meta.pk.__class__)
-        sys.exit()
-    
-    pk_field_args = []
-    pk_field_kwargs = {}
-    other_mocks = []
-    # If it's a OneToOneField or ForeignKey, take it's first arg
-    if model._meta.pk.__class__.__name__ in ["OneToOneField", "ForeignKey"]:
-        if model._meta.pk.rel.to == model:
-            pk_field_args += ["'self'"]
-        else:
-            pk_field_args += [model._meta.pk.rel.to._meta.object_name]
-            other_mocks += [model._meta.pk.rel.to]
-    
-    # Perhaps it has a max_length set?
-    if model._meta.pk.max_length:
-        pk_field_kwargs["max_length"] = model._meta.pk.max_length
-    
-    return "%s%s%s = db.mock_model(model_name='%s', db_table='%s', db_tablespace='%s', pk_field_name='%s', pk_field_type=models.%s, pk_field_args=[%s], pk_field_kwargs=%r)" % \
-        (
-        "\n".join([create_mock_model(m, indent) for m in other_mocks]+[""]),
-        indent,
-        model._meta.object_name,
-        model._meta.object_name,
-        model._meta.db_table,
-        model._meta.db_tablespace,
-        model._meta.pk.name,
-        model._meta.pk.__class__.__name__,
-        ", ".join(pk_field_args),
-        pk_field_kwargs,
-        )
diff --git a/apps/south/management/commands/syncdb.py b/apps/south/management/commands/syncdb.py
deleted file mode 100644 (file)
index 7b160c2..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-from django.core.management.base import NoArgsCommand, BaseCommand 
-from django.core.management.color import no_style
-from django.utils.datastructures import SortedDict
-from optparse import make_option
-from south import migration
-from django.core.management.commands import syncdb
-from django.conf import settings
-from django.db import models
-from django.db.models.loading import cache
-from django.core import management
-import sys
-
-def get_app_name(app):
-    return '.'.join( app.__name__.split('.')[0:-1] )
-
-class Command(NoArgsCommand):
-    option_list = NoArgsCommand.option_list + (
-        make_option('--noinput', action='store_false', dest='interactive', default=True,
-            help='Tells Django to NOT prompt the user for input of any kind.'),
-        make_option('--migrate', action='store_true', dest='migrate', default=False,
-            help='Tells South to also perform migrations after the sync. Default for during testing, and other internal calls.'),
-    )
-    if '--verbosity' not in [opt.get_opt_string() for opt in BaseCommand.option_list]:
-        option_list += (
-            make_option('--verbosity', action='store', dest='verbosity', default='1',
-            type='choice', choices=['0', '1', '2'],
-            help='Verbosity level; 0=minimal output, 1=normal output, 2=all output'),
-        )
-    help = "Create the database tables for all apps in INSTALLED_APPS whose tables haven't already been created, except those which use migrations."
-
-    def handle_noargs(self, **options):
-        # Work out what uses migrations and so doesn't need syncing
-        apps_needing_sync = []
-        apps_migrated = []
-        for app in models.get_apps():
-            app_name = get_app_name(app)
-            migrations = migration.get_app(app)
-            if migrations is None:
-                apps_needing_sync.append(app_name)
-            else:
-                # This is a migrated app, leave it
-                apps_migrated.append(app_name)
-        verbosity = int(options.get('verbosity', 0))
-        # Run syncdb on only the ones needed
-        if verbosity > 0:
-            print "Syncing..."
-        old_installed, settings.INSTALLED_APPS = settings.INSTALLED_APPS, apps_needing_sync
-        old_app_store, cache.app_store = cache.app_store, SortedDict([
-            (k, v) for (k, v) in cache.app_store.items()
-            if get_app_name(k) in apps_needing_sync
-        ])
-        syncdb.Command().execute(**options)
-        settings.INSTALLED_APPS = old_installed
-        cache.app_store = old_app_store
-        # Migrate if needed
-        if options.get('migrate', True):
-            if verbosity > 0:
-                print "Migrating..."
-            management.call_command('migrate', **options)
-        # Be obvious about what we did
-        if verbosity > 0:
-            print "\nSynced:\n > %s" % "\n > ".join(apps_needing_sync)
-        
-        if options.get('migrate', True):
-            if verbosity > 0:
-                print "\nMigrated:\n - %s" % "\n - ".join(apps_migrated)
-        else:
-            if verbosity > 0:
-                print "\nNot synced (use migrations):\n - %s" % "\n - ".join(apps_migrated)
-                print "(use ./manage.py migrate to migrate these)"
diff --git a/apps/south/management/commands/test.py b/apps/south/management/commands/test.py
deleted file mode 100644 (file)
index eef8f31..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-from django.core import management
-from django.core.management.commands import test
-from django.core.management.commands import syncdb
-
-class Command(test.Command):
-    
-    def handle(self, *args, **kwargs):
-        # point at the core syncdb command when creating tests
-        # tests should always be up to date with the most recent model structure
-        management.get_commands()
-        management._commands['syncdb'] = 'django.core'
-        super(Command, self).handle(*args, **kwargs)
\ No newline at end of file
diff --git a/apps/south/migration.py b/apps/south/migration.py
deleted file mode 100644 (file)
index 6452442..0000000
+++ /dev/null
@@ -1,528 +0,0 @@
-
-import datetime
-import os
-import sys
-import traceback
-from django.conf import settings
-from django.db import models
-from django.core.exceptions import ImproperlyConfigured
-from django.core.management import call_command
-from models import MigrationHistory
-from south.db import db
-
-
-def get_app(app):
-    """
-    Returns the migrations module for the given app model name/module, or None
-    if it does not use migrations.
-    """
-    if isinstance(app, (str, unicode)):
-        # If it's a string, use the models module
-        app = models.get_app(app)
-    mod = __import__(app.__name__[:-7], {}, {}, ['migrations'])
-    if hasattr(mod, 'migrations'):
-        return getattr(mod, 'migrations')
-
-
-def get_migrated_apps():
-    """
-    Returns all apps with migrations.
-    """
-    for mapp in models.get_apps():
-        app = get_app(mapp)
-        if app:
-            yield app
-
-
-def get_app_name(app):
-    """
-    Returns the _internal_ app name for the given app module.
-    i.e. for <module django.contrib.auth.models> will return 'auth'
-    """
-    return app.__name__.split('.')[-2]
-
-
-def get_app_fullname(app):
-    """
-    Returns the full python name of an app - e.g. django.contrib.auth
-    """
-    return app.__name__[:-11]
-
-
-def short_from_long(app_name):
-    return app_name.split(".")[-1]
-
-
-def get_migration_names(app):
-    """
-    Returns a list of migration file names for the given app.
-    """
-    return sorted([
-        filename[:-3]
-        for filename in os.listdir(os.path.dirname(app.__file__))
-        if filename.endswith(".py") and filename != "__init__.py" and not filename.startswith(".")
-    ])
-
-
-def get_migration_classes(app):
-    """
-    Returns a list of migration classes (one for each migration) for the app.
-    """
-    for name in get_migration_names(app):
-        yield get_migration(app, name)
-
-
-def get_migration(app, name):
-    """
-    Returns the migration class implied by 'name'.
-    """
-    try:
-        module = __import__(app.__name__ + "." + name, '', '', ['Migration'])
-        return module.Migration
-    except ImportError:
-        print " ! Migration %s:%s probably doesn't exist." % (get_app_name(app), name)
-        print " - Traceback:"
-        raise
-
-
-def all_migrations():
-    return dict([
-        (app, dict([(name, get_migration(app, name)) for name in get_migration_names(app)]))
-        for app in get_migrated_apps()
-    ])
-
-
-def dependency_tree():
-    tree = all_migrations()
-    
-    # Annotate tree with 'backwards edges'
-    for app, classes in tree.items():
-        for name, cls in classes.items():
-            cls.needs = []
-            if not hasattr(cls, "needed_by"):
-                cls.needed_by = []
-            if hasattr(cls, "depends_on"):
-                for dapp, dname in cls.depends_on:
-                    dapp = get_app(dapp)
-                    if dapp not in tree:
-                        print "Migration %s in app %s depends on unmigrated app %s." % (
-                            name,
-                            get_app_name(app),
-                            dapp,
-                        )
-                        sys.exit(1)
-                    if dname not in tree[dapp]:
-                        print "Migration %s in app %s depends on nonexistent migration %s in app %s." % (
-                            name,
-                            get_app_name(app),
-                            dname,
-                            get_app_name(dapp),
-                        )
-                        sys.exit(1)
-                    cls.needs.append((dapp, dname))
-                    if not hasattr(tree[dapp][dname], "needed_by"):
-                        tree[dapp][dname].needed_by = []
-                    tree[dapp][dname].needed_by.append((app, name))
-    
-    # Sanity check whole tree
-    for app, classes in tree.items():
-        for name, cls in classes.items():
-            cls.dependencies = dependencies(tree, app, name)
-    
-    return tree
-
-
-def nice_trace(trace):
-    return " -> ".join([str((get_app_name(a), n)) for a, n in trace])
-
-
-def dependencies(tree, app, name, trace=[]):
-    # Copy trace to stop pass-by-ref problems
-    trace = trace[:]
-    # Sanity check
-    for papp, pname in trace:
-        if app == papp:
-            if pname == name:
-                print "Found circular dependency: %s" % nice_trace(trace + [(app,name)])
-                sys.exit(1)
-            else:
-                # See if they depend in the same app the wrong way
-                migrations = get_migration_names(app)
-                if migrations.index(name) > migrations.index(pname):
-                    print "Found a lower migration (%s) depending on a higher migration (%s) in the same app (%s)." % (pname, name, get_app_name(app))
-                    print "Path: %s" % nice_trace(trace + [(app,name)])
-                    sys.exit(1)
-    # Get the dependencies of a migration
-    deps = []
-    migration = tree[app][name]
-    for dapp, dname in migration.needs:
-        deps.extend(
-            dependencies(tree, dapp, dname, trace+[(app,name)])
-        )
-    return deps
-
-
-def remove_duplicates(l):
-    m = []
-    for x in l:
-        if x not in m:
-            m.append(x)
-    return m
-
-
-def needed_before_forwards(tree, app, name, sameapp=True):
-    """
-    Returns a list of migrations that must be applied before (app, name),
-    in the order they should be applied.
-    Used to make sure a migration can be applied (and to help apply up to it).
-    """
-    app_migrations = get_migration_names(app)
-    needed = []
-    if sameapp:
-        for aname in app_migrations[:app_migrations.index(name)]:
-            needed += needed_before_forwards(tree, app, aname, False)
-            needed += [(app, aname)]
-    for dapp, dname in tree[app][name].needs:
-        needed += needed_before_forwards(tree, dapp, dname)
-        needed += [(dapp, dname)]
-    return remove_duplicates(needed)
-
-
-def needed_before_backwards(tree, app, name, sameapp=True):
-    """
-    Returns a list of migrations that must be unapplied before (app, name) is,
-    in the order they should be unapplied.
-    Used to make sure a migration can be unapplied (and to help unapply up to it).
-    """
-    app_migrations = get_migration_names(app)
-    needed = []
-    if sameapp:
-        for aname in reversed(app_migrations[app_migrations.index(name)+1:]):
-            needed += needed_before_backwards(tree, app, aname, False)
-            needed += [(app, aname)]
-    for dapp, dname in tree[app][name].needed_by:
-        needed += needed_before_backwards(tree, dapp, dname)
-        needed += [(dapp, dname)]
-    return remove_duplicates(needed)
-
-
-def run_migrations(toprint, torun, recorder, app, migrations, fake=False, db_dry_run=False, silent=False):
-    """
-    Runs the specified migrations forwards, in order.
-    """
-    for migration in migrations:
-        app_name = get_app_name(app)
-        if not silent:
-            print toprint % (app_name, migration)
-        klass = get_migration(app, migration)
-
-        if fake:
-            if not silent:
-                print "   (faked)"
-        else:
-            
-            # If the database doesn't support running DDL inside a transaction
-            # *cough*MySQL*cough* then do a dry run first.
-            if not db.has_ddl_transactions:
-                db.dry_run = True
-                db.debug, old_debug = False, db.debug
-                try:
-                    getattr(klass(), torun)()
-                except:
-                    traceback.print_exc()
-                    print " ! Error found during dry run of migration! Aborting."
-                    return False
-                db.debug = old_debug
-                db.clear_run_data()
-            
-            db.dry_run = bool(db_dry_run)
-            
-            if db.has_ddl_transactions:
-                db.start_transaction()
-            try:
-                getattr(klass(), torun)()
-                db.execute_deferred_sql()
-            except:
-                if db.has_ddl_transactions:
-                    db.rollback_transaction()
-                    raise
-                else:
-                    traceback.print_exc()
-                    print " ! Error found during real run of migration! Aborting."
-                    print
-                    print " ! Since you have a database that does not support running"
-                    print " ! schema-altering statements in transactions, we have had to"
-                    print " ! leave it in an interim state between migrations."
-                    if torun == "forwards":
-                        print
-                        print " ! You *might* be able to recover with:"
-                        db.debug = db.dry_run = True
-                        klass().backwards()
-                    print
-                    print " ! The South developers regret this has happened, and would"
-                    print " ! like to gently persuade you to consider a slightly"
-                    print " ! easier-to-deal-with DBMS."
-                    return False
-            else:
-                if db.has_ddl_transactions:
-                    db.commit_transaction()
-
-        if not db_dry_run:
-            # Record us as having done this
-            recorder(app_name, migration)
-
-
-def run_forwards(app, migrations, fake=False, db_dry_run=False, silent=False):
-    """
-    Runs the specified migrations forwards, in order.
-    """
-    
-    def record(app_name, migration):
-        # Record us as having done this
-        record = MigrationHistory.for_migration(app_name, migration)
-        record.applied = datetime.datetime.utcnow()
-        record.save()
-    
-    return run_migrations(
-        toprint = " > %s: %s",
-        torun = "forwards",
-        recorder = record,
-        app = app,
-        migrations = migrations,
-        fake = fake,
-        db_dry_run = db_dry_run,
-        silent = silent,
-    )
-
-
-def run_backwards(app, migrations, ignore=[], fake=False, db_dry_run=False, silent=False):
-    """
-    Runs the specified migrations backwards, in order, skipping those
-    migrations in 'ignore'.
-    """
-    
-    def record(app_name, migration):
-        # Record us as having not done this
-        record = MigrationHistory.for_migration(app_name, migration)
-        record.delete()
-    
-    return run_migrations(
-        toprint = " < %s: %s",
-        torun = "backwards",
-        recorder = record,
-        app = app,
-        migrations = [x for x in migrations if x not in ignore],
-        fake = fake,
-        db_dry_run = db_dry_run,
-        silent = silent,
-    )
-
-
-def right_side_of(x, y):
-    return left_side_of(reversed(x), reversed(y))
-
-
-def left_side_of(x, y):
-    return list(y)[:len(x)] == list(x)
-
-
-def forwards_problems(tree, forwards, done, silent=False):
-    problems = []
-    for app, name in forwards:
-        if (app, name) not in done:
-            for dapp, dname in needed_before_backwards(tree, app, name):
-                if (dapp, dname) in done:
-                    if not silent:
-                        print " ! Migration (%s, %s) should not have been applied before (%s, %s) but was." % (get_app_name(dapp), dname, get_app_name(app), name)
-                    problems.append(((app, name), (dapp, dname)))
-    return problems
-
-
-
-def backwards_problems(tree, backwards, done, silent=False):
-    problems = []
-    for app, name in backwards:
-        if (app, name) in done:
-            for dapp, dname in needed_before_forwards(tree, app, name):
-                if (dapp, dname) not in done:
-                    if not silent:
-                        print " ! Migration (%s, %s) should have been applied before (%s, %s) but wasn't." % (get_app_name(dapp), dname, get_app_name(app), name)
-                    problems.append(((app, name), (dapp, dname)))
-    return problems
-
-
-def migrate_app(app, target_name=None, resolve_mode=None, fake=False, db_dry_run=False, yes=False, silent=False, load_inital_data=False):
-    
-    app_name = get_app_name(app)
-    
-    db.debug = not silent
-    
-    # If any of their app names in the DB contain a ., they're 0.2 or below, so migrate em
-    longuns = MigrationHistory.objects.filter(app_name__contains=".")
-    if longuns:
-        for mh in longuns:
-            mh.app_name = short_from_long(mh.app_name)
-            mh.save()
-        if not silent:
-            print "- Updated your South 0.2 database."
-    
-    # Find out what delightful migrations we have
-    tree = dependency_tree()
-    migrations = get_migration_names(app)
-    
-    # If there aren't any, quit quizically
-    if not migrations:
-        if not silent:
-            print "? You have no migrations for the '%s' app. You might want some." % app_name
-        return
-    
-    if target_name not in migrations and target_name not in ["zero", None]:
-        matches = [x for x in migrations if x.startswith(target_name)]
-        if len(matches) == 1:
-            target = migrations.index(matches[0]) + 1
-            if not silent:
-                print " - Soft matched migration %s to %s." % (
-                    target_name,
-                    matches[0]
-                )
-            target_name = matches[0]
-        elif len(matches) > 1:
-            if not silent:
-                print " - Prefix %s matches more than one migration:" % target_name
-                print "     " + "\n     ".join(matches)
-            return
-        else:
-            if not silent:
-                print " ! '%s' is not a migration." % target_name
-            return
-    
-    # Check there's no strange ones in the database
-    ghost_migrations = []
-    for m in MigrationHistory.objects.filter(applied__isnull = False):
-        try:
-            if get_app(m.app_name) not in tree or m.migration not in tree[get_app(m.app_name)]:
-                ghost_migrations.append(m)
-        except ImproperlyConfigured:
-            pass
-            
-        
-    if ghost_migrations:
-        if not silent:
-            print " ! These migrations are in the database but not on disk:"
-            print "   - " + "\n   - ".join(["%s: %s" % (x.app_name, x.migration) for x in ghost_migrations])
-            print " ! I'm not trusting myself; fix this yourself by fiddling"
-            print " ! with the south_migrationhistory table."
-        return
-    
-    # Say what we're doing
-    if not silent:
-        print "Running migrations for %s:" % app_name
-    
-    # Get the forwards and reverse dependencies for this target
-    if target_name == None:
-        target_name = migrations[-1]
-    if target_name == "zero":
-        forwards = []
-        backwards = needed_before_backwards(tree, app, migrations[0]) + [(app, migrations[0])]
-    else:
-        forwards = needed_before_forwards(tree, app, target_name) + [(app, target_name)]
-        # When migrating backwards we want to remove up to and including
-        # the next migration up in this app (not the next one, that includes other apps)
-        try:
-            migration_before_here = migrations[migrations.index(target_name)+1]
-            backwards = needed_before_backwards(tree, app, migration_before_here) + [(app, migration_before_here)]
-        except IndexError:
-            backwards = []
-    
-    # Get the list of currently applied migrations from the db
-    current_migrations = []
-    for m in MigrationHistory.objects.filter(applied__isnull = False):
-        try:
-            current_migrations.append((get_app(m.app_name), m.migration))
-        except ImproperlyConfigured:
-            pass
-    
-    direction = None
-    bad = False
-    
-    # Work out the direction
-    applied_for_this_app = list(MigrationHistory.objects.filter(app_name=app_name, applied__isnull=False).order_by("migration"))
-    if target_name == "zero":
-        direction = -1
-    elif not applied_for_this_app:
-        direction = 1
-    elif migrations.index(target_name) > migrations.index(applied_for_this_app[-1].migration):
-        direction = 1
-    elif migrations.index(target_name) < migrations.index(applied_for_this_app[-1].migration):
-        direction = -1
-    else:
-        direction = None
-    
-    # Is the whole forward branch applied?
-    missing = [step for step in forwards if step not in current_migrations]
-    # If they're all applied, we only know it's not backwards
-    if not missing:
-        direction = None
-    # If the remaining migrations are strictly a right segment of the forwards
-    # trace, we just need to go forwards to our target (and check for badness)
-    else:
-        problems = forwards_problems(tree, forwards, current_migrations, silent=silent)
-        if problems:
-            bad = True
-        direction = 1
-    
-    # What about the whole backward trace then?
-    if not bad:
-        missing = [step for step in backwards if step not in current_migrations]
-        # If they're all missing, stick with the forwards decision
-        if missing == backwards:
-            pass
-        # If what's missing is a strict left segment of backwards (i.e.
-        # all the higher migrations) then we need to go backwards
-        else:
-            problems = backwards_problems(tree, backwards, current_migrations, silent=silent)
-            if problems:
-                bad = True
-            direction = -1
-    
-    if bad and resolve_mode not in ['merge']:
-        if not silent:
-            print " ! Inconsistent migration history"
-            print " ! The following options are available:"
-            print "    --merge: will just attempt the migration ignoring any potential dependency conflicts."
-        sys.exit(1)
-    
-    if direction == 1:
-        if not silent:
-            print " - Migrating forwards to %s." % target_name
-        try:
-            for mapp, mname in forwards:
-                if (mapp, mname) not in current_migrations:
-                    result = run_forwards(mapp, [mname], fake=fake, db_dry_run=db_dry_run, silent=silent)
-                    if result is False: # The migrations errored, but nicely.
-                        return
-        finally:
-            # Call any pending post_syncdb signals
-            db.send_pending_create_signals()
-        # Now load initial data, only if we're really doing things and ended up at current
-        if not fake and not db_dry_run and load_inital_data and target_name == migrations[-1]:
-            print " - Loading initial data for %s." % app_name
-            # Override Django's get_apps call temporarily to only load from the
-            # current app
-            old_get_apps, models.get_apps = (
-                models.get_apps,
-                lambda: [models.get_app(get_app_name(app))],
-            )
-            # Load the initial fixture
-            call_command('loaddata', 'initial_data', verbosity=1)
-            # Un-override
-            models.get_apps = old_get_apps
-    elif direction == -1:
-        if not silent:
-            print " - Migrating backwards to just after %s." % target_name
-        for mapp, mname in backwards:
-            if (mapp, mname) in current_migrations:
-                run_backwards(mapp, [mname], fake=fake, db_dry_run=db_dry_run, silent=silent)
-    else:
-        if not silent:
-            print "- Nothing to migrate."
diff --git a/apps/south/models.py b/apps/south/models.py
deleted file mode 100644 (file)
index e95c79a..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-from django.db import models
-
-class MigrationHistory(models.Model):
-    app_name = models.CharField(max_length=255)
-    migration = models.CharField(max_length=255)
-    applied = models.DateTimeField(blank=True, null=True)
-
-    @classmethod
-    def for_migration(cls, app_name, migration):
-        try:
-            return cls.objects.get(
-                app_name = app_name,
-                migration = migration,
-            )
-        except cls.DoesNotExist:
-            return cls(
-                app_name = app_name,
-                migration = migration,
-            )
\ No newline at end of file
diff --git a/apps/south/setup.py b/apps/south/setup.py
deleted file mode 100755 (executable)
index 9e09583..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/usr/bin/python
-
-from setuptools import setup, find_packages
-
-setup(
-    name='South',
-    version='0.4',
-    description='South: Migrations for Django',
-    long_description='South is an intelligent database migrations library for the Django web framework. It is database-independent and DVCS-friendly, as well as a whole host of other features.',
-    author='Andrew Godwin & Andy McCurdy',
-    author_email='south@aeracode.org',
-    url='http://south.aeracode.org/',
-    download_url='http://south.aeracode.org/wiki/Download',
-    classifiers=[
-        "Development Status :: 5 - Production/Stable",
-        "Framework :: Django",
-        "Intended Audience :: Developers",
-        "Intended Audience :: System Administrators",
-        "Intended Audience :: System Administrators",
-        "License :: OSI Approved :: Apache Software License",
-        "Operating System :: OS Independent",
-        "Topic :: Software Development"
-    ],
-    packages=["south", "south.db", "south.management", "south.management.commands", "south.tests", "south.tests.fakeapp", "south.tests.fakeapp.migrations"],
-    package_dir = {"south" : ""},
-)
diff --git a/apps/south/tests/__init__.py b/apps/south/tests/__init__.py
deleted file mode 100644 (file)
index d8953fe..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-
-from django.conf import settings
-
-try:
-    skiptest = settings.SKIP_SOUTH_TESTS
-except:
-    skiptest = False
-
-if not skiptest:
-    from south.tests.db import *
-    from south.tests.logic import *
\ No newline at end of file
diff --git a/apps/south/tests/db.py b/apps/south/tests/db.py
deleted file mode 100644 (file)
index b7bb145..0000000
+++ /dev/null
@@ -1,185 +0,0 @@
-import unittest
-
-from south.db import db
-from django.db import connection, models
-
-# Create a list of error classes from the various database libraries
-errors = []
-try:
-    from psycopg2 import ProgrammingError
-    errors.append(ProgrammingError)
-except ImportError:
-    pass
-errors = tuple(errors)
-
-class TestOperations(unittest.TestCase):
-
-    """
-    Tests if the various DB abstraction calls work.
-    Can only test a limited amount due to DB differences.
-    """
-
-    def setUp(self):
-        db.debug = False
-        db.clear_deferred_sql()
-
-    def test_create(self):
-        """
-        Test creation and deletion of tables.
-        """
-        cursor = connection.cursor()
-        # It needs to take at least 2 args
-        self.assertRaises(TypeError, db.create_table)
-        self.assertRaises(TypeError, db.create_table, "test1")
-        # Empty tables (i.e. no columns) are not fine, so make at least 1
-        db.create_table("test1", [('email_confirmed', models.BooleanField(default=False))])
-        db.start_transaction()
-        # And should exist
-        cursor.execute("SELECT * FROM test1")
-        # Make sure we can't do the same query on an empty table
-        try:
-            cursor.execute("SELECT * FROM nottheretest1")
-            self.fail("Non-existent table could be selected!")
-        except:
-            pass
-        # Clear the dirty transaction
-        db.rollback_transaction()
-        db.start_transaction()
-        # Remove the table
-        db.drop_table("test1")
-        # Make sure it went
-        try:
-            cursor.execute("SELECT * FROM test1")
-            self.fail("Just-deleted table could be selected!")
-        except:
-            pass
-        # Clear the dirty transaction
-        db.rollback_transaction()
-        db.start_transaction()
-        # Try deleting a nonexistent one
-        try:
-            db.delete_table("nottheretest1")
-            self.fail("Non-existent table could be deleted!")
-        except:
-            pass
-        db.rollback_transaction()
-    
-    def test_foreign_keys(self):
-        """
-        Tests foreign key creation, especially uppercase (see #61)
-        """
-        Test = db.mock_model(model_name='Test', db_table='test5a',
-                             db_tablespace='', pk_field_name='ID',
-                             pk_field_type=models.AutoField, pk_field_args=[])
-        cursor = connection.cursor()
-        db.start_transaction()
-        db.create_table("test5a", [('ID', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True))])
-        db.create_table("test5b", [
-            ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
-            ('UNIQUE', models.ForeignKey(Test)),
-        ])
-        db.execute_deferred_sql()
-        db.rollback_transaction()
-    
-    def test_rename(self):
-        """
-        Test column renaming
-        """
-        cursor = connection.cursor()
-        db.create_table("test2", [('spam', models.BooleanField(default=False))])
-        db.start_transaction()
-        # Make sure we can select the column
-        cursor.execute("SELECT spam FROM test2")
-        # Rename it
-        db.rename_column("test2", "spam", "eggs")
-        cursor.execute("SELECT eggs FROM test2")
-        try:
-            cursor.execute("SELECT spam FROM test2")
-            self.fail("Just-renamed column could be selected!")
-        except:
-            pass
-        db.rollback_transaction()
-        db.delete_table("test2")
-    
-    def test_dry_rename(self):
-        """
-        Test column renaming while --dry-run is turned on (should do nothing)
-        See ticket #65
-        """
-        cursor = connection.cursor()
-        db.create_table("test2", [('spam', models.BooleanField(default=False))])
-        db.start_transaction()
-        # Make sure we can select the column
-        cursor.execute("SELECT spam FROM test2")
-        # Rename it
-        db.dry_run = True
-        db.rename_column("test2", "spam", "eggs")
-        db.dry_run = False
-        cursor.execute("SELECT spam FROM test2")
-        try:
-            cursor.execute("SELECT eggs FROM test2")
-            self.fail("Dry-renamed new column could be selected!")
-        except:
-            pass
-        db.rollback_transaction()
-        db.delete_table("test2")
-    
-    def test_table_rename(self):
-        """
-        Test column renaming
-        """
-        cursor = connection.cursor()
-        db.create_table("testtr", [('spam', models.BooleanField(default=False))])
-        db.start_transaction()
-        # Make sure we can select the column
-        cursor.execute("SELECT spam FROM testtr")
-        # Rename it
-        db.rename_table("testtr", "testtr2")
-        cursor.execute("SELECT spam FROM testtr2")
-        try:
-            cursor.execute("SELECT spam FROM testtr")
-            self.fail("Just-renamed column could be selected!")
-        except:
-            pass
-        db.rollback_transaction()
-        db.delete_table("testtr2")
-    
-    def test_index(self):
-        """
-        Test the index operations
-        """
-        db.create_table("test3", [
-            ('SELECT', models.BooleanField(default=False)),
-            ('eggs', models.IntegerField(unique=True)),
-        ])
-        db.execute_deferred_sql()
-        db.start_transaction()
-        # Add an index on that column
-        db.create_index("test3", ["SELECT"])
-        # Add another index on two columns
-        db.create_index("test3", ["SELECT", "eggs"])
-        # Delete them both
-        db.delete_index("test3", ["SELECT"])
-        db.delete_index("test3", ["SELECT", "eggs"])
-        # Delete the unique index
-        db.delete_index("test3", ["eggs"])
-        db.rollback_transaction()
-        db.delete_table("test3")
-    
-    def test_alter(self):
-        """
-        Test altering columns/tables
-        """
-        db.create_table("test4", [
-            ('spam', models.BooleanField(default=False)),
-            ('eggs', models.IntegerField()),
-        ])
-        db.start_transaction()
-        # Add a column
-        db.add_column("test4", "add1", models.IntegerField(default=3), keep_default=False)
-        # Add a FK with keep_default=False (#69)
-        User = db.mock_model(model_name='User', db_table='auth_user', db_tablespace='', pk_field_name='id', pk_field_type=models.AutoField, pk_field_args=[], pk_field_kwargs={})
-        db.add_column("test4", "user", models.ForeignKey(User), keep_default=False)
-        
-        db.rollback_transaction()
-        db.delete_table("test4")
\ No newline at end of file
diff --git a/apps/south/tests/fakeapp/__init__.py b/apps/south/tests/fakeapp/__init__.py
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/apps/south/tests/fakeapp/migrations/0001_spam.py b/apps/south/tests/fakeapp/migrations/0001_spam.py
deleted file mode 100644 (file)
index d814548..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-from south.db import db
-from django.db import models
-
-class Migration:
-    
-    def forwards(self):
-        
-        # Model 'Spam'
-        db.create_table("southtest_spam", (
-            ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
-            ('weight', models.FloatField()),
-            ('expires', models.DateTimeField()),
-            ('name', models.CharField(max_length=255))
-        ))
-    
-    def backwards(self):
-        
-        db.delete_table("southtest_spam")
-
diff --git a/apps/south/tests/fakeapp/migrations/0002_eggs.py b/apps/south/tests/fakeapp/migrations/0002_eggs.py
deleted file mode 100644 (file)
index 3ec8399..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-from south.db import db
-from django.db import models
-
-class Migration:
-    
-    def forwards(self):
-        
-        Spam = db.mock_model(model_name='Spam', db_table='southtest_spam', db_tablespace='', pk_field_name='id', pk_field_type=models.AutoField)
-        
-        db.create_table("southtest_eggs", (
-            ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
-            ('size', models.FloatField()),
-            ('quantity', models.IntegerField()),
-            ('spam', models.ForeignKey(Spam)),
-        ))
-    
-    def backwards(self):
-        
-        db.delete_table("southtest_eggs")
-
diff --git a/apps/south/tests/fakeapp/migrations/0003_alter_spam.py b/apps/south/tests/fakeapp/migrations/0003_alter_spam.py
deleted file mode 100644 (file)
index 3a9aea4..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-from south.db import db
-from django.db import models
-
-class Migration:
-    
-    def forwards(self):
-        
-        db.alter_column("southtest_spam", 'name', models.CharField(max_length=255, null=True))
-    
-    def backwards(self):
-        
-        db.alter_column("southtest_spam", 'name', models.CharField(max_length=255))
diff --git a/apps/south/tests/fakeapp/migrations/__init__.py b/apps/south/tests/fakeapp/migrations/__init__.py
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/apps/south/tests/fakeapp/models.py b/apps/south/tests/fakeapp/models.py
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/apps/south/tests/logic.py b/apps/south/tests/logic.py
deleted file mode 100644 (file)
index 862c52d..0000000
+++ /dev/null
@@ -1,292 +0,0 @@
-import unittest
-import datetime
-import sys
-import os
-
-from south import migration
-
-# Add the tests directory so fakeapp is on sys.path
-test_root = os.path.dirname(__file__)
-sys.path.append(test_root)
-
-
-class TestMigrationLogic(unittest.TestCase):
-
-    """
-    Tests if the various logic functions in migration actually work.
-    """
-
-    def create_fake_app(self, name):
-        
-        class Fake:
-            pass
-        
-        fake = Fake()
-        fake.__name__ = name
-        return fake
-
-
-    def create_test_app(self):
-        
-        class Fake:
-            pass
-        
-        fake = Fake()
-        fake.__name__ = "fakeapp.migrations"
-        fake.__file__ = os.path.join(test_root, "fakeapp", "migrations", "__init__.py")
-        return fake
-    
-    
-    def monkeypatch(self):
-        """Swaps out various Django calls for fake ones for our own nefarious purposes."""
-        
-        def new_get_apps():
-            return ['fakeapp']
-        
-        from django.db import models
-        from django.conf import settings
-        models.get_apps_old, models.get_apps = models.get_apps, new_get_apps
-        settings.INSTALLED_APPS, settings.OLD_INSTALLED_APPS = (
-            ["fakeapp"],
-            settings.INSTALLED_APPS,
-        )
-        self.redo_app_cache()
-    setUp = monkeypatch
-    
-    
-    def unmonkeypatch(self):
-        """Undoes what monkeypatch did."""
-        
-        from django.db import models
-        from django.conf import settings
-        models.get_apps = models.get_apps_old
-        settings.INSTALLED_APPS = settings.OLD_INSTALLED_APPS
-        self.redo_app_cache()
-    tearDown = unmonkeypatch
-    
-    
-    def redo_app_cache(self):
-        from django.db.models.loading import AppCache
-        a = AppCache()
-        a.loaded = False
-        a._populate()
-    
-
-    def test_get_app_name(self):
-        self.assertEqual(
-            "southtest",
-            migration.get_app_name(self.create_fake_app("southtest.migrations")),
-        )
-        self.assertEqual(
-            "baz",
-            migration.get_app_name(self.create_fake_app("foo.bar.baz.migrations")),
-        )
-    
-    
-    def test_get_migrated_apps(self):
-        
-        P1 = __import__("fakeapp.migrations", {}, {}, [''])
-        
-        self.assertEqual(
-            [P1],
-            list(migration.get_migrated_apps()),
-        )
-    
-    
-    def test_get_app(self):
-        
-        P1 = __import__("fakeapp.migrations", {}, {}, [''])
-        
-        self.assertEqual(P1, migration.get_app("fakeapp"))
-        self.assertEqual(P1, migration.get_app(self.create_fake_app("fakeapp.models")))
-    
-    
-    def test_get_app_fullname(self):
-        self.assertEqual(
-            "southtest",
-            migration.get_app_fullname(self.create_fake_app("southtest.migrations")),
-        )
-        self.assertEqual(
-            "foo.bar.baz",
-            migration.get_app_fullname(self.create_fake_app("foo.bar.baz.migrations")),
-        )
-    
-    
-    def test_get_migration_names(self):
-        
-        app = self.create_test_app()
-        
-        self.assertEqual(
-            ["0001_spam", "0002_eggs", "0003_alter_spam"],
-            migration.get_migration_names(app),
-        )
-    
-    
-    def test_get_migration_classes(self):
-        
-        app = self.create_test_app()
-        
-        # Can't use vanilla import, modules beginning with numbers aren't in grammar
-        M1 = __import__("fakeapp.migrations.0001_spam", {}, {}, ['Migration']).Migration
-        M2 = __import__("fakeapp.migrations.0002_eggs", {}, {}, ['Migration']).Migration
-        M3 = __import__("fakeapp.migrations.0003_alter_spam", {}, {}, ['Migration']).Migration
-        
-        self.assertEqual(
-            [M1, M2, M3],
-            list(migration.get_migration_classes(app)),
-        )
-    
-    
-    def test_get_migration(self):
-        
-        app = self.create_test_app()
-        
-        # Can't use vanilla import, modules beginning with numbers aren't in grammar
-        M1 = __import__("fakeapp.migrations.0001_spam", {}, {}, ['Migration']).Migration
-        M2 = __import__("fakeapp.migrations.0002_eggs", {}, {}, ['Migration']).Migration
-        
-        self.assertEqual(M1, migration.get_migration(app, "0001_spam"))
-        self.assertEqual(M2, migration.get_migration(app, "0002_eggs"))
-        
-        self.assertRaises((ImportError, ValueError), migration.get_migration, app, "0001_jam")
-    
-    
-    def test_all_migrations(self):
-        
-        app = migration.get_app("fakeapp")
-        
-        self.assertEqual(
-            {app: {
-                "0001_spam": migration.get_migration(app, "0001_spam"),
-                "0002_eggs": migration.get_migration(app, "0002_eggs"),
-                "0003_alter_spam": migration.get_migration(app, "0003_alter_spam"),
-            }},
-            migration.all_migrations(),
-        )
-    
-    
-    def assertListEqual(self, list1, list2):
-        list1 = list(list1)
-        list2 = list(list2)
-        list1.sort()
-        list2.sort()
-        return self.assertEqual(list1, list2)
-    
-    
-    def test_apply_migrations(self):
-        
-        app = migration.get_app("fakeapp")
-        
-        # We should start with no migrations
-        self.assertEqual(list(migration.MigrationHistory.objects.all()), [])
-        
-        # Apply them normally
-        migration.migrate_app(app, target_name=None, resolve_mode=None, fake=False, silent=True)
-        
-        # We should finish with all migrations
-        self.assertListEqual(
-            (
-                (u"fakeapp", u"0001_spam"),
-                (u"fakeapp", u"0002_eggs"),
-                (u"fakeapp", u"0003_alter_spam"),
-            ),
-            migration.MigrationHistory.objects.values_list("app_name", "migration"),
-        )
-        
-        # Now roll them backwards
-        migration.migrate_app(app, target_name="zero", resolve_mode=None, fake=False, silent=True)
-        
-        # Finish with none
-        self.assertEqual(list(migration.MigrationHistory.objects.all()), [])
-    
-    
-    def test_migration_merge_forwards(self):
-        
-        app = migration.get_app("fakeapp")
-        
-        # We should start with no migrations
-        self.assertEqual(list(migration.MigrationHistory.objects.all()), [])
-        
-        # Insert one in the wrong order
-        migration.MigrationHistory.objects.create(
-            app_name = "fakeapp",
-            migration = "0002_eggs",
-            applied = datetime.datetime.now(),
-        )
-        
-        # Did it go in?
-        self.assertListEqual(
-            (
-                (u"fakeapp", u"0002_eggs"),
-            ),
-            migration.MigrationHistory.objects.values_list("app_name", "migration"),
-        )
-        
-        # Apply them normally
-        try:
-            migration.migrate_app(app, target_name=None, resolve_mode=None, fake=False, silent=True)
-        except SystemExit:
-            pass
-        
-        # Nothing should have changed (no merge mode!)
-        self.assertListEqual(
-            (
-                (u"fakeapp", u"0002_eggs"),
-            ),
-            migration.MigrationHistory.objects.values_list("app_name", "migration"),
-        )
-        
-        # Apply with merge
-        migration.migrate_app(app, target_name=None, resolve_mode="merge", fake=False, silent=True)
-        
-        # We should finish with all migrations
-        self.assertListEqual(
-            (
-                (u"fakeapp", u"0001_spam"),
-                (u"fakeapp", u"0002_eggs"),
-                (u"fakeapp", u"0003_alter_spam"),
-            ),
-            migration.MigrationHistory.objects.values_list("app_name", "migration"),
-        )
-        
-        # Now roll them backwards
-        migration.migrate_app(app, target_name="0002", resolve_mode=None, fake=False, silent=True)
-        migration.migrate_app(app, target_name="0001", resolve_mode=None, fake=True, silent=True)
-        migration.migrate_app(app, target_name="zero", resolve_mode=None, fake=False, silent=True)
-        
-        # Finish with none
-        self.assertEqual(list(migration.MigrationHistory.objects.all()), [])
-    
-    def test_alter_column_null(self):
-        def null_ok():
-            from django.db import connection, transaction
-            # the DBAPI introspection module fails on postgres NULLs.
-            cursor = connection.cursor()
-            try:
-                cursor.execute("INSERT INTO southtest_spam (id, weight, expires, name) VALUES (100, 10.1, now(), NULL);")
-            except:
-                transaction.rollback()
-                return False
-            else:
-                cursor.execute("DELETE FROM southtest_spam")
-                transaction.commit()
-                return True
-        
-        app = migration.get_app("fakeapp")
-        self.assertEqual(list(migration.MigrationHistory.objects.all()), [])
-        
-        # by default name is NOT NULL
-        migration.migrate_app(app, target_name="0002", resolve_mode=None, fake=False, silent=True)
-        self.failIf(null_ok())
-        
-        # after 0003, it should be NULL
-        migration.migrate_app(app, target_name="0003", resolve_mode=None, fake=False, silent=True)
-        self.assert_(null_ok())
-
-        # make sure it is NOT NULL again
-        migration.migrate_app(app, target_name="0002", resolve_mode=None, fake=False, silent=True)
-        self.failIf(null_ok(), 'name not null after migration')
-        
-        # finish with no migrations, otherwise other tests fail...
-        migration.migrate_app(app, target_name="zero", resolve_mode=None, fake=False, silent=True)
-        self.assertEqual(list(migration.MigrationHistory.objects.all()), [])
\ No newline at end of file
index f39d8b4..f6a851d 100644 (file)
@@ -1,3 +1,6 @@
-# -*- encoding: utf-8 -*-
+# -*- coding: utf-8 -*-
+# This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
 __author__ = u'Marek Stępniowski, <marek@stepniowski.com>'
 __version__ = '0.1'
index 55af9d7..f8e2801 100644 (file)
@@ -1,3 +1,7 @@
+# -*- coding: utf-8 -*-
+# This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
 from django.contrib import admin
 from django.conf import settings
 
index 678788e..26effc5 100644 (file)
@@ -1,4 +1,7 @@
-# -*- encoding: utf-8 -*-
+# -*- coding: utf-8 -*-
+# This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
 import datetime
 
 from django.conf import settings
diff --git a/apps/sponsors/locale/en/LC_MESSAGES/django.po b/apps/sponsors/locale/en/LC_MESSAGES/django.po
new file mode 100644 (file)
index 0000000..23cbca1
--- /dev/null
@@ -0,0 +1,37 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2010-05-18 11:04+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: models.py:14 models.py:37
+msgid "name"
+msgstr ""
+
+#: models.py:15
+msgid "description"
+msgstr ""
+
+#: models.py:17
+msgid "logo"
+msgstr ""
+
+#: models.py:24
+msgid "url"
+msgstr ""
+
+#: models.py:38
+msgid "sponsors"
+msgstr ""
diff --git a/apps/sponsors/locale/es/LC_MESSAGES/django.po b/apps/sponsors/locale/es/LC_MESSAGES/django.po
new file mode 100644 (file)
index 0000000..23cbca1
--- /dev/null
@@ -0,0 +1,37 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2010-05-18 11:04+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: models.py:14 models.py:37
+msgid "name"
+msgstr ""
+
+#: models.py:15
+msgid "description"
+msgstr ""
+
+#: models.py:17
+msgid "logo"
+msgstr ""
+
+#: models.py:24
+msgid "url"
+msgstr ""
+
+#: models.py:38
+msgid "sponsors"
+msgstr ""
diff --git a/apps/sponsors/locale/fr/LC_MESSAGES/django.po b/apps/sponsors/locale/fr/LC_MESSAGES/django.po
new file mode 100644 (file)
index 0000000..23cbca1
--- /dev/null
@@ -0,0 +1,37 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2010-05-18 11:04+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: models.py:14 models.py:37
+msgid "name"
+msgstr ""
+
+#: models.py:15
+msgid "description"
+msgstr ""
+
+#: models.py:17
+msgid "logo"
+msgstr ""
+
+#: models.py:24
+msgid "url"
+msgstr ""
+
+#: models.py:38
+msgid "sponsors"
+msgstr ""
diff --git a/apps/sponsors/locale/lt/LC_MESSAGES/django.po b/apps/sponsors/locale/lt/LC_MESSAGES/django.po
new file mode 100644 (file)
index 0000000..23cbca1
--- /dev/null
@@ -0,0 +1,37 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2010-05-18 11:04+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: models.py:14 models.py:37
+msgid "name"
+msgstr ""
+
+#: models.py:15
+msgid "description"
+msgstr ""
+
+#: models.py:17
+msgid "logo"
+msgstr ""
+
+#: models.py:24
+msgid "url"
+msgstr ""
+
+#: models.py:38
+msgid "sponsors"
+msgstr ""
diff --git a/apps/sponsors/locale/pl/LC_MESSAGES/django.po b/apps/sponsors/locale/pl/LC_MESSAGES/django.po
new file mode 100644 (file)
index 0000000..23cbca1
--- /dev/null
@@ -0,0 +1,37 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2010-05-18 11:04+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: models.py:14 models.py:37
+msgid "name"
+msgstr ""
+
+#: models.py:15
+msgid "description"
+msgstr ""
+
+#: models.py:17
+msgid "logo"
+msgstr ""
+
+#: models.py:24
+msgid "url"
+msgstr ""
+
+#: models.py:38
+msgid "sponsors"
+msgstr ""
diff --git a/apps/sponsors/locale/ru/LC_MESSAGES/django.po b/apps/sponsors/locale/ru/LC_MESSAGES/django.po
new file mode 100644 (file)
index 0000000..23cbca1
--- /dev/null
@@ -0,0 +1,37 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2010-05-18 11:04+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: models.py:14 models.py:37
+msgid "name"
+msgstr ""
+
+#: models.py:15
+msgid "description"
+msgstr ""
+
+#: models.py:17
+msgid "logo"
+msgstr ""
+
+#: models.py:24
+msgid "url"
+msgstr ""
+
+#: models.py:38
+msgid "sponsors"
+msgstr ""
index 03b0d65..60e4c49 100644 (file)
@@ -1,3 +1,7 @@
+# -*- coding: utf-8 -*-
+# This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
 from django.db import models
 from django.utils.translation import ugettext_lazy as _
 from django.template.loader import render_to_string
index 8c3d74c..112241d 100644 (file)
@@ -1,3 +1,7 @@
+# -*- coding: utf-8 -*-
+# This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
 from PIL import Image, ImageFilter, ImageChops
 
 
index c1d18d1..fb8e6b3 100644 (file)
@@ -1,3 +1,7 @@
+# -*- coding: utf-8 -*-
+# This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
 from django import template
 from django.utils.safestring import mark_safe
 
index ed06ba6..70ddde6 100644 (file)
@@ -1,3 +1,7 @@
+# -*- coding: utf-8 -*-
+# This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
 from django.conf import settings
 from django import forms
 from django.utils.safestring import mark_safe
diff --git a/deployment.py b/deployment.py
new file mode 100644 (file)
index 0000000..ab19880
--- /dev/null
@@ -0,0 +1,67 @@
+#!/srv/library/wolnelektury/pythonenv/bin/python
+from __future__ import with_statement
+
+import shutil
+import os
+import sys
+
+from string import Template
+
+def render_template(source, dest, context={}):
+    print "Rendering template:",
+    with open(source, 'rb') as source_file:
+        t = Template(source_file.read())
+    with open(dest, 'wb') as dest_file:
+        dest_file.write(t.safe_substitute(context))
+    print "done."
+
+def restart_wsgi():
+    print "Restarting wsgi application:",
+    os.system("touch %s" % WSGI_TARGET)
+    print "done."
+
+def update_application():
+    print "Updating repository.",
+    os.system("cd %s; git pull" % PROJECT_ROOT)
+
+    print "Installing requirements"
+    os.system("%s install -r %s" % (PIP, os.path.join(PROJECT_ROOT, 'requirements.txt')))
+
+    print "Installing local requirements"
+    os.system("%s install -r %s" % (PIP, os.path.join(ROOT, 'etc', 'requirements.txt')))
+    print "done."
+
+ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+
+PYTHON = os.path.join(ROOT, 'pythonenv', 'bin', 'python')
+PIP = os.path.join(ROOT, 'pythonenv', 'bin', 'pip')
+PYTHON_SITE = os.path.join(ROOT, 'pythonenv', 'lib', 'python2.6', 'site-packages')
+
+PROJECT_NAME = 'wolnelektury'
+PROJECT_ROOT = os.path.join(ROOT, 'application')
+
+MEDIA_ROOT = os.path.join(ROOT, 'www', 'media')
+
+ADMIN_EMAIL = 'lrekucki@gmail.com'
+
+WSGI_TARGET = os.path.join(ROOT, 'www', 'wsgi', PROJECT_NAME + '.wsgi')
+WSGI_DIR = os.path.dirname(WSGI_TARGET)
+
+WSGI_USER = PROJECT_NAME
+WSGI_PROCESSES = 5
+WSGI_THREADS = 1
+
+DOMAIN = 'lektury.staging.nowoczesnapolska.org.pl'
+
+#
+# Load local configuration
+#
+sys.path = [ os.path.join(ROOT, 'etc') ] + sys.path
+
+from local_deployment import *
+
+if __name__ == '__main__':
+    update_application()
+    render_template(os.path.join(PROJECT_ROOT, PROJECT_NAME + '.wsgi.tmpl'), WSGI_TARGET, context=globals())
+    render_template(os.path.join(PROJECT_ROOT, PROJECT_NAME + '.vhost.tmpl'), os.path.join(ROOT, 'etc', PROJECT_NAME + '.vhost'), context=globals())
+    restart_wsgi()
diff --git a/lib/feedparser.py b/lib/feedparser.py
deleted file mode 100644 (file)
index bb802df..0000000
+++ /dev/null
@@ -1,2858 +0,0 @@
-#!/usr/bin/env python
-"""Universal feed parser
-
-Handles RSS 0.9x, RSS 1.0, RSS 2.0, CDF, Atom 0.3, and Atom 1.0 feeds
-
-Visit http://feedparser.org/ for the latest version
-Visit http://feedparser.org/docs/ for the latest documentation
-
-Required: Python 2.1 or later
-Recommended: Python 2.3 or later
-Recommended: CJKCodecs and iconv_codec <http://cjkpython.i18n.org/>
-"""
-
-__version__ = "4.1"# + "$Revision: 1.92 $"[11:15] + "-cvs"
-__license__ = """Copyright (c) 2002-2006, Mark Pilgrim, All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification,
-are permitted provided that the following conditions are met:
-
-* Redistributions of source code must retain the above copyright notice,
-  this list of conditions and the following disclaimer.
-* Redistributions in binary form must reproduce the above copyright notice,
-  this list of conditions and the following disclaimer in the documentation
-  and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS'
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
-LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE."""
-__author__ = "Mark Pilgrim <http://diveintomark.org/>"
-__contributors__ = ["Jason Diamond <http://injektilo.org/>",
-                    "John Beimler <http://john.beimler.org/>",
-                    "Fazal Majid <http://www.majid.info/mylos/weblog/>",
-                    "Aaron Swartz <http://aaronsw.com/>",
-                    "Kevin Marks <http://epeus.blogspot.com/>"]
-_debug = 0
-
-# HTTP "User-Agent" header to send to servers when downloading feeds.
-# If you are embedding feedparser in a larger application, you should
-# change this to your application name and URL.
-USER_AGENT = "UniversalFeedParser/%s +http://feedparser.org/" % __version__
-
-# HTTP "Accept" header to send to servers when downloading feeds.  If you don't
-# want to send an Accept header, set this to None.
-ACCEPT_HEADER = "application/atom+xml,application/rdf+xml,application/rss+xml,application/x-netcdf,application/xml;q=0.9,text/xml;q=0.2,*/*;q=0.1"
-
-# List of preferred XML parsers, by SAX driver name.  These will be tried first,
-# but if they're not installed, Python will keep searching through its own list
-# of pre-installed parsers until it finds one that supports everything we need.
-PREFERRED_XML_PARSERS = ["drv_libxml2"]
-
-# If you want feedparser to automatically run HTML markup through HTML Tidy, set
-# this to 1.  Requires mxTidy <http://www.egenix.com/files/python/mxTidy.html>
-# or utidylib <http://utidylib.berlios.de/>.
-TIDY_MARKUP = 0
-
-# List of Python interfaces for HTML Tidy, in order of preference.  Only useful
-# if TIDY_MARKUP = 1
-PREFERRED_TIDY_INTERFACES = ["uTidy", "mxTidy"]
-
-# ---------- required modules (should come with any Python distribution) ----------
-import sgmllib, re, sys, copy, urlparse, time, rfc822, types, cgi, urllib, urllib2
-try:
-    from cStringIO import StringIO as _StringIO
-except:
-    from StringIO import StringIO as _StringIO
-
-# ---------- optional modules (feedparser will work without these, but with reduced functionality) ----------
-
-# gzip is included with most Python distributions, but may not be available if you compiled your own
-try:
-    import gzip
-except:
-    gzip = None
-try:
-    import zlib
-except:
-    zlib = None
-
-# If a real XML parser is available, feedparser will attempt to use it.  feedparser has
-# been tested with the built-in SAX parser, PyXML, and libxml2.  On platforms where the
-# Python distribution does not come with an XML parser (such as Mac OS X 10.2 and some
-# versions of FreeBSD), feedparser will quietly fall back on regex-based parsing.
-try:
-    import xml.sax
-    xml.sax.make_parser(PREFERRED_XML_PARSERS) # test for valid parsers
-    from xml.sax.saxutils import escape as _xmlescape
-    _XML_AVAILABLE = 1
-except:
-    _XML_AVAILABLE = 0
-    def _xmlescape(data):
-        data = data.replace('&', '&amp;')
-        data = data.replace('>', '&gt;')
-        data = data.replace('<', '&lt;')
-        return data
-
-# base64 support for Atom feeds that contain embedded binary data
-try:
-    import base64, binascii
-except:
-    base64 = binascii = None
-
-# cjkcodecs and iconv_codec provide support for more character encodings.
-# Both are available from http://cjkpython.i18n.org/
-try:
-    import cjkcodecs.aliases
-except:
-    pass
-try:
-    import iconv_codec
-except:
-    pass
-
-# chardet library auto-detects character encodings
-# Download from http://chardet.feedparser.org/
-try:
-    import chardet
-    if _debug:
-        import chardet.constants
-        chardet.constants._debug = 1
-except:
-    chardet = None
-
-# ---------- don't touch these ----------
-class ThingsNobodyCaresAboutButMe(Exception): pass
-class CharacterEncodingOverride(ThingsNobodyCaresAboutButMe): pass
-class CharacterEncodingUnknown(ThingsNobodyCaresAboutButMe): pass
-class NonXMLContentType(ThingsNobodyCaresAboutButMe): pass
-class UndeclaredNamespace(Exception): pass
-
-sgmllib.tagfind = re.compile('[a-zA-Z][-_.:a-zA-Z0-9]*')
-sgmllib.special = re.compile('<!')
-sgmllib.charref = re.compile('&#(x?[0-9A-Fa-f]+)[^0-9A-Fa-f]')
-
-SUPPORTED_VERSIONS = {'': 'unknown',
-                      'rss090': 'RSS 0.90',
-                      'rss091n': 'RSS 0.91 (Netscape)',
-                      'rss091u': 'RSS 0.91 (Userland)',
-                      'rss092': 'RSS 0.92',
-                      'rss093': 'RSS 0.93',
-                      'rss094': 'RSS 0.94',
-                      'rss20': 'RSS 2.0',
-                      'rss10': 'RSS 1.0',
-                      'rss': 'RSS (unknown version)',
-                      'atom01': 'Atom 0.1',
-                      'atom02': 'Atom 0.2',
-                      'atom03': 'Atom 0.3',
-                      'atom10': 'Atom 1.0',
-                      'atom': 'Atom (unknown version)',
-                      'cdf': 'CDF',
-                      'hotrss': 'Hot RSS'
-                      }
-
-try:
-    UserDict = dict
-except NameError:
-    # Python 2.1 does not have dict
-    from UserDict import UserDict
-    def dict(aList):
-        rc = {}
-        for k, v in aList:
-            rc[k] = v
-        return rc
-
-class FeedParserDict(UserDict):
-    keymap = {'channel': 'feed',
-              'items': 'entries',
-              'guid': 'id',
-              'date': 'updated',
-              'date_parsed': 'updated_parsed',
-              'description': ['subtitle', 'summary'],
-              'url': ['href'],
-              'modified': 'updated',
-              'modified_parsed': 'updated_parsed',
-              'issued': 'published',
-              'issued_parsed': 'published_parsed',
-              'copyright': 'rights',
-              'copyright_detail': 'rights_detail',
-              'tagline': 'subtitle',
-              'tagline_detail': 'subtitle_detail'}
-    def __getitem__(self, key):
-        if key == 'category':
-            return UserDict.__getitem__(self, 'tags')[0]['term']
-        if key == 'categories':
-            return [(tag['scheme'], tag['term']) for tag in UserDict.__getitem__(self, 'tags')]
-        realkey = self.keymap.get(key, key)
-        if type(realkey) == types.ListType:
-            for k in realkey:
-                if UserDict.has_key(self, k):
-                    return UserDict.__getitem__(self, k)
-        if UserDict.has_key(self, key):
-            return UserDict.__getitem__(self, key)
-        return UserDict.__getitem__(self, realkey)
-
-    def __setitem__(self, key, value):
-        for k in self.keymap.keys():
-            if key == k:
-                key = self.keymap[k]
-                if type(key) == types.ListType:
-                    key = key[0]
-        return UserDict.__setitem__(self, key, value)
-
-    def get(self, key, default=None):
-        if self.has_key(key):
-            return self[key]
-        else:
-            return default
-
-    def setdefault(self, key, value):
-        if not self.has_key(key):
-            self[key] = value
-        return self[key]
-        
-    def has_key(self, key):
-        try:
-            return hasattr(self, key) or UserDict.has_key(self, key)
-        except AttributeError:
-            return False
-        
-    def __getattr__(self, key):
-        try:
-            return self.__dict__[key]
-        except KeyError:
-            pass
-        try:
-            assert not key.startswith('_')
-            return self.__getitem__(key)
-        except:
-            raise AttributeError, "object has no attribute '%s'" % key
-
-    def __setattr__(self, key, value):
-        if key.startswith('_') or key == 'data':
-            self.__dict__[key] = value
-        else:
-            return self.__setitem__(key, value)
-
-    def __contains__(self, key):
-        return self.has_key(key)
-
-def zopeCompatibilityHack():
-    global FeedParserDict
-    del FeedParserDict
-    def FeedParserDict(aDict=None):
-        rc = {}
-        if aDict:
-            rc.update(aDict)
-        return rc
-
-_ebcdic_to_ascii_map = None
-def _ebcdic_to_ascii(s):
-    global _ebcdic_to_ascii_map
-    if not _ebcdic_to_ascii_map:
-        emap = (
-            0,1,2,3,156,9,134,127,151,141,142,11,12,13,14,15,
-            16,17,18,19,157,133,8,135,24,25,146,143,28,29,30,31,
-            128,129,130,131,132,10,23,27,136,137,138,139,140,5,6,7,
-            144,145,22,147,148,149,150,4,152,153,154,155,20,21,158,26,
-            32,160,161,162,163,164,165,166,167,168,91,46,60,40,43,33,
-            38,169,170,171,172,173,174,175,176,177,93,36,42,41,59,94,
-            45,47,178,179,180,181,182,183,184,185,124,44,37,95,62,63,
-            186,187,188,189,190,191,192,193,194,96,58,35,64,39,61,34,
-            195,97,98,99,100,101,102,103,104,105,196,197,198,199,200,201,
-            202,106,107,108,109,110,111,112,113,114,203,204,205,206,207,208,
-            209,126,115,116,117,118,119,120,121,122,210,211,212,213,214,215,
-            216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,
-            123,65,66,67,68,69,70,71,72,73,232,233,234,235,236,237,
-            125,74,75,76,77,78,79,80,81,82,238,239,240,241,242,243,
-            92,159,83,84,85,86,87,88,89,90,244,245,246,247,248,249,
-            48,49,50,51,52,53,54,55,56,57,250,251,252,253,254,255
-            )
-        import string
-        _ebcdic_to_ascii_map = string.maketrans( \
-            ''.join(map(chr, range(256))), ''.join(map(chr, emap)))
-    return s.translate(_ebcdic_to_ascii_map)
-
-_urifixer = re.compile('^([A-Za-z][A-Za-z0-9+-.]*://)(/*)(.*?)')
-def _urljoin(base, uri):
-    uri = _urifixer.sub(r'\1\3', uri)
-    return urlparse.urljoin(base, uri)
-
-class _FeedParserMixin:
-    namespaces = {'': '',
-                  'http://backend.userland.com/rss': '',
-                  'http://blogs.law.harvard.edu/tech/rss': '',
-                  'http://purl.org/rss/1.0/': '',
-                  'http://my.netscape.com/rdf/simple/0.9/': '',
-                  'http://example.com/newformat#': '',
-                  'http://example.com/necho': '',
-                  'http://purl.org/echo/': '',
-                  'uri/of/echo/namespace#': '',
-                  'http://purl.org/pie/': '',
-                  'http://purl.org/atom/ns#': '',
-                  'http://www.w3.org/2005/Atom': '',
-                  'http://purl.org/rss/1.0/modules/rss091#': '',
-                  
-                  'http://webns.net/mvcb/':                               'admin',
-                  'http://purl.org/rss/1.0/modules/aggregation/':         'ag',
-                  'http://purl.org/rss/1.0/modules/annotate/':            'annotate',
-                  'http://media.tangent.org/rss/1.0/':                    'audio',
-                  'http://backend.userland.com/blogChannelModule':        'blogChannel',
-                  'http://web.resource.org/cc/':                          'cc',
-                  'http://backend.userland.com/creativeCommonsRssModule': 'creativeCommons',
-                  'http://purl.org/rss/1.0/modules/company':              'co',
-                  'http://purl.org/rss/1.0/modules/content/':             'content',
-                  'http://my.theinfo.org/changed/1.0/rss/':               'cp',
-                  'http://purl.org/dc/elements/1.1/':                     'dc',
-                  'http://purl.org/dc/terms/':                            'dcterms',
-                  'http://purl.org/rss/1.0/modules/email/':               'email',
-                  'http://purl.org/rss/1.0/modules/event/':               'ev',
-                  'http://rssnamespace.org/feedburner/ext/1.0':           'feedburner',
-                  'http://freshmeat.net/rss/fm/':                         'fm',
-                  'http://xmlns.com/foaf/0.1/':                           'foaf',
-                  'http://www.w3.org/2003/01/geo/wgs84_pos#':             'geo',
-                  'http://postneo.com/icbm/':                             'icbm',
-                  'http://purl.org/rss/1.0/modules/image/':               'image',
-                  'http://www.itunes.com/DTDs/PodCast-1.0.dtd':           'itunes',
-                  'http://example.com/DTDs/PodCast-1.0.dtd':              'itunes',
-                  'http://purl.org/rss/1.0/modules/link/':                'l',
-                  'http://search.yahoo.com/mrss':                         'media',
-                  'http://madskills.com/public/xml/rss/module/pingback/': 'pingback',
-                  'http://prismstandard.org/namespaces/1.2/basic/':       'prism',
-                  'http://www.w3.org/1999/02/22-rdf-syntax-ns#':          'rdf',
-                  'http://www.w3.org/2000/01/rdf-schema#':                'rdfs',
-                  'http://purl.org/rss/1.0/modules/reference/':           'ref',
-                  'http://purl.org/rss/1.0/modules/richequiv/':           'reqv',
-                  'http://purl.org/rss/1.0/modules/search/':              'search',
-                  'http://purl.org/rss/1.0/modules/slash/':               'slash',
-                  'http://schemas.xmlsoap.org/soap/envelope/':            'soap',
-                  'http://purl.org/rss/1.0/modules/servicestatus/':       'ss',
-                  'http://hacks.benhammersley.com/rss/streaming/':        'str',
-                  'http://purl.org/rss/1.0/modules/subscription/':        'sub',
-                  'http://purl.org/rss/1.0/modules/syndication/':         'sy',
-                  'http://purl.org/rss/1.0/modules/taxonomy/':            'taxo',
-                  'http://purl.org/rss/1.0/modules/threading/':           'thr',
-                  'http://purl.org/rss/1.0/modules/textinput/':           'ti',
-                  'http://madskills.com/public/xml/rss/module/trackback/':'trackback',
-                  'http://wellformedweb.org/commentAPI/':                 'wfw',
-                  'http://purl.org/rss/1.0/modules/wiki/':                'wiki',
-                  'http://www.w3.org/1999/xhtml':                         'xhtml',
-                  'http://www.w3.org/XML/1998/namespace':                 'xml',
-                  'http://schemas.pocketsoap.com/rss/myDescModule/':      'szf'
-}
-    _matchnamespaces = {}
-
-    can_be_relative_uri = ['link', 'id', 'wfw_comment', 'wfw_commentrss', 'docs', 'url', 'href', 'comments', 'license', 'icon', 'logo']
-    can_contain_relative_uris = ['content', 'title', 'summary', 'info', 'tagline', 'subtitle', 'copyright', 'rights', 'description']
-    can_contain_dangerous_markup = ['content', 'title', 'summary', 'info', 'tagline', 'subtitle', 'copyright', 'rights', 'description']
-    html_types = ['text/html', 'application/xhtml+xml']
-    
-    def __init__(self, baseuri=None, baselang=None, encoding='utf-8'):
-        if _debug: sys.stderr.write('initializing FeedParser\n')
-        if not self._matchnamespaces:
-            for k, v in self.namespaces.items():
-                self._matchnamespaces[k.lower()] = v
-        self.feeddata = FeedParserDict() # feed-level data
-        self.encoding = encoding # character encoding
-        self.entries = [] # list of entry-level data
-        self.version = '' # feed type/version, see SUPPORTED_VERSIONS
-        self.namespacesInUse = {} # dictionary of namespaces defined by the feed
-
-        # the following are used internally to track state;
-        # this is really out of control and should be refactored
-        self.infeed = 0
-        self.inentry = 0
-        self.incontent = 0
-        self.intextinput = 0
-        self.inimage = 0
-        self.inauthor = 0
-        self.incontributor = 0
-        self.inpublisher = 0
-        self.insource = 0
-        self.sourcedata = FeedParserDict()
-        self.contentparams = FeedParserDict()
-        self._summaryKey = None
-        self.namespacemap = {}
-        self.elementstack = []
-        self.basestack = []
-        self.langstack = []
-        self.baseuri = baseuri or ''
-        self.lang = baselang or None
-        if baselang:
-            self.feeddata['language'] = baselang
-
-    def unknown_starttag(self, tag, attrs):
-        if _debug: sys.stderr.write('start %s with %s\n' % (tag, attrs))
-        # normalize attrs
-        attrs = [(k.lower(), v) for k, v in attrs]
-        attrs = [(k, k in ('rel', 'type') and v.lower() or v) for k, v in attrs]
-        
-        # track xml:base and xml:lang
-        attrsD = dict(attrs)
-        baseuri = attrsD.get('xml:base', attrsD.get('base')) or self.baseuri
-        self.baseuri = _urljoin(self.baseuri, baseuri)
-        lang = attrsD.get('xml:lang', attrsD.get('lang'))
-        if lang == '':
-            # xml:lang could be explicitly set to '', we need to capture that
-            lang = None
-        elif lang is None:
-            # if no xml:lang is specified, use parent lang
-            lang = self.lang
-        if lang:
-            if tag in ('feed', 'rss', 'rdf:RDF'):
-                self.feeddata['language'] = lang
-        self.lang = lang
-        self.basestack.append(self.baseuri)
-        self.langstack.append(lang)
-        
-        # track namespaces
-        for prefix, uri in attrs:
-            if prefix.startswith('xmlns:'):
-                self.trackNamespace(prefix[6:], uri)
-            elif prefix == 'xmlns':
-                self.trackNamespace(None, uri)
-
-        # track inline content
-        if self.incontent and self.contentparams.has_key('type') and not self.contentparams.get('type', 'xml').endswith('xml'):
-            # element declared itself as escaped markup, but it isn't really
-            self.contentparams['type'] = 'application/xhtml+xml'
-        if self.incontent and self.contentparams.get('type') == 'application/xhtml+xml':
-            # Note: probably shouldn't simply recreate localname here, but
-            # our namespace handling isn't actually 100% correct in cases where
-            # the feed redefines the default namespace (which is actually
-            # the usual case for inline content, thanks Sam), so here we
-            # cheat and just reconstruct the element based on localname
-            # because that compensates for the bugs in our namespace handling.
-            # This will horribly munge inline content with non-empty qnames,
-            # but nobody actually does that, so I'm not fixing it.
-            tag = tag.split(':')[-1]
-            return self.handle_data('<%s%s>' % (tag, ''.join([' %s="%s"' % t for t in attrs])), escape=0)
-
-        # match namespaces
-        if tag.find(':') <> -1:
-            prefix, suffix = tag.split(':', 1)
-        else:
-            prefix, suffix = '', tag
-        prefix = self.namespacemap.get(prefix, prefix)
-        if prefix:
-            prefix = prefix + '_'
-
-        # special hack for better tracking of empty textinput/image elements in illformed feeds
-        if (not prefix) and tag not in ('title', 'link', 'description', 'name'):
-            self.intextinput = 0
-        if (not prefix) and tag not in ('title', 'link', 'description', 'url', 'href', 'width', 'height'):
-            self.inimage = 0
-        
-        # call special handler (if defined) or default handler
-        methodname = '_start_' + prefix + suffix
-        try:
-            method = getattr(self, methodname)
-            return method(attrsD)
-        except AttributeError:
-            return self.push(prefix + suffix, 1)
-
-    def unknown_endtag(self, tag):
-        if _debug: sys.stderr.write('end %s\n' % tag)
-        # match namespaces
-        if tag.find(':') <> -1:
-            prefix, suffix = tag.split(':', 1)
-        else:
-            prefix, suffix = '', tag
-        prefix = self.namespacemap.get(prefix, prefix)
-        if prefix:
-            prefix = prefix + '_'
-
-        # call special handler (if defined) or default handler
-        methodname = '_end_' + prefix + suffix
-        try:
-            method = getattr(self, methodname)
-            method()
-        except AttributeError:
-            self.pop(prefix + suffix)
-
-        # track inline content
-        if self.incontent and self.contentparams.has_key('type') and not self.contentparams.get('type', 'xml').endswith('xml'):
-            # element declared itself as escaped markup, but it isn't really
-            self.contentparams['type'] = 'application/xhtml+xml'
-        if self.incontent and self.contentparams.get('type') == 'application/xhtml+xml':
-            tag = tag.split(':')[-1]
-            self.handle_data('</%s>' % tag, escape=0)
-
-        # track xml:base and xml:lang going out of scope
-        if self.basestack:
-            self.basestack.pop()
-            if self.basestack and self.basestack[-1]:
-                self.baseuri = self.basestack[-1]
-        if self.langstack:
-            self.langstack.pop()
-            if self.langstack: # and (self.langstack[-1] is not None):
-                self.lang = self.langstack[-1]
-
-    def handle_charref(self, ref):
-        # called for each character reference, e.g. for '&#160;', ref will be '160'
-        if not self.elementstack: return
-        ref = ref.lower()
-        if ref in ('34', '38', '39', '60', '62', 'x22', 'x26', 'x27', 'x3c', 'x3e'):
-            text = '&#%s;' % ref
-        else:
-            if ref[0] == 'x':
-                c = int(ref[1:], 16)
-            else:
-                c = int(ref)
-            text = unichr(c).encode('utf-8')
-        self.elementstack[-1][2].append(text)
-
-    def handle_entityref(self, ref):
-        # called for each entity reference, e.g. for '&copy;', ref will be 'copy'
-        if not self.elementstack: return
-        if _debug: sys.stderr.write('entering handle_entityref with %s\n' % ref)
-        if ref in ('lt', 'gt', 'quot', 'amp', 'apos'):
-            text = '&%s;' % ref
-        else:
-            # entity resolution graciously donated by Aaron Swartz
-            def name2cp(k):
-                import htmlentitydefs
-                if hasattr(htmlentitydefs, 'name2codepoint'): # requires Python 2.3
-                    return htmlentitydefs.name2codepoint[k]
-                k = htmlentitydefs.entitydefs[k]
-                if k.startswith('&#') and k.endswith(';'):
-                    return int(k[2:-1]) # not in latin-1
-                return ord(k)
-            try: name2cp(ref)
-            except KeyError: text = '&%s;' % ref
-            else: text = unichr(name2cp(ref)).encode('utf-8')
-        self.elementstack[-1][2].append(text)
-
-    def handle_data(self, text, escape=1):
-        # called for each block of plain text, i.e. outside of any tag and
-        # not containing any character or entity references
-        if not self.elementstack: return
-        if escape and self.contentparams.get('type') == 'application/xhtml+xml':
-            text = _xmlescape(text)
-        self.elementstack[-1][2].append(text)
-
-    def handle_comment(self, text):
-        # called for each comment, e.g. <!-- insert message here -->
-        pass
-
-    def handle_pi(self, text):
-        # called for each processing instruction, e.g. <?instruction>
-        pass
-
-    def handle_decl(self, text):
-        pass
-
-    def parse_declaration(self, i):
-        # override internal declaration handler to handle CDATA blocks
-        if _debug: sys.stderr.write('entering parse_declaration\n')
-        if self.rawdata[i:i+9] == '<![CDATA[':
-            k = self.rawdata.find(']]>', i)
-            if k == -1: k = len(self.rawdata)
-            self.handle_data(_xmlescape(self.rawdata[i+9:k]), 0)
-            return k+3
-        else:
-            k = self.rawdata.find('>', i)
-            return k+1
-
-    def mapContentType(self, contentType):
-        contentType = contentType.lower()
-        if contentType == 'text':
-            contentType = 'text/plain'
-        elif contentType == 'html':
-            contentType = 'text/html'
-        elif contentType == 'xhtml':
-            contentType = 'application/xhtml+xml'
-        return contentType
-    
-    def trackNamespace(self, prefix, uri):
-        loweruri = uri.lower()
-        if (prefix, loweruri) == (None, 'http://my.netscape.com/rdf/simple/0.9/') and not self.version:
-            self.version = 'rss090'
-        if loweruri == 'http://purl.org/rss/1.0/' and not self.version:
-            self.version = 'rss10'
-        if loweruri == 'http://www.w3.org/2005/atom' and not self.version:
-            self.version = 'atom10'
-        if loweruri.find('backend.userland.com/rss') <> -1:
-            # match any backend.userland.com namespace
-            uri = 'http://backend.userland.com/rss'
-            loweruri = uri
-        if self._matchnamespaces.has_key(loweruri):
-            self.namespacemap[prefix] = self._matchnamespaces[loweruri]
-            self.namespacesInUse[self._matchnamespaces[loweruri]] = uri
-        else:
-            self.namespacesInUse[prefix or ''] = uri
-
-    def resolveURI(self, uri):
-        return _urljoin(self.baseuri or '', uri)
-    
-    def decodeEntities(self, element, data):
-        return data
-
-    def push(self, element, expectingText):
-        self.elementstack.append([element, expectingText, []])
-
-    def pop(self, element, stripWhitespace=1):
-        if not self.elementstack: return
-        if self.elementstack[-1][0] != element: return
-        
-        element, expectingText, pieces = self.elementstack.pop()
-        output = ''.join(pieces)
-        if stripWhitespace:
-            output = output.strip()
-        if not expectingText: return output
-
-        # decode base64 content
-        if base64 and self.contentparams.get('base64', 0):
-            try:
-                output = base64.decodestring(output)
-            except binascii.Error:
-                pass
-            except binascii.Incomplete:
-                pass
-                
-        # resolve relative URIs
-        if (element in self.can_be_relative_uri) and output:
-            output = self.resolveURI(output)
-        
-        # decode entities within embedded markup
-        if not self.contentparams.get('base64', 0):
-            output = self.decodeEntities(element, output)
-
-        # remove temporary cruft from contentparams
-        try:
-            del self.contentparams['mode']
-        except KeyError:
-            pass
-        try:
-            del self.contentparams['base64']
-        except KeyError:
-            pass
-
-        # resolve relative URIs within embedded markup
-        if self.mapContentType(self.contentparams.get('type', 'text/html')) in self.html_types:
-            if element in self.can_contain_relative_uris:
-                output = _resolveRelativeURIs(output, self.baseuri, self.encoding)
-        
-        # sanitize embedded markup
-        if self.mapContentType(self.contentparams.get('type', 'text/html')) in self.html_types:
-            if element in self.can_contain_dangerous_markup:
-                output = _sanitizeHTML(output, self.encoding)
-
-        if self.encoding and type(output) != type(u''):
-            try:
-                output = unicode(output, self.encoding)
-            except:
-                pass
-
-        # categories/tags/keywords/whatever are handled in _end_category
-        if element == 'category':
-            return output
-        
-        # store output in appropriate place(s)
-        if self.inentry and not self.insource:
-            if element == 'content':
-                self.entries[-1].setdefault(element, [])
-                contentparams = copy.deepcopy(self.contentparams)
-                contentparams['value'] = output
-                self.entries[-1][element].append(contentparams)
-            elif element == 'link':
-                self.entries[-1][element] = output
-                if output:
-                    self.entries[-1]['links'][-1]['href'] = output
-            else:
-                if element == 'description':
-                    element = 'summary'
-                self.entries[-1][element] = output
-                if self.incontent:
-                    contentparams = copy.deepcopy(self.contentparams)
-                    contentparams['value'] = output
-                    self.entries[-1][element + '_detail'] = contentparams
-        elif (self.infeed or self.insource) and (not self.intextinput) and (not self.inimage):
-            context = self._getContext()
-            if element == 'description':
-                element = 'subtitle'
-            context[element] = output
-            if element == 'link':
-                context['links'][-1]['href'] = output
-            elif self.incontent:
-                contentparams = copy.deepcopy(self.contentparams)
-                contentparams['value'] = output
-                context[element + '_detail'] = contentparams
-        return output
-
-    def pushContent(self, tag, attrsD, defaultContentType, expectingText):
-        self.incontent += 1
-        self.contentparams = FeedParserDict({
-            'type': self.mapContentType(attrsD.get('type', defaultContentType)),
-            'language': self.lang,
-            'base': self.baseuri})
-        self.contentparams['base64'] = self._isBase64(attrsD, self.contentparams)
-        self.push(tag, expectingText)
-
-    def popContent(self, tag):
-        value = self.pop(tag)
-        self.incontent -= 1
-        self.contentparams.clear()
-        return value
-        
-    def _mapToStandardPrefix(self, name):
-        colonpos = name.find(':')
-        if colonpos <> -1:
-            prefix = name[:colonpos]
-            suffix = name[colonpos+1:]
-            prefix = self.namespacemap.get(prefix, prefix)
-            name = prefix + ':' + suffix
-        return name
-        
-    def _getAttribute(self, attrsD, name):
-        return attrsD.get(self._mapToStandardPrefix(name))
-
-    def _isBase64(self, attrsD, contentparams):
-        if attrsD.get('mode', '') == 'base64':
-            return 1
-        if self.contentparams['type'].startswith('text/'):
-            return 0
-        if self.contentparams['type'].endswith('+xml'):
-            return 0
-        if self.contentparams['type'].endswith('/xml'):
-            return 0
-        return 1
-
-    def _itsAnHrefDamnIt(self, attrsD):
-        href = attrsD.get('url', attrsD.get('uri', attrsD.get('href', None)))
-        if href:
-            try:
-                del attrsD['url']
-            except KeyError:
-                pass
-            try:
-                del attrsD['uri']
-            except KeyError:
-                pass
-            attrsD['href'] = href
-        return attrsD
-    
-    def _save(self, key, value):
-        context = self._getContext()
-        context.setdefault(key, value)
-
-    def _start_rss(self, attrsD):
-        versionmap = {'0.91': 'rss091u',
-                      '0.92': 'rss092',
-                      '0.93': 'rss093',
-                      '0.94': 'rss094'}
-        if not self.version:
-            attr_version = attrsD.get('version', '')
-            version = versionmap.get(attr_version)
-            if version:
-                self.version = version
-            elif attr_version.startswith('2.'):
-                self.version = 'rss20'
-            else:
-                self.version = 'rss'
-    
-    def _start_dlhottitles(self, attrsD):
-        self.version = 'hotrss'
-
-    def _start_channel(self, attrsD):
-        self.infeed = 1
-        self._cdf_common(attrsD)
-    _start_feedinfo = _start_channel
-
-    def _cdf_common(self, attrsD):
-        if attrsD.has_key('lastmod'):
-            self._start_modified({})
-            self.elementstack[-1][-1] = attrsD['lastmod']
-            self._end_modified()
-        if attrsD.has_key('href'):
-            self._start_link({})
-            self.elementstack[-1][-1] = attrsD['href']
-            self._end_link()
-    
-    def _start_feed(self, attrsD):
-        self.infeed = 1
-        versionmap = {'0.1': 'atom01',
-                      '0.2': 'atom02',
-                      '0.3': 'atom03'}
-        if not self.version:
-            attr_version = attrsD.get('version')
-            version = versionmap.get(attr_version)
-            if version:
-                self.version = version
-            else:
-                self.version = 'atom'
-
-    def _end_channel(self):
-        self.infeed = 0
-    _end_feed = _end_channel
-    
-    def _start_image(self, attrsD):
-        self.inimage = 1
-        self.push('image', 0)
-        context = self._getContext()
-        context.setdefault('image', FeedParserDict())
-            
-    def _end_image(self):
-        self.pop('image')
-        self.inimage = 0
-
-    def _start_textinput(self, attrsD):
-        self.intextinput = 1
-        self.push('textinput', 0)
-        context = self._getContext()
-        context.setdefault('textinput', FeedParserDict())
-    _start_textInput = _start_textinput
-    
-    def _end_textinput(self):
-        self.pop('textinput')
-        self.intextinput = 0
-    _end_textInput = _end_textinput
-
-    def _start_author(self, attrsD):
-        self.inauthor = 1
-        self.push('author', 1)
-    _start_managingeditor = _start_author
-    _start_dc_author = _start_author
-    _start_dc_creator = _start_author
-    _start_itunes_author = _start_author
-
-    def _end_author(self):
-        self.pop('author')
-        self.inauthor = 0
-        self._sync_author_detail()
-    _end_managingeditor = _end_author
-    _end_dc_author = _end_author
-    _end_dc_creator = _end_author
-    _end_itunes_author = _end_author
-
-    def _start_itunes_owner(self, attrsD):
-        self.inpublisher = 1
-        self.push('publisher', 0)
-
-    def _end_itunes_owner(self):
-        self.pop('publisher')
-        self.inpublisher = 0
-        self._sync_author_detail('publisher')
-
-    def _start_contributor(self, attrsD):
-        self.incontributor = 1
-        context = self._getContext()
-        context.setdefault('contributors', [])
-        context['contributors'].append(FeedParserDict())
-        self.push('contributor', 0)
-
-    def _end_contributor(self):
-        self.pop('contributor')
-        self.incontributor = 0
-
-    def _start_dc_contributor(self, attrsD):
-        self.incontributor = 1
-        context = self._getContext()
-        context.setdefault('contributors', [])
-        context['contributors'].append(FeedParserDict())
-        self.push('name', 0)
-
-    def _end_dc_contributor(self):
-        self._end_name()
-        self.incontributor = 0
-
-    def _start_name(self, attrsD):
-        self.push('name', 0)
-    _start_itunes_name = _start_name
-
-    def _end_name(self):
-        value = self.pop('name')
-        if self.inpublisher:
-            self._save_author('name', value, 'publisher')
-        elif self.inauthor:
-            self._save_author('name', value)
-        elif self.incontributor:
-            self._save_contributor('name', value)
-        elif self.intextinput:
-            context = self._getContext()
-            context['textinput']['name'] = value
-    _end_itunes_name = _end_name
-
-    def _start_width(self, attrsD):
-        self.push('width', 0)
-
-    def _end_width(self):
-        value = self.pop('width')
-        try:
-            value = int(value)
-        except:
-            value = 0
-        if self.inimage:
-            context = self._getContext()
-            context['image']['width'] = value
-
-    def _start_height(self, attrsD):
-        self.push('height', 0)
-
-    def _end_height(self):
-        value = self.pop('height')
-        try:
-            value = int(value)
-        except:
-            value = 0
-        if self.inimage:
-            context = self._getContext()
-            context['image']['height'] = value
-
-    def _start_url(self, attrsD):
-        self.push('href', 1)
-    _start_homepage = _start_url
-    _start_uri = _start_url
-
-    def _end_url(self):
-        value = self.pop('href')
-        if self.inauthor:
-            self._save_author('href', value)
-        elif self.incontributor:
-            self._save_contributor('href', value)
-        elif self.inimage:
-            context = self._getContext()
-            context['image']['href'] = value
-        elif self.intextinput:
-            context = self._getContext()
-            context['textinput']['link'] = value
-    _end_homepage = _end_url
-    _end_uri = _end_url
-
-    def _start_email(self, attrsD):
-        self.push('email', 0)
-    _start_itunes_email = _start_email
-
-    def _end_email(self):
-        value = self.pop('email')
-        if self.inpublisher:
-            self._save_author('email', value, 'publisher')
-        elif self.inauthor:
-            self._save_author('email', value)
-        elif self.incontributor:
-            self._save_contributor('email', value)
-    _end_itunes_email = _end_email
-
-    def _getContext(self):
-        if self.insource:
-            context = self.sourcedata
-        elif self.inentry:
-            context = self.entries[-1]
-        else:
-            context = self.feeddata
-        return context
-
-    def _save_author(self, key, value, prefix='author'):
-        context = self._getContext()
-        context.setdefault(prefix + '_detail', FeedParserDict())
-        context[prefix + '_detail'][key] = value
-        self._sync_author_detail()
-
-    def _save_contributor(self, key, value):
-        context = self._getContext()
-        context.setdefault('contributors', [FeedParserDict()])
-        context['contributors'][-1][key] = value
-
-    def _sync_author_detail(self, key='author'):
-        context = self._getContext()
-        detail = context.get('%s_detail' % key)
-        if detail:
-            name = detail.get('name')
-            email = detail.get('email')
-            if name and email:
-                context[key] = '%s (%s)' % (name, email)
-            elif name:
-                context[key] = name
-            elif email:
-                context[key] = email
-        else:
-            author = context.get(key)
-            if not author: return
-            emailmatch = re.search(r'''(([a-zA-Z0-9\_\-\.\+]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?))''', author)
-            if not emailmatch: return
-            email = emailmatch.group(0)
-            # probably a better way to do the following, but it passes all the tests
-            author = author.replace(email, '')
-            author = author.replace('()', '')
-            author = author.strip()
-            if author and (author[0] == '('):
-                author = author[1:]
-            if author and (author[-1] == ')'):
-                author = author[:-1]
-            author = author.strip()
-            context.setdefault('%s_detail' % key, FeedParserDict())
-            context['%s_detail' % key]['name'] = author
-            context['%s_detail' % key]['email'] = email
-
-    def _start_subtitle(self, attrsD):
-        self.pushContent('subtitle', attrsD, 'text/plain', 1)
-    _start_tagline = _start_subtitle
-    _start_itunes_subtitle = _start_subtitle
-
-    def _end_subtitle(self):
-        self.popContent('subtitle')
-    _end_tagline = _end_subtitle
-    _end_itunes_subtitle = _end_subtitle
-            
-    def _start_rights(self, attrsD):
-        self.pushContent('rights', attrsD, 'text/plain', 1)
-    _start_dc_rights = _start_rights
-    _start_copyright = _start_rights
-
-    def _end_rights(self):
-        self.popContent('rights')
-    _end_dc_rights = _end_rights
-    _end_copyright = _end_rights
-
-    def _start_item(self, attrsD):
-        self.entries.append(FeedParserDict())
-        self.push('item', 0)
-        self.inentry = 1
-        self.guidislink = 0
-        id = self._getAttribute(attrsD, 'rdf:about')
-        if id:
-            context = self._getContext()
-            context['id'] = id
-        self._cdf_common(attrsD)
-    _start_entry = _start_item
-    _start_product = _start_item
-
-    def _end_item(self):
-        self.pop('item')
-        self.inentry = 0
-    _end_entry = _end_item
-
-    def _start_dc_language(self, attrsD):
-        self.push('language', 1)
-    _start_language = _start_dc_language
-
-    def _end_dc_language(self):
-        self.lang = self.pop('language')
-    _end_language = _end_dc_language
-
-    def _start_dc_publisher(self, attrsD):
-        self.push('publisher', 1)
-    _start_webmaster = _start_dc_publisher
-
-    def _end_dc_publisher(self):
-        self.pop('publisher')
-        self._sync_author_detail('publisher')
-    _end_webmaster = _end_dc_publisher
-
-    def _start_published(self, attrsD):
-        self.push('published', 1)
-    _start_dcterms_issued = _start_published
-    _start_issued = _start_published
-
-    def _end_published(self):
-        value = self.pop('published')
-        self._save('published_parsed', _parse_date(value))
-    _end_dcterms_issued = _end_published
-    _end_issued = _end_published
-
-    def _start_updated(self, attrsD):
-        self.push('updated', 1)
-    _start_modified = _start_updated
-    _start_dcterms_modified = _start_updated
-    _start_pubdate = _start_updated
-    _start_dc_date = _start_updated
-
-    def _end_updated(self):
-        value = self.pop('updated')
-        parsed_value = _parse_date(value)
-        self._save('updated_parsed', parsed_value)
-    _end_modified = _end_updated
-    _end_dcterms_modified = _end_updated
-    _end_pubdate = _end_updated
-    _end_dc_date = _end_updated
-
-    def _start_created(self, attrsD):
-        self.push('created', 1)
-    _start_dcterms_created = _start_created
-
-    def _end_created(self):
-        value = self.pop('created')
-        self._save('created_parsed', _parse_date(value))
-    _end_dcterms_created = _end_created
-
-    def _start_expirationdate(self, attrsD):
-        self.push('expired', 1)
-
-    def _end_expirationdate(self):
-        self._save('expired_parsed', _parse_date(self.pop('expired')))
-
-    def _start_cc_license(self, attrsD):
-        self.push('license', 1)
-        value = self._getAttribute(attrsD, 'rdf:resource')
-        if value:
-            self.elementstack[-1][2].append(value)
-        self.pop('license')
-        
-    def _start_creativecommons_license(self, attrsD):
-        self.push('license', 1)
-
-    def _end_creativecommons_license(self):
-        self.pop('license')
-
-    def _addTag(self, term, scheme, label):
-        context = self._getContext()
-        tags = context.setdefault('tags', [])
-        if (not term) and (not scheme) and (not label): return
-        value = FeedParserDict({'term': term, 'scheme': scheme, 'label': label})
-        if value not in tags:
-            tags.append(FeedParserDict({'term': term, 'scheme': scheme, 'label': label}))
-
-    def _start_category(self, attrsD):
-        if _debug: sys.stderr.write('entering _start_category with %s\n' % repr(attrsD))
-        term = attrsD.get('term')
-        scheme = attrsD.get('scheme', attrsD.get('domain'))
-        label = attrsD.get('label')
-        self._addTag(term, scheme, label)
-        self.push('category', 1)
-    _start_dc_subject = _start_category
-    _start_keywords = _start_category
-        
-    def _end_itunes_keywords(self):
-        for term in self.pop('itunes_keywords').split():
-            self._addTag(term, 'http://www.itunes.com/', None)
-        
-    def _start_itunes_category(self, attrsD):
-        self._addTag(attrsD.get('text'), 'http://www.itunes.com/', None)
-        self.push('category', 1)
-        
-    def _end_category(self):
-        value = self.pop('category')
-        if not value: return
-        context = self._getContext()
-        tags = context['tags']
-        if value and len(tags) and not tags[-1]['term']:
-            tags[-1]['term'] = value
-        else:
-            self._addTag(value, None, None)
-    _end_dc_subject = _end_category
-    _end_keywords = _end_category
-    _end_itunes_category = _end_category
-
-    def _start_cloud(self, attrsD):
-        self._getContext()['cloud'] = FeedParserDict(attrsD)
-        
-    def _start_link(self, attrsD):
-        attrsD.setdefault('rel', 'alternate')
-        attrsD.setdefault('type', 'text/html')
-        attrsD = self._itsAnHrefDamnIt(attrsD)
-        if attrsD.has_key('href'):
-            attrsD['href'] = self.resolveURI(attrsD['href'])
-        expectingText = self.infeed or self.inentry or self.insource
-        context = self._getContext()
-        context.setdefault('links', [])
-        context['links'].append(FeedParserDict(attrsD))
-        if attrsD['rel'] == 'enclosure':
-            self._start_enclosure(attrsD)
-        if attrsD.has_key('href'):
-            expectingText = 0
-            if (attrsD.get('rel') == 'alternate') and (self.mapContentType(attrsD.get('type')) in self.html_types):
-                context['link'] = attrsD['href']
-        else:
-            self.push('link', expectingText)
-    _start_producturl = _start_link
-
-    def _end_link(self):
-        value = self.pop('link')
-        context = self._getContext()
-        if self.intextinput:
-            context['textinput']['link'] = value
-        if self.inimage:
-            context['image']['link'] = value
-    _end_producturl = _end_link
-
-    def _start_guid(self, attrsD):
-        self.guidislink = (attrsD.get('ispermalink', 'true') == 'true')
-        self.push('id', 1)
-
-    def _end_guid(self):
-        value = self.pop('id')
-        self._save('guidislink', self.guidislink and not self._getContext().has_key('link'))
-        if self.guidislink:
-            # guid acts as link, but only if 'ispermalink' is not present or is 'true',
-            # and only if the item doesn't already have a link element
-            self._save('link', value)
-
-    def _start_title(self, attrsD):
-        self.pushContent('title', attrsD, 'text/plain', self.infeed or self.inentry or self.insource)
-    _start_dc_title = _start_title
-    _start_media_title = _start_title
-
-    def _end_title(self):
-        value = self.popContent('title')
-        context = self._getContext()
-        if self.intextinput:
-            context['textinput']['title'] = value
-        elif self.inimage:
-            context['image']['title'] = value
-    _end_dc_title = _end_title
-    _end_media_title = _end_title
-
-    def _start_description(self, attrsD):
-        context = self._getContext()
-        if context.has_key('summary'):
-            self._summaryKey = 'content'
-            self._start_content(attrsD)
-        else:
-            self.pushContent('description', attrsD, 'text/html', self.infeed or self.inentry or self.insource)
-
-    def _start_abstract(self, attrsD):
-        self.pushContent('description', attrsD, 'text/plain', self.infeed or self.inentry or self.insource)
-
-    def _end_description(self):
-        if self._summaryKey == 'content':
-            self._end_content()
-        else:
-            value = self.popContent('description')
-            context = self._getContext()
-            if self.intextinput:
-                context['textinput']['description'] = value
-            elif self.inimage:
-                context['image']['description'] = value
-        self._summaryKey = None
-    _end_abstract = _end_description
-
-    def _start_info(self, attrsD):
-        self.pushContent('info', attrsD, 'text/plain', 1)
-    _start_feedburner_browserfriendly = _start_info
-
-    def _end_info(self):
-        self.popContent('info')
-    _end_feedburner_browserfriendly = _end_info
-
-    def _start_generator(self, attrsD):
-        if attrsD:
-            attrsD = self._itsAnHrefDamnIt(attrsD)
-            if attrsD.has_key('href'):
-                attrsD['href'] = self.resolveURI(attrsD['href'])
-        self._getContext()['generator_detail'] = FeedParserDict(attrsD)
-        self.push('generator', 1)
-
-    def _end_generator(self):
-        value = self.pop('generator')
-        context = self._getContext()
-        if context.has_key('generator_detail'):
-            context['generator_detail']['name'] = value
-            
-    def _start_admin_generatoragent(self, attrsD):
-        self.push('generator', 1)
-        value = self._getAttribute(attrsD, 'rdf:resource')
-        if value:
-            self.elementstack[-1][2].append(value)
-        self.pop('generator')
-        self._getContext()['generator_detail'] = FeedParserDict({'href': value})
-
-    def _start_admin_errorreportsto(self, attrsD):
-        self.push('errorreportsto', 1)
-        value = self._getAttribute(attrsD, 'rdf:resource')
-        if value:
-            self.elementstack[-1][2].append(value)
-        self.pop('errorreportsto')
-        
-    def _start_summary(self, attrsD):
-        context = self._getContext()
-        if context.has_key('summary'):
-            self._summaryKey = 'content'
-            self._start_content(attrsD)
-        else:
-            self._summaryKey = 'summary'
-            self.pushContent(self._summaryKey, attrsD, 'text/plain', 1)
-    _start_itunes_summary = _start_summary
-
-    def _end_summary(self):
-        if self._summaryKey == 'content':
-            self._end_content()
-        else:
-            self.popContent(self._summaryKey or 'summary')
-        self._summaryKey = None
-    _end_itunes_summary = _end_summary
-        
-    def _start_enclosure(self, attrsD):
-        attrsD = self._itsAnHrefDamnIt(attrsD)
-        self._getContext().setdefault('enclosures', []).append(FeedParserDict(attrsD))
-        href = attrsD.get('href')
-        if href:
-            context = self._getContext()
-            if not context.get('id'):
-                context['id'] = href
-            
-    def _start_source(self, attrsD):
-        self.insource = 1
-
-    def _end_source(self):
-        self.insource = 0
-        self._getContext()['source'] = copy.deepcopy(self.sourcedata)
-        self.sourcedata.clear()
-
-    def _start_content(self, attrsD):
-        self.pushContent('content', attrsD, 'text/plain', 1)
-        src = attrsD.get('src')
-        if src:
-            self.contentparams['src'] = src
-        self.push('content', 1)
-
-    def _start_prodlink(self, attrsD):
-        self.pushContent('content', attrsD, 'text/html', 1)
-
-    def _start_body(self, attrsD):
-        self.pushContent('content', attrsD, 'application/xhtml+xml', 1)
-    _start_xhtml_body = _start_body
-
-    def _start_content_encoded(self, attrsD):
-        self.pushContent('content', attrsD, 'text/html', 1)
-    _start_fullitem = _start_content_encoded
-
-    def _end_content(self):
-        copyToDescription = self.mapContentType(self.contentparams.get('type')) in (['text/plain'] + self.html_types)
-        value = self.popContent('content')
-        if copyToDescription:
-            self._save('description', value)
-    _end_body = _end_content
-    _end_xhtml_body = _end_content
-    _end_content_encoded = _end_content
-    _end_fullitem = _end_content
-    _end_prodlink = _end_content
-
-    def _start_itunes_image(self, attrsD):
-        self.push('itunes_image', 0)
-        self._getContext()['image'] = FeedParserDict({'href': attrsD.get('href')})
-    _start_itunes_link = _start_itunes_image
-        
-    def _end_itunes_block(self):
-        value = self.pop('itunes_block', 0)
-        self._getContext()['itunes_block'] = (value == 'yes') and 1 or 0
-
-    def _end_itunes_explicit(self):
-        value = self.pop('itunes_explicit', 0)
-        self._getContext()['itunes_explicit'] = (value == 'yes') and 1 or 0
-
-if _XML_AVAILABLE:
-    class _StrictFeedParser(_FeedParserMixin, xml.sax.handler.ContentHandler):
-        def __init__(self, baseuri, baselang, encoding):
-            if _debug: sys.stderr.write('trying StrictFeedParser\n')
-            xml.sax.handler.ContentHandler.__init__(self)
-            _FeedParserMixin.__init__(self, baseuri, baselang, encoding)
-            self.bozo = 0
-            self.exc = None
-        
-        def startPrefixMapping(self, prefix, uri):
-            self.trackNamespace(prefix, uri)
-        
-        def startElementNS(self, name, qname, attrs):
-            namespace, localname = name
-            lowernamespace = str(namespace or '').lower()
-            if lowernamespace.find('backend.userland.com/rss') <> -1:
-                # match any backend.userland.com namespace
-                namespace = 'http://backend.userland.com/rss'
-                lowernamespace = namespace
-            if qname and qname.find(':') > 0:
-                givenprefix = qname.split(':')[0]
-            else:
-                givenprefix = None
-            prefix = self._matchnamespaces.get(lowernamespace, givenprefix)
-            if givenprefix and (prefix == None or (prefix == '' and lowernamespace == '')) and not self.namespacesInUse.has_key(givenprefix):
-                    raise UndeclaredNamespace, "'%s' is not associated with a namespace" % givenprefix
-            if prefix:
-                localname = prefix + ':' + localname
-            localname = str(localname).lower()
-            if _debug: sys.stderr.write('startElementNS: qname = %s, namespace = %s, givenprefix = %s, prefix = %s, attrs = %s, localname = %s\n' % (qname, namespace, givenprefix, prefix, attrs.items(), localname))
-
-            # qname implementation is horribly broken in Python 2.1 (it
-            # doesn't report any), and slightly broken in Python 2.2 (it
-            # doesn't report the xml: namespace). So we match up namespaces
-            # with a known list first, and then possibly override them with
-            # the qnames the SAX parser gives us (if indeed it gives us any
-            # at all).  Thanks to MatejC for helping me test this and
-            # tirelessly telling me that it didn't work yet.
-            attrsD = {}
-            for (namespace, attrlocalname), attrvalue in attrs._attrs.items():
-                lowernamespace = (namespace or '').lower()
-                prefix = self._matchnamespaces.get(lowernamespace, '')
-                if prefix:
-                    attrlocalname = prefix + ':' + attrlocalname
-                attrsD[str(attrlocalname).lower()] = attrvalue
-            for qname in attrs.getQNames():
-                attrsD[str(qname).lower()] = attrs.getValueByQName(qname)
-            self.unknown_starttag(localname, attrsD.items())
-
-        def characters(self, text):
-            self.handle_data(text)
-
-        def endElementNS(self, name, qname):
-            namespace, localname = name
-            lowernamespace = str(namespace or '').lower()
-            if qname and qname.find(':') > 0:
-                givenprefix = qname.split(':')[0]
-            else:
-                givenprefix = ''
-            prefix = self._matchnamespaces.get(lowernamespace, givenprefix)
-            if prefix:
-                localname = prefix + ':' + localname
-            localname = str(localname).lower()
-            self.unknown_endtag(localname)
-
-        def error(self, exc):
-            self.bozo = 1
-            self.exc = exc
-            
-        def fatalError(self, exc):
-            self.error(exc)
-            raise exc
-
-class _BaseHTMLProcessor(sgmllib.SGMLParser):
-    elements_no_end_tag = ['area', 'base', 'basefont', 'br', 'col', 'frame', 'hr',
-      'img', 'input', 'isindex', 'link', 'meta', 'param']
-    
-    def __init__(self, encoding):
-        self.encoding = encoding
-        if _debug: sys.stderr.write('entering BaseHTMLProcessor, encoding=%s\n' % self.encoding)
-        sgmllib.SGMLParser.__init__(self)
-        
-    def reset(self):
-        self.pieces = []
-        sgmllib.SGMLParser.reset(self)
-
-    def _shorttag_replace(self, match):
-        tag = match.group(1)
-        if tag in self.elements_no_end_tag:
-            return '<' + tag + ' />'
-        else:
-            return '<' + tag + '></' + tag + '>'
-        
-    def feed(self, data):
-        data = re.compile(r'<!((?!DOCTYPE|--|\[))', re.IGNORECASE).sub(r'&lt;!\1', data)
-        #data = re.sub(r'<(\S+?)\s*?/>', self._shorttag_replace, data) # bug [ 1399464 ] Bad regexp for _shorttag_replace
-        data = re.sub(r'<([^<\s]+?)\s*/>', self._shorttag_replace, data) 
-        data = data.replace('&#39;', "'")
-        data = data.replace('&#34;', '"')
-        if self.encoding and type(data) == type(u''):
-            data = data.encode(self.encoding)
-        sgmllib.SGMLParser.feed(self, data)
-
-    def normalize_attrs(self, attrs):
-        # utility method to be called by descendants
-        attrs = [(k.lower(), v) for k, v in attrs]
-        attrs = [(k, k in ('rel', 'type') and v.lower() or v) for k, v in attrs]
-        return attrs
-
-    def unknown_starttag(self, tag, attrs):
-        # called for each start tag
-        # attrs is a list of (attr, value) tuples
-        # e.g. for <pre class='screen'>, tag='pre', attrs=[('class', 'screen')]
-        if _debug: sys.stderr.write('_BaseHTMLProcessor, unknown_starttag, tag=%s\n' % tag)
-        uattrs = []
-        # thanks to Kevin Marks for this breathtaking hack to deal with (valid) high-bit attribute values in UTF-8 feeds
-        for key, value in attrs:
-            if type(value) != type(u''):
-                value = unicode(value, self.encoding)
-            uattrs.append((unicode(key, self.encoding), value))
-        strattrs = u''.join([u' %s="%s"' % (key, value) for key, value in uattrs]).encode(self.encoding)
-        if tag in self.elements_no_end_tag:
-            self.pieces.append('<%(tag)s%(strattrs)s />' % locals())
-        else:
-            self.pieces.append('<%(tag)s%(strattrs)s>' % locals())
-
-    def unknown_endtag(self, tag):
-        # called for each end tag, e.g. for </pre>, tag will be 'pre'
-        # Reconstruct the original end tag.
-        if tag not in self.elements_no_end_tag:
-            self.pieces.append("</%(tag)s>" % locals())
-
-    def handle_charref(self, ref):
-        # called for each character reference, e.g. for '&#160;', ref will be '160'
-        # Reconstruct the original character reference.
-        self.pieces.append('&#%(ref)s;' % locals())
-        
-    def handle_entityref(self, ref):
-        # called for each entity reference, e.g. for '&copy;', ref will be 'copy'
-        # Reconstruct the original entity reference.
-        self.pieces.append('&%(ref)s;' % locals())
-
-    def handle_data(self, text):
-        # called for each block of plain text, i.e. outside of any tag and
-        # not containing any character or entity references
-        # Store the original text verbatim.
-        if _debug: sys.stderr.write('_BaseHTMLProcessor, handle_text, text=%s\n' % text)
-        self.pieces.append(text)
-        
-    def handle_comment(self, text):
-        # called for each HTML comment, e.g. <!-- insert Javascript code here -->
-        # Reconstruct the original comment.
-        self.pieces.append('<!--%(text)s-->' % locals())
-        
-    def handle_pi(self, text):
-        # called for each processing instruction, e.g. <?instruction>
-        # Reconstruct original processing instruction.
-        self.pieces.append('<?%(text)s>' % locals())
-
-    def handle_decl(self, text):
-        # called for the DOCTYPE, if present, e.g.
-        # <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
-        #     "http://www.w3.org/TR/html4/loose.dtd">
-        # Reconstruct original DOCTYPE
-        self.pieces.append('<!%(text)s>' % locals())
-        
-    _new_declname_match = re.compile(r'[a-zA-Z][-_.a-zA-Z0-9:]*\s*').match
-    def _scan_name(self, i, declstartpos):
-        rawdata = self.rawdata
-        n = len(rawdata)
-        if i == n:
-            return None, -1
-        m = self._new_declname_match(rawdata, i)
-        if m:
-            s = m.group()
-            name = s.strip()
-            if (i + len(s)) == n:
-                return None, -1  # end of buffer
-            return name.lower(), m.end()
-        else:
-            self.handle_data(rawdata)
-#            self.updatepos(declstartpos, i)
-            return None, -1
-
-    def output(self):
-        '''Return processed HTML as a single string'''
-        return ''.join([str(p) for p in self.pieces])
-
-class _LooseFeedParser(_FeedParserMixin, _BaseHTMLProcessor):
-    def __init__(self, baseuri, baselang, encoding):
-        sgmllib.SGMLParser.__init__(self)
-        _FeedParserMixin.__init__(self, baseuri, baselang, encoding)
-
-    def decodeEntities(self, element, data):
-        data = data.replace('&#60;', '&lt;')
-        data = data.replace('&#x3c;', '&lt;')
-        data = data.replace('&#62;', '&gt;')
-        data = data.replace('&#x3e;', '&gt;')
-        data = data.replace('&#38;', '&amp;')
-        data = data.replace('&#x26;', '&amp;')
-        data = data.replace('&#34;', '&quot;')
-        data = data.replace('&#x22;', '&quot;')
-        data = data.replace('&#39;', '&apos;')
-        data = data.replace('&#x27;', '&apos;')
-        if self.contentparams.has_key('type') and not self.contentparams.get('type', 'xml').endswith('xml'):
-            data = data.replace('&lt;', '<')
-            data = data.replace('&gt;', '>')
-            data = data.replace('&amp;', '&')
-            data = data.replace('&quot;', '"')
-            data = data.replace('&apos;', "'")
-        return data
-        
-class _RelativeURIResolver(_BaseHTMLProcessor):
-    relative_uris = [('a', 'href'),
-                     ('applet', 'codebase'),
-                     ('area', 'href'),
-                     ('blockquote', 'cite'),
-                     ('body', 'background'),
-                     ('del', 'cite'),
-                     ('form', 'action'),
-                     ('frame', 'longdesc'),
-                     ('frame', 'src'),
-                     ('iframe', 'longdesc'),
-                     ('iframe', 'src'),
-                     ('head', 'profile'),
-                     ('img', 'longdesc'),
-                     ('img', 'src'),
-                     ('img', 'usemap'),
-                     ('input', 'src'),
-                     ('input', 'usemap'),
-                     ('ins', 'cite'),
-                     ('link', 'href'),
-                     ('object', 'classid'),
-                     ('object', 'codebase'),
-                     ('object', 'data'),
-                     ('object', 'usemap'),
-                     ('q', 'cite'),
-                     ('script', 'src')]
-
-    def __init__(self, baseuri, encoding):
-        _BaseHTMLProcessor.__init__(self, encoding)
-        self.baseuri = baseuri
-
-    def resolveURI(self, uri):
-        return _urljoin(self.baseuri, uri)
-    
-    def unknown_starttag(self, tag, attrs):
-        attrs = self.normalize_attrs(attrs)
-        attrs = [(key, ((tag, key) in self.relative_uris) and self.resolveURI(value) or value) for key, value in attrs]
-        _BaseHTMLProcessor.unknown_starttag(self, tag, attrs)
-        
-def _resolveRelativeURIs(htmlSource, baseURI, encoding):
-    if _debug: sys.stderr.write('entering _resolveRelativeURIs\n')
-    p = _RelativeURIResolver(baseURI, encoding)
-    p.feed(htmlSource)
-    return p.output()
-
-class _HTMLSanitizer(_BaseHTMLProcessor):
-    acceptable_elements = ['a', 'abbr', 'acronym', 'address', 'area', 'b', 'big',
-      'blockquote', 'br', 'button', 'caption', 'center', 'cite', 'code', 'col',
-      'colgroup', 'dd', 'del', 'dfn', 'dir', 'div', 'dl', 'dt', 'em', 'fieldset',
-      'font', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'i', 'img', 'input',
-      'ins', 'kbd', 'label', 'legend', 'li', 'map', 'menu', 'ol', 'optgroup',
-      'option', 'p', 'pre', 'q', 's', 'samp', 'select', 'small', 'span', 'strike',
-      'strong', 'sub', 'sup', 'table', 'tbody', 'td', 'textarea', 'tfoot', 'th',
-      'thead', 'tr', 'tt', 'u', 'ul', 'var']
-
-    acceptable_attributes = ['abbr', 'accept', 'accept-charset', 'accesskey',
-      'action', 'align', 'alt', 'axis', 'border', 'cellpadding', 'cellspacing',
-      'char', 'charoff', 'charset', 'checked', 'cite', 'class', 'clear', 'cols',
-      'colspan', 'color', 'compact', 'coords', 'datetime', 'dir', 'disabled',
-      'enctype', 'for', 'frame', 'headers', 'height', 'href', 'hreflang', 'hspace',
-      'id', 'ismap', 'label', 'lang', 'longdesc', 'maxlength', 'media', 'method',
-      'multiple', 'name', 'nohref', 'noshade', 'nowrap', 'prompt', 'readonly',
-      'rel', 'rev', 'rows', 'rowspan', 'rules', 'scope', 'selected', 'shape', 'size',
-      'span', 'src', 'start', 'summary', 'tabindex', 'target', 'title', 'type',
-      'usemap', 'valign', 'value', 'vspace', 'width']
-
-    unacceptable_elements_with_end_tag = ['script', 'applet']
-
-    def reset(self):
-        _BaseHTMLProcessor.reset(self)
-        self.unacceptablestack = 0
-        
-    def unknown_starttag(self, tag, attrs):
-        if not tag in self.acceptable_elements:
-            if tag in self.unacceptable_elements_with_end_tag:
-                self.unacceptablestack += 1
-            return
-        attrs = self.normalize_attrs(attrs)
-        attrs = [(key, value) for key, value in attrs if key in self.acceptable_attributes]
-        _BaseHTMLProcessor.unknown_starttag(self, tag, attrs)
-        
-    def unknown_endtag(self, tag):
-        if not tag in self.acceptable_elements:
-            if tag in self.unacceptable_elements_with_end_tag:
-                self.unacceptablestack -= 1
-            return
-        _BaseHTMLProcessor.unknown_endtag(self, tag)
-
-    def handle_pi(self, text):
-        pass
-
-    def handle_decl(self, text):
-        pass
-
-    def handle_data(self, text):
-        if not self.unacceptablestack:
-            _BaseHTMLProcessor.handle_data(self, text)
-
-def _sanitizeHTML(htmlSource, encoding):
-    p = _HTMLSanitizer(encoding)
-    p.feed(htmlSource)
-    data = p.output()
-    if TIDY_MARKUP:
-        # loop through list of preferred Tidy interfaces looking for one that's installed,
-        # then set up a common _tidy function to wrap the interface-specific API.
-        _tidy = None
-        for tidy_interface in PREFERRED_TIDY_INTERFACES:
-            try:
-                if tidy_interface == "uTidy":
-                    from tidy import parseString as _utidy
-                    def _tidy(data, **kwargs):
-                        return str(_utidy(data, **kwargs))
-                    break
-                elif tidy_interface == "mxTidy":
-                    from mx.Tidy import Tidy as _mxtidy
-                    def _tidy(data, **kwargs):
-                        nerrors, nwarnings, data, errordata = _mxtidy.tidy(data, **kwargs)
-                        return data
-                    break
-            except:
-                pass
-        if _tidy:
-            utf8 = type(data) == type(u'')
-            if utf8:
-                data = data.encode('utf-8')
-            data = _tidy(data, output_xhtml=1, numeric_entities=1, wrap=0, char_encoding="utf8")
-            if utf8:
-                data = unicode(data, 'utf-8')
-            if data.count('<body'):
-                data = data.split('<body', 1)[1]
-                if data.count('>'):
-                    data = data.split('>', 1)[1]
-            if data.count('</body'):
-                data = data.split('</body', 1)[0]
-    data = data.strip().replace('\r\n', '\n')
-    return data
-
-class _FeedURLHandler(urllib2.HTTPDigestAuthHandler, urllib2.HTTPRedirectHandler, urllib2.HTTPDefaultErrorHandler):
-    def http_error_default(self, req, fp, code, msg, headers):
-        if ((code / 100) == 3) and (code != 304):
-            return self.http_error_302(req, fp, code, msg, headers)
-        infourl = urllib.addinfourl(fp, headers, req.get_full_url())
-        infourl.status = code
-        return infourl
-
-    def http_error_302(self, req, fp, code, msg, headers):
-        if headers.dict.has_key('location'):
-            infourl = urllib2.HTTPRedirectHandler.http_error_302(self, req, fp, code, msg, headers)
-        else:
-            infourl = urllib.addinfourl(fp, headers, req.get_full_url())
-        if not hasattr(infourl, 'status'):
-            infourl.status = code
-        return infourl
-
-    def http_error_301(self, req, fp, code, msg, headers):
-        if headers.dict.has_key('location'):
-            infourl = urllib2.HTTPRedirectHandler.http_error_301(self, req, fp, code, msg, headers)
-        else:
-            infourl = urllib.addinfourl(fp, headers, req.get_full_url())
-        if not hasattr(infourl, 'status'):
-            infourl.status = code
-        return infourl
-
-    http_error_300 = http_error_302
-    http_error_303 = http_error_302
-    http_error_307 = http_error_302
-        
-    def http_error_401(self, req, fp, code, msg, headers):
-        # Check if
-        # - server requires digest auth, AND
-        # - we tried (unsuccessfully) with basic auth, AND
-        # - we're using Python 2.3.3 or later (digest auth is irreparably broken in earlier versions)
-        # If all conditions hold, parse authentication information
-        # out of the Authorization header we sent the first time
-        # (for the username and password) and the WWW-Authenticate
-        # header the server sent back (for the realm) and retry
-        # the request with the appropriate digest auth headers instead.
-        # This evil genius hack has been brought to you by Aaron Swartz.
-        host = urlparse.urlparse(req.get_full_url())[1]
-        try:
-            assert sys.version.split()[0] >= '2.3.3'
-            assert base64 != None
-            user, passw = base64.decodestring(req.headers['Authorization'].split(' ')[1]).split(':')
-            realm = re.findall('realm="([^"]*)"', headers['WWW-Authenticate'])[0]
-            self.add_password(realm, host, user, passw)
-            retry = self.http_error_auth_reqed('www-authenticate', host, req, headers)
-            self.reset_retry_count()
-            return retry
-        except:
-            return self.http_error_default(req, fp, code, msg, headers)
-
-def _open_resource(url_file_stream_or_string, etag, modified, agent, referrer, handlers):
-    """URL, filename, or string --> stream
-
-    This function lets you define parsers that take any input source
-    (URL, pathname to local or network file, or actual data as a string)
-    and deal with it in a uniform manner.  Returned object is guaranteed
-    to have all the basic stdio read methods (read, readline, readlines).
-    Just .close() the object when you're done with it.
-
-    If the etag argument is supplied, it will be used as the value of an
-    If-None-Match request header.
-
-    If the modified argument is supplied, it must be a tuple of 9 integers
-    as returned by gmtime() in the standard Python time module. This MUST
-    be in GMT (Greenwich Mean Time). The formatted date/time will be used
-    as the value of an If-Modified-Since request header.
-
-    If the agent argument is supplied, it will be used as the value of a
-    User-Agent request header.
-
-    If the referrer argument is supplied, it will be used as the value of a
-    Referer[sic] request header.
-
-    If handlers is supplied, it is a list of handlers used to build a
-    urllib2 opener.
-    """
-
-    if hasattr(url_file_stream_or_string, 'read'):
-        return url_file_stream_or_string
-
-    if url_file_stream_or_string == '-':
-        return sys.stdin
-
-    if urlparse.urlparse(url_file_stream_or_string)[0] in ('http', 'https', 'ftp'):
-        if not agent:
-            agent = USER_AGENT
-        # test for inline user:password for basic auth
-        auth = None
-        if base64:
-            urltype, rest = urllib.splittype(url_file_stream_or_string)
-            realhost, rest = urllib.splithost(rest)
-            if realhost:
-                user_passwd, realhost = urllib.splituser(realhost)
-                if user_passwd:
-                    url_file_stream_or_string = '%s://%s%s' % (urltype, realhost, rest)
-                    auth = base64.encodestring(user_passwd).strip()
-        # try to open with urllib2 (to use optional headers)
-        request = urllib2.Request(url_file_stream_or_string)
-        request.add_header('User-Agent', agent)
-        if etag:
-            request.add_header('If-None-Match', etag)
-        if modified:
-            # format into an RFC 1123-compliant timestamp. We can't use
-            # time.strftime() since the %a and %b directives can be affected
-            # by the current locale, but RFC 2616 states that dates must be
-            # in English.
-            short_weekdays = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
-            months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
-            request.add_header('If-Modified-Since', '%s, %02d %s %04d %02d:%02d:%02d GMT' % (short_weekdays[modified[6]], modified[2], months[modified[1] - 1], modified[0], modified[3], modified[4], modified[5]))
-        if referrer:
-            request.add_header('Referer', referrer)
-        if gzip and zlib:
-            request.add_header('Accept-encoding', 'gzip, deflate')
-        elif gzip:
-            request.add_header('Accept-encoding', 'gzip')
-        elif zlib:
-            request.add_header('Accept-encoding', 'deflate')
-        else:
-            request.add_header('Accept-encoding', '')
-        if auth:
-            request.add_header('Authorization', 'Basic %s' % auth)
-        if ACCEPT_HEADER:
-            request.add_header('Accept', ACCEPT_HEADER)
-        request.add_header('A-IM', 'feed') # RFC 3229 support
-        opener = apply(urllib2.build_opener, tuple([_FeedURLHandler()] + handlers))
-        opener.addheaders = [] # RMK - must clear so we only send our custom User-Agent
-        try:
-            return opener.open(request)
-        finally:
-            opener.close() # JohnD
-    
-    # try to open with native open function (if url_file_stream_or_string is a filename)
-    try:
-        return open(url_file_stream_or_string)
-    except:
-        pass
-
-    # treat url_file_stream_or_string as string
-    return _StringIO(str(url_file_stream_or_string))
-
-_date_handlers = []
-def registerDateHandler(func):
-    '''Register a date handler function (takes string, returns 9-tuple date in GMT)'''
-    _date_handlers.insert(0, func)
-    
-# ISO-8601 date parsing routines written by Fazal Majid.
-# The ISO 8601 standard is very convoluted and irregular - a full ISO 8601
-# parser is beyond the scope of feedparser and would be a worthwhile addition
-# to the Python library.
-# A single regular expression cannot parse ISO 8601 date formats into groups
-# as the standard is highly irregular (for instance is 030104 2003-01-04 or
-# 0301-04-01), so we use templates instead.
-# Please note the order in templates is significant because we need a
-# greedy match.
-_iso8601_tmpl = ['YYYY-?MM-?DD', 'YYYY-MM', 'YYYY-?OOO',
-                'YY-?MM-?DD', 'YY-?OOO', 'YYYY', 
-                '-YY-?MM', '-OOO', '-YY',
-                '--MM-?DD', '--MM',
-                '---DD',
-                'CC', '']
-_iso8601_re = [
-    tmpl.replace(
-    'YYYY', r'(?P<year>\d{4})').replace(
-    'YY', r'(?P<year>\d\d)').replace(
-    'MM', r'(?P<month>[01]\d)').replace(
-    'DD', r'(?P<day>[0123]\d)').replace(
-    'OOO', r'(?P<ordinal>[0123]\d\d)').replace(
-    'CC', r'(?P<century>\d\d$)')
-    + r'(T?(?P<hour>\d{2}):(?P<minute>\d{2})'
-    + r'(:(?P<second>\d{2}))?'
-    + r'(?P<tz>[+-](?P<tzhour>\d{2})(:(?P<tzmin>\d{2}))?|Z)?)?'
-    for tmpl in _iso8601_tmpl]
-del tmpl
-_iso8601_matches = [re.compile(regex).match for regex in _iso8601_re]
-del regex
-def _parse_date_iso8601(dateString):
-    '''Parse a variety of ISO-8601-compatible formats like 20040105'''
-    m = None
-    for _iso8601_match in _iso8601_matches:
-        m = _iso8601_match(dateString)
-        if m: break
-    if not m: return
-    if m.span() == (0, 0): return
-    params = m.groupdict()
-    ordinal = params.get('ordinal', 0)
-    if ordinal:
-        ordinal = int(ordinal)
-    else:
-        ordinal = 0
-    year = params.get('year', '--')
-    if not year or year == '--':
-        year = time.gmtime()[0]
-    elif len(year) == 2:
-        # ISO 8601 assumes current century, i.e. 93 -> 2093, NOT 1993
-        year = 100 * int(time.gmtime()[0] / 100) + int(year)
-    else:
-        year = int(year)
-    month = params.get('month', '-')
-    if not month or month == '-':
-        # ordinals are NOT normalized by mktime, we simulate them
-        # by setting month=1, day=ordinal
-        if ordinal:
-            month = 1
-        else:
-            month = time.gmtime()[1]
-    month = int(month)
-    day = params.get('day', 0)
-    if not day:
-        # see above
-        if ordinal:
-            day = ordinal
-        elif params.get('century', 0) or \
-                 params.get('year', 0) or params.get('month', 0):
-            day = 1
-        else:
-            day = time.gmtime()[2]
-    else:
-        day = int(day)
-    # special case of the century - is the first year of the 21st century
-    # 2000 or 2001 ? The debate goes on...
-    if 'century' in params.keys():
-        year = (int(params['century']) - 1) * 100 + 1
-    # in ISO 8601 most fields are optional
-    for field in ['hour', 'minute', 'second', 'tzhour', 'tzmin']:
-        if not params.get(field, None):
-            params[field] = 0
-    hour = int(params.get('hour', 0))
-    minute = int(params.get('minute', 0))
-    second = int(params.get('second', 0))
-    # weekday is normalized by mktime(), we can ignore it
-    weekday = 0
-    # daylight savings is complex, but not needed for feedparser's purposes
-    # as time zones, if specified, include mention of whether it is active
-    # (e.g. PST vs. PDT, CET). Using -1 is implementation-dependent and
-    # and most implementations have DST bugs
-    daylight_savings_flag = 0
-    tm = [year, month, day, hour, minute, second, weekday,
-          ordinal, daylight_savings_flag]
-    # ISO 8601 time zone adjustments
-    tz = params.get('tz')
-    if tz and tz != 'Z':
-        if tz[0] == '-':
-            tm[3] += int(params.get('tzhour', 0))
-            tm[4] += int(params.get('tzmin', 0))
-        elif tz[0] == '+':
-            tm[3] -= int(params.get('tzhour', 0))
-            tm[4] -= int(params.get('tzmin', 0))
-        else:
-            return None
-    # Python's time.mktime() is a wrapper around the ANSI C mktime(3c)
-    # which is guaranteed to normalize d/m/y/h/m/s.
-    # Many implementations have bugs, but we'll pretend they don't.
-    return time.localtime(time.mktime(tm))
-registerDateHandler(_parse_date_iso8601)
-    
-# 8-bit date handling routines written by ytrewq1.
-_korean_year  = u'\ub144' # b3e2 in euc-kr
-_korean_month = u'\uc6d4' # bff9 in euc-kr
-_korean_day   = u'\uc77c' # c0cf in euc-kr
-_korean_am    = u'\uc624\uc804' # bfc0 c0fc in euc-kr
-_korean_pm    = u'\uc624\ud6c4' # bfc0 c8c4 in euc-kr
-
-_korean_onblog_date_re = \
-    re.compile('(\d{4})%s\s+(\d{2})%s\s+(\d{2})%s\s+(\d{2}):(\d{2}):(\d{2})' % \
-               (_korean_year, _korean_month, _korean_day))
-_korean_nate_date_re = \
-    re.compile(u'(\d{4})-(\d{2})-(\d{2})\s+(%s|%s)\s+(\d{,2}):(\d{,2}):(\d{,2})' % \
-               (_korean_am, _korean_pm))
-def _parse_date_onblog(dateString):
-    '''Parse a string according to the OnBlog 8-bit date format'''
-    m = _korean_onblog_date_re.match(dateString)
-    if not m: return
-    w3dtfdate = '%(year)s-%(month)s-%(day)sT%(hour)s:%(minute)s:%(second)s%(zonediff)s' % \
-                {'year': m.group(1), 'month': m.group(2), 'day': m.group(3),\
-                 'hour': m.group(4), 'minute': m.group(5), 'second': m.group(6),\
-                 'zonediff': '+09:00'}
-    if _debug: sys.stderr.write('OnBlog date parsed as: %s\n' % w3dtfdate)
-    return _parse_date_w3dtf(w3dtfdate)
-registerDateHandler(_parse_date_onblog)
-
-def _parse_date_nate(dateString):
-    '''Parse a string according to the Nate 8-bit date format'''
-    m = _korean_nate_date_re.match(dateString)
-    if not m: return
-    hour = int(m.group(5))
-    ampm = m.group(4)
-    if (ampm == _korean_pm):
-        hour += 12
-    hour = str(hour)
-    if len(hour) == 1:
-        hour = '0' + hour
-    w3dtfdate = '%(year)s-%(month)s-%(day)sT%(hour)s:%(minute)s:%(second)s%(zonediff)s' % \
-                {'year': m.group(1), 'month': m.group(2), 'day': m.group(3),\
-                 'hour': hour, 'minute': m.group(6), 'second': m.group(7),\
-                 'zonediff': '+09:00'}
-    if _debug: sys.stderr.write('Nate date parsed as: %s\n' % w3dtfdate)
-    return _parse_date_w3dtf(w3dtfdate)
-registerDateHandler(_parse_date_nate)
-
-_mssql_date_re = \
-    re.compile('(\d{4})-(\d{2})-(\d{2})\s+(\d{2}):(\d{2}):(\d{2})(\.\d+)?')
-def _parse_date_mssql(dateString):
-    '''Parse a string according to the MS SQL date format'''
-    m = _mssql_date_re.match(dateString)
-    if not m: return
-    w3dtfdate = '%(year)s-%(month)s-%(day)sT%(hour)s:%(minute)s:%(second)s%(zonediff)s' % \
-                {'year': m.group(1), 'month': m.group(2), 'day': m.group(3),\
-                 'hour': m.group(4), 'minute': m.group(5), 'second': m.group(6),\
-                 'zonediff': '+09:00'}
-    if _debug: sys.stderr.write('MS SQL date parsed as: %s\n' % w3dtfdate)
-    return _parse_date_w3dtf(w3dtfdate)
-registerDateHandler(_parse_date_mssql)
-
-# Unicode strings for Greek date strings
-_greek_months = \
-  { \
-   u'\u0399\u03b1\u03bd': u'Jan',       # c9e1ed in iso-8859-7
-   u'\u03a6\u03b5\u03b2': u'Feb',       # d6e5e2 in iso-8859-7
-   u'\u039c\u03ac\u03ce': u'Mar',       # ccdcfe in iso-8859-7
-   u'\u039c\u03b1\u03ce': u'Mar',       # cce1fe in iso-8859-7
-   u'\u0391\u03c0\u03c1': u'Apr',       # c1f0f1 in iso-8859-7
-   u'\u039c\u03ac\u03b9': u'May',       # ccdce9 in iso-8859-7
-   u'\u039c\u03b1\u03ca': u'May',       # cce1fa in iso-8859-7
-   u'\u039c\u03b1\u03b9': u'May',       # cce1e9 in iso-8859-7
-   u'\u0399\u03bf\u03cd\u03bd': u'Jun', # c9effded in iso-8859-7
-   u'\u0399\u03bf\u03bd': u'Jun',       # c9efed in iso-8859-7
-   u'\u0399\u03bf\u03cd\u03bb': u'Jul', # c9effdeb in iso-8859-7
-   u'\u0399\u03bf\u03bb': u'Jul',       # c9f9eb in iso-8859-7
-   u'\u0391\u03cd\u03b3': u'Aug',       # c1fde3 in iso-8859-7
-   u'\u0391\u03c5\u03b3': u'Aug',       # c1f5e3 in iso-8859-7
-   u'\u03a3\u03b5\u03c0': u'Sep',       # d3e5f0 in iso-8859-7
-   u'\u039f\u03ba\u03c4': u'Oct',       # cfeaf4 in iso-8859-7
-   u'\u039d\u03bf\u03ad': u'Nov',       # cdefdd in iso-8859-7
-   u'\u039d\u03bf\u03b5': u'Nov',       # cdefe5 in iso-8859-7
-   u'\u0394\u03b5\u03ba': u'Dec',       # c4e5ea in iso-8859-7
-  }
-
-_greek_wdays = \
-  { \
-   u'\u039a\u03c5\u03c1': u'Sun', # caf5f1 in iso-8859-7
-   u'\u0394\u03b5\u03c5': u'Mon', # c4e5f5 in iso-8859-7
-   u'\u03a4\u03c1\u03b9': u'Tue', # d4f1e9 in iso-8859-7
-   u'\u03a4\u03b5\u03c4': u'Wed', # d4e5f4 in iso-8859-7
-   u'\u03a0\u03b5\u03bc': u'Thu', # d0e5ec in iso-8859-7
-   u'\u03a0\u03b1\u03c1': u'Fri', # d0e1f1 in iso-8859-7
-   u'\u03a3\u03b1\u03b2': u'Sat', # d3e1e2 in iso-8859-7   
-  }
-
-_greek_date_format_re = \
-    re.compile(u'([^,]+),\s+(\d{2})\s+([^\s]+)\s+(\d{4})\s+(\d{2}):(\d{2}):(\d{2})\s+([^\s]+)')
-
-def _parse_date_greek(dateString):
-    '''Parse a string according to a Greek 8-bit date format.'''
-    m = _greek_date_format_re.match(dateString)
-    if not m: return
-    try:
-        wday = _greek_wdays[m.group(1)]
-        month = _greek_months[m.group(3)]
-    except:
-        return
-    rfc822date = '%(wday)s, %(day)s %(month)s %(year)s %(hour)s:%(minute)s:%(second)s %(zonediff)s' % \
-                 {'wday': wday, 'day': m.group(2), 'month': month, 'year': m.group(4),\
-                  'hour': m.group(5), 'minute': m.group(6), 'second': m.group(7),\
-                  'zonediff': m.group(8)}
-    if _debug: sys.stderr.write('Greek date parsed as: %s\n' % rfc822date)
-    return _parse_date_rfc822(rfc822date)
-registerDateHandler(_parse_date_greek)
-
-# Unicode strings for Hungarian date strings
-_hungarian_months = \
-  { \
-    u'janu\u00e1r':   u'01',  # e1 in iso-8859-2
-    u'febru\u00e1ri': u'02',  # e1 in iso-8859-2
-    u'm\u00e1rcius':  u'03',  # e1 in iso-8859-2
-    u'\u00e1prilis':  u'04',  # e1 in iso-8859-2
-    u'm\u00e1ujus':   u'05',  # e1 in iso-8859-2
-    u'j\u00fanius':   u'06',  # fa in iso-8859-2
-    u'j\u00falius':   u'07',  # fa in iso-8859-2
-    u'augusztus':     u'08',
-    u'szeptember':    u'09',
-    u'okt\u00f3ber':  u'10',  # f3 in iso-8859-2
-    u'november':      u'11',
-    u'december':      u'12',
-  }
-
-_hungarian_date_format_re = \
-  re.compile(u'(\d{4})-([^-]+)-(\d{,2})T(\d{,2}):(\d{2})((\+|-)(\d{,2}:\d{2}))')
-
-def _parse_date_hungarian(dateString):
-    '''Parse a string according to a Hungarian 8-bit date format.'''
-    m = _hungarian_date_format_re.match(dateString)
-    if not m: return
-    try:
-        month = _hungarian_months[m.group(2)]
-        day = m.group(3)
-        if len(day) == 1:
-            day = '0' + day
-        hour = m.group(4)
-        if len(hour) == 1:
-            hour = '0' + hour
-    except:
-        return
-    w3dtfdate = '%(year)s-%(month)s-%(day)sT%(hour)s:%(minute)s%(zonediff)s' % \
-                {'year': m.group(1), 'month': month, 'day': day,\
-                 'hour': hour, 'minute': m.group(5),\
-                 'zonediff': m.group(6)}
-    if _debug: sys.stderr.write('Hungarian date parsed as: %s\n' % w3dtfdate)
-    return _parse_date_w3dtf(w3dtfdate)
-registerDateHandler(_parse_date_hungarian)
-
-# W3DTF-style date parsing adapted from PyXML xml.utils.iso8601, written by
-# Drake and licensed under the Python license.  Removed all range checking
-# for month, day, hour, minute, and second, since mktime will normalize
-# these later
-def _parse_date_w3dtf(dateString):
-    def __extract_date(m):
-        year = int(m.group('year'))
-        if year < 100:
-            year = 100 * int(time.gmtime()[0] / 100) + int(year)
-        if year < 1000:
-            return 0, 0, 0
-        julian = m.group('julian')
-        if julian:
-            julian = int(julian)
-            month = julian / 30 + 1
-            day = julian % 30 + 1
-            jday = None
-            while jday != julian:
-                t = time.mktime((year, month, day, 0, 0, 0, 0, 0, 0))
-                jday = time.gmtime(t)[-2]
-                diff = abs(jday - julian)
-                if jday > julian:
-                    if diff < day:
-                        day = day - diff
-                    else:
-                        month = month - 1
-                        day = 31
-                elif jday < julian:
-                    if day + diff < 28:
-                       day = day + diff
-                    else:
-                        month = month + 1
-            return year, month, day
-        month = m.group('month')
-        day = 1
-        if month is None:
-            month = 1
-        else:
-            month = int(month)
-            day = m.group('day')
-            if day:
-                day = int(day)
-            else:
-                day = 1
-        return year, month, day
-
-    def __extract_time(m):
-        if not m:
-            return 0, 0, 0
-        hours = m.group('hours')
-        if not hours:
-            return 0, 0, 0
-        hours = int(hours)
-        minutes = int(m.group('minutes'))
-        seconds = m.group('seconds')
-        if seconds:
-            seconds = int(seconds)
-        else:
-            seconds = 0
-        return hours, minutes, seconds
-
-    def __extract_tzd(m):
-        '''Return the Time Zone Designator as an offset in seconds from UTC.'''
-        if not m:
-            return 0
-        tzd = m.group('tzd')
-        if not tzd:
-            return 0
-        if tzd == 'Z':
-            return 0
-        hours = int(m.group('tzdhours'))
-        minutes = m.group('tzdminutes')
-        if minutes:
-            minutes = int(minutes)
-        else:
-            minutes = 0
-        offset = (hours*60 + minutes) * 60
-        if tzd[0] == '+':
-            return -offset
-        return offset
-
-    __date_re = ('(?P<year>\d\d\d\d)'
-                 '(?:(?P<dsep>-|)'
-                 '(?:(?P<julian>\d\d\d)'
-                 '|(?P<month>\d\d)(?:(?P=dsep)(?P<day>\d\d))?))?')
-    __tzd_re = '(?P<tzd>[-+](?P<tzdhours>\d\d)(?::?(?P<tzdminutes>\d\d))|Z)'
-    __tzd_rx = re.compile(__tzd_re)
-    __time_re = ('(?P<hours>\d\d)(?P<tsep>:|)(?P<minutes>\d\d)'
-                 '(?:(?P=tsep)(?P<seconds>\d\d(?:[.,]\d+)?))?'
-                 + __tzd_re)
-    __datetime_re = '%s(?:T%s)?' % (__date_re, __time_re)
-    __datetime_rx = re.compile(__datetime_re)
-    m = __datetime_rx.match(dateString)
-    if (m is None) or (m.group() != dateString): return
-    gmt = __extract_date(m) + __extract_time(m) + (0, 0, 0)
-    if gmt[0] == 0: return
-    return time.gmtime(time.mktime(gmt) + __extract_tzd(m) - time.timezone)
-registerDateHandler(_parse_date_w3dtf)
-
-def _parse_date_rfc822(dateString):
-    '''Parse an RFC822, RFC1123, RFC2822, or asctime-style date'''
-    data = dateString.split()
-    if data[0][-1] in (',', '.') or data[0].lower() in rfc822._daynames:
-        del data[0]
-    if len(data) == 4:
-        s = data[3]
-        i = s.find('+')
-        if i > 0:
-            data[3:] = [s[:i], s[i+1:]]
-        else:
-            data.append('')
-        dateString = " ".join(data)
-    if len(data) < 5:
-        dateString += ' 00:00:00 GMT'
-    tm = rfc822.parsedate_tz(dateString)
-    if tm:
-        return time.gmtime(rfc822.mktime_tz(tm))
-# rfc822.py defines several time zones, but we define some extra ones.
-# 'ET' is equivalent to 'EST', etc.
-_additional_timezones = {'AT': -400, 'ET': -500, 'CT': -600, 'MT': -700, 'PT': -800}
-rfc822._timezones.update(_additional_timezones)
-registerDateHandler(_parse_date_rfc822)    
-
-def _parse_date(dateString):
-    '''Parses a variety of date formats into a 9-tuple in GMT'''
-    for handler in _date_handlers:
-        try:
-            date9tuple = handler(dateString)
-            if not date9tuple: continue
-            if len(date9tuple) != 9:
-                if _debug: sys.stderr.write('date handler function must return 9-tuple\n')
-                raise ValueError
-            map(int, date9tuple)
-            return date9tuple
-        except Exception, e:
-            if _debug: sys.stderr.write('%s raised %s\n' % (handler.__name__, repr(e)))
-            pass
-    return None
-
-def _getCharacterEncoding(http_headers, xml_data):
-    '''Get the character encoding of the XML document
-
-    http_headers is a dictionary
-    xml_data is a raw string (not Unicode)
-    
-    This is so much trickier than it sounds, it's not even funny.
-    According to RFC 3023 ('XML Media Types'), if the HTTP Content-Type
-    is application/xml, application/*+xml,
-    application/xml-external-parsed-entity, or application/xml-dtd,
-    the encoding given in the charset parameter of the HTTP Content-Type
-    takes precedence over the encoding given in the XML prefix within the
-    document, and defaults to 'utf-8' if neither are specified.  But, if
-    the HTTP Content-Type is text/xml, text/*+xml, or
-    text/xml-external-parsed-entity, the encoding given in the XML prefix
-    within the document is ALWAYS IGNORED and only the encoding given in
-    the charset parameter of the HTTP Content-Type header should be
-    respected, and it defaults to 'us-ascii' if not specified.
-
-    Furthermore, discussion on the atom-syntax mailing list with the
-    author of RFC 3023 leads me to the conclusion that any document
-    served with a Content-Type of text/* and no charset parameter
-    must be treated as us-ascii.  (We now do this.)  And also that it
-    must always be flagged as non-well-formed.  (We now do this too.)
-    
-    If Content-Type is unspecified (input was local file or non-HTTP source)
-    or unrecognized (server just got it totally wrong), then go by the
-    encoding given in the XML prefix of the document and default to
-    'iso-8859-1' as per the HTTP specification (RFC 2616).
-    
-    Then, assuming we didn't find a character encoding in the HTTP headers
-    (and the HTTP Content-type allowed us to look in the body), we need
-    to sniff the first few bytes of the XML data and try to determine
-    whether the encoding is ASCII-compatible.  Section F of the XML
-    specification shows the way here:
-    http://www.w3.org/TR/REC-xml/#sec-guessing-no-ext-info
-
-    If the sniffed encoding is not ASCII-compatible, we need to make it
-    ASCII compatible so that we can sniff further into the XML declaration
-    to find the encoding attribute, which will tell us the true encoding.
-
-    Of course, none of this guarantees that we will be able to parse the
-    feed in the declared character encoding (assuming it was declared
-    correctly, which many are not).  CJKCodecs and iconv_codec help a lot;
-    you should definitely install them if you can.
-    http://cjkpython.i18n.org/
-    '''
-
-    def _parseHTTPContentType(content_type):
-        '''takes HTTP Content-Type header and returns (content type, charset)
-
-        If no charset is specified, returns (content type, '')
-        If no content type is specified, returns ('', '')
-        Both return parameters are guaranteed to be lowercase strings
-        '''
-        content_type = content_type or ''
-        content_type, params = cgi.parse_header(content_type)
-        return content_type, params.get('charset', '').replace("'", '')
-
-    sniffed_xml_encoding = ''
-    xml_encoding = ''
-    true_encoding = ''
-    http_content_type, http_encoding = _parseHTTPContentType(http_headers.get('content-type'))
-    # Must sniff for non-ASCII-compatible character encodings before
-    # searching for XML declaration.  This heuristic is defined in
-    # section F of the XML specification:
-    # http://www.w3.org/TR/REC-xml/#sec-guessing-no-ext-info
-    try:
-        if xml_data[:4] == '\x4c\x6f\xa7\x94':
-            # EBCDIC
-            xml_data = _ebcdic_to_ascii(xml_data)
-        elif xml_data[:4] == '\x00\x3c\x00\x3f':
-            # UTF-16BE
-            sniffed_xml_encoding = 'utf-16be'
-            xml_data = unicode(xml_data, 'utf-16be').encode('utf-8')
-        elif (len(xml_data) >= 4) and (xml_data[:2] == '\xfe\xff') and (xml_data[2:4] != '\x00\x00'):
-            # UTF-16BE with BOM
-            sniffed_xml_encoding = 'utf-16be'
-            xml_data = unicode(xml_data[2:], 'utf-16be').encode('utf-8')
-        elif xml_data[:4] == '\x3c\x00\x3f\x00':
-            # UTF-16LE
-            sniffed_xml_encoding = 'utf-16le'
-            xml_data = unicode(xml_data, 'utf-16le').encode('utf-8')
-        elif (len(xml_data) >= 4) and (xml_data[:2] == '\xff\xfe') and (xml_data[2:4] != '\x00\x00'):
-            # UTF-16LE with BOM
-            sniffed_xml_encoding = 'utf-16le'
-            xml_data = unicode(xml_data[2:], 'utf-16le').encode('utf-8')
-        elif xml_data[:4] == '\x00\x00\x00\x3c':
-            # UTF-32BE
-            sniffed_xml_encoding = 'utf-32be'
-            xml_data = unicode(xml_data, 'utf-32be').encode('utf-8')
-        elif xml_data[:4] == '\x3c\x00\x00\x00':
-            # UTF-32LE
-            sniffed_xml_encoding = 'utf-32le'
-            xml_data = unicode(xml_data, 'utf-32le').encode('utf-8')
-        elif xml_data[:4] == '\x00\x00\xfe\xff':
-            # UTF-32BE with BOM
-            sniffed_xml_encoding = 'utf-32be'
-            xml_data = unicode(xml_data[4:], 'utf-32be').encode('utf-8')
-        elif xml_data[:4] == '\xff\xfe\x00\x00':
-            # UTF-32LE with BOM
-            sniffed_xml_encoding = 'utf-32le'
-            xml_data = unicode(xml_data[4:], 'utf-32le').encode('utf-8')
-        elif xml_data[:3] == '\xef\xbb\xbf':
-            # UTF-8 with BOM
-            sniffed_xml_encoding = 'utf-8'
-            xml_data = unicode(xml_data[3:], 'utf-8').encode('utf-8')
-        else:
-            # ASCII-compatible
-            pass
-        xml_encoding_match = re.compile('^<\?.*encoding=[\'"](.*?)[\'"].*\?>').match(xml_data)
-    except:
-        xml_encoding_match = None
-    if xml_encoding_match:
-        xml_encoding = xml_encoding_match.groups()[0].lower()
-        if sniffed_xml_encoding and (xml_encoding in ('iso-10646-ucs-2', 'ucs-2', 'csunicode', 'iso-10646-ucs-4', 'ucs-4', 'csucs4', 'utf-16', 'utf-32', 'utf_16', 'utf_32', 'utf16', 'u16')):
-            xml_encoding = sniffed_xml_encoding
-    acceptable_content_type = 0
-    application_content_types = ('application/xml', 'application/xml-dtd', 'application/xml-external-parsed-entity')
-    text_content_types = ('text/xml', 'text/xml-external-parsed-entity')
-    if (http_content_type in application_content_types) or \
-       (http_content_type.startswith('application/') and http_content_type.endswith('+xml')):
-        acceptable_content_type = 1
-        true_encoding = http_encoding or xml_encoding or 'utf-8'
-    elif (http_content_type in text_content_types) or \
-         (http_content_type.startswith('text/')) and http_content_type.endswith('+xml'):
-        acceptable_content_type = 1
-        true_encoding = http_encoding or 'us-ascii'
-    elif http_content_type.startswith('text/'):
-        true_encoding = http_encoding or 'us-ascii'
-    elif http_headers and (not http_headers.has_key('content-type')):
-        true_encoding = xml_encoding or 'iso-8859-1'
-    else:
-        true_encoding = xml_encoding or 'utf-8'
-    return true_encoding, http_encoding, xml_encoding, sniffed_xml_encoding, acceptable_content_type
-    
-def _toUTF8(data, encoding):
-    '''Changes an XML data stream on the fly to specify a new encoding
-
-    data is a raw sequence of bytes (not Unicode) that is presumed to be in %encoding already
-    encoding is a string recognized by encodings.aliases
-    '''
-    if _debug: sys.stderr.write('entering _toUTF8, trying encoding %s\n' % encoding)
-    # strip Byte Order Mark (if present)
-    if (len(data) >= 4) and (data[:2] == '\xfe\xff') and (data[2:4] != '\x00\x00'):
-        if _debug:
-            sys.stderr.write('stripping BOM\n')
-            if encoding != 'utf-16be':
-                sys.stderr.write('trying utf-16be instead\n')
-        encoding = 'utf-16be'
-        data = data[2:]
-    elif (len(data) >= 4) and (data[:2] == '\xff\xfe') and (data[2:4] != '\x00\x00'):
-        if _debug:
-            sys.stderr.write('stripping BOM\n')
-            if encoding != 'utf-16le':
-                sys.stderr.write('trying utf-16le instead\n')
-        encoding = 'utf-16le'
-        data = data[2:]
-    elif data[:3] == '\xef\xbb\xbf':
-        if _debug:
-            sys.stderr.write('stripping BOM\n')
-            if encoding != 'utf-8':
-                sys.stderr.write('trying utf-8 instead\n')
-        encoding = 'utf-8'
-        data = data[3:]
-    elif data[:4] == '\x00\x00\xfe\xff':
-        if _debug:
-            sys.stderr.write('stripping BOM\n')
-            if encoding != 'utf-32be':
-                sys.stderr.write('trying utf-32be instead\n')
-        encoding = 'utf-32be'
-        data = data[4:]
-    elif data[:4] == '\xff\xfe\x00\x00':
-        if _debug:
-            sys.stderr.write('stripping BOM\n')
-            if encoding != 'utf-32le':
-                sys.stderr.write('trying utf-32le instead\n')
-        encoding = 'utf-32le'
-        data = data[4:]
-    newdata = unicode(data, encoding)
-    if _debug: sys.stderr.write('successfully converted %s data to unicode\n' % encoding)
-    declmatch = re.compile('^<\?xml[^>]*?>')
-    newdecl = '''<?xml version='1.0' encoding='utf-8'?>'''
-    if declmatch.search(newdata):
-        newdata = declmatch.sub(newdecl, newdata)
-    else:
-        newdata = newdecl + u'\n' + newdata
-    return newdata.encode('utf-8')
-
-def _stripDoctype(data):
-    '''Strips DOCTYPE from XML document, returns (rss_version, stripped_data)
-
-    rss_version may be 'rss091n' or None
-    stripped_data is the same XML document, minus the DOCTYPE
-    '''
-    entity_pattern = re.compile(r'<!ENTITY([^>]*?)>', re.MULTILINE)
-    data = entity_pattern.sub('', data)
-    doctype_pattern = re.compile(r'<!DOCTYPE([^>]*?)>', re.MULTILINE)
-    doctype_results = doctype_pattern.findall(data)
-    doctype = doctype_results and doctype_results[0] or ''
-    if doctype.lower().count('netscape'):
-        version = 'rss091n'
-    else:
-        version = None
-    data = doctype_pattern.sub('', data)
-    return version, data
-    
-def parse(url_file_stream_or_string, etag=None, modified=None, agent=None, referrer=None, handlers=[]):
-    '''Parse a feed from a URL, file, stream, or string'''
-    result = FeedParserDict()
-    result['feed'] = FeedParserDict()
-    result['entries'] = []
-    if _XML_AVAILABLE:
-        result['bozo'] = 0
-    if type(handlers) == types.InstanceType:
-        handlers = [handlers]
-    try:
-        f = _open_resource(url_file_stream_or_string, etag, modified, agent, referrer, handlers)
-        data = f.read()
-    except Exception, e:
-        result['bozo'] = 1
-        result['bozo_exception'] = e
-        data = ''
-        f = None
-
-    # if feed is gzip-compressed, decompress it
-    if f and data and hasattr(f, 'headers'):
-        if gzip and f.headers.get('content-encoding', '') == 'gzip':
-            try:
-                data = gzip.GzipFile(fileobj=_StringIO(data)).read()
-            except Exception, e:
-                # Some feeds claim to be gzipped but they're not, so
-                # we get garbage.  Ideally, we should re-request the
-                # feed without the 'Accept-encoding: gzip' header,
-                # but we don't.
-                result['bozo'] = 1
-                result['bozo_exception'] = e
-                data = ''
-        elif zlib and f.headers.get('content-encoding', '') == 'deflate':
-            try:
-                data = zlib.decompress(data, -zlib.MAX_WBITS)
-            except Exception, e:
-                result['bozo'] = 1
-                result['bozo_exception'] = e
-                data = ''
-
-    # save HTTP headers
-    if hasattr(f, 'info'):
-        info = f.info()
-        result['etag'] = info.getheader('ETag')
-        last_modified = info.getheader('Last-Modified')
-        if last_modified:
-            result['modified'] = _parse_date(last_modified)
-    if hasattr(f, 'url'):
-        result['href'] = f.url
-        result['status'] = 200
-    if hasattr(f, 'status'):
-        result['status'] = f.status
-    if hasattr(f, 'headers'):
-        result['headers'] = f.headers.dict
-    if hasattr(f, 'close'):
-        f.close()
-
-    # there are four encodings to keep track of:
-    # - http_encoding is the encoding declared in the Content-Type HTTP header
-    # - xml_encoding is the encoding declared in the <?xml declaration
-    # - sniffed_encoding is the encoding sniffed from the first 4 bytes of the XML data
-    # - result['encoding'] is the actual encoding, as per RFC 3023 and a variety of other conflicting specifications
-    http_headers = result.get('headers', {})
-    result['encoding'], http_encoding, xml_encoding, sniffed_xml_encoding, acceptable_content_type = \
-        _getCharacterEncoding(http_headers, data)
-    if http_headers and (not acceptable_content_type):
-        if http_headers.has_key('content-type'):
-            bozo_message = '%s is not an XML media type' % http_headers['content-type']
-        else:
-            bozo_message = 'no Content-type specified'
-        result['bozo'] = 1
-        result['bozo_exception'] = NonXMLContentType(bozo_message)
-        
-    result['version'], data = _stripDoctype(data)
-
-    baseuri = http_headers.get('content-location', result.get('href'))
-    baselang = http_headers.get('content-language', None)
-
-    # if server sent 304, we're done
-    if result.get('status', 0) == 304:
-        result['version'] = ''
-        result['debug_message'] = 'The feed has not changed since you last checked, ' + \
-            'so the server sent no data.  This is a feature, not a bug!'
-        return result
-
-    # if there was a problem downloading, we're done
-    if not data:
-        return result
-
-    # determine character encoding
-    use_strict_parser = 0
-    known_encoding = 0
-    tried_encodings = []
-    # try: HTTP encoding, declared XML encoding, encoding sniffed from BOM
-    for proposed_encoding in (result['encoding'], xml_encoding, sniffed_xml_encoding):
-        if not proposed_encoding: continue
-        if proposed_encoding in tried_encodings: continue
-        tried_encodings.append(proposed_encoding)
-        try:
-            data = _toUTF8(data, proposed_encoding)
-            known_encoding = use_strict_parser = 1
-            break
-        except:
-            pass
-    # if no luck and we have auto-detection library, try that
-    if (not known_encoding) and chardet:
-        try:
-            proposed_encoding = chardet.detect(data)['encoding']
-            if proposed_encoding and (proposed_encoding not in tried_encodings):
-                tried_encodings.append(proposed_encoding)
-                data = _toUTF8(data, proposed_encoding)
-                known_encoding = use_strict_parser = 1
-        except:
-            pass
-    # if still no luck and we haven't tried utf-8 yet, try that
-    if (not known_encoding) and ('utf-8' not in tried_encodings):
-        try:
-            proposed_encoding = 'utf-8'
-            tried_encodings.append(proposed_encoding)
-            data = _toUTF8(data, proposed_encoding)
-            known_encoding = use_strict_parser = 1
-        except:
-            pass
-    # if still no luck and we haven't tried windows-1252 yet, try that
-    if (not known_encoding) and ('windows-1252' not in tried_encodings):
-        try:
-            proposed_encoding = 'windows-1252'
-            tried_encodings.append(proposed_encoding)
-            data = _toUTF8(data, proposed_encoding)
-            known_encoding = use_strict_parser = 1
-        except:
-            pass
-    # if still no luck, give up
-    if not known_encoding:
-        result['bozo'] = 1
-        result['bozo_exception'] = CharacterEncodingUnknown( \
-            'document encoding unknown, I tried ' + \
-            '%s, %s, utf-8, and windows-1252 but nothing worked' % \
-            (result['encoding'], xml_encoding))
-        result['encoding'] = ''
-    elif proposed_encoding != result['encoding']:
-        result['bozo'] = 1
-        result['bozo_exception'] = CharacterEncodingOverride( \
-            'documented declared as %s, but parsed as %s' % \
-            (result['encoding'], proposed_encoding))
-        result['encoding'] = proposed_encoding
-
-    if not _XML_AVAILABLE:
-        use_strict_parser = 0
-    if use_strict_parser:
-        # initialize the SAX parser
-        feedparser = _StrictFeedParser(baseuri, baselang, 'utf-8')
-        saxparser = xml.sax.make_parser(PREFERRED_XML_PARSERS)
-        saxparser.setFeature(xml.sax.handler.feature_namespaces, 1)
-        saxparser.setContentHandler(feedparser)
-        saxparser.setErrorHandler(feedparser)
-        source = xml.sax.xmlreader.InputSource()
-        source.setByteStream(_StringIO(data))
-        if hasattr(saxparser, '_ns_stack'):
-            # work around bug in built-in SAX parser (doesn't recognize xml: namespace)
-            # PyXML doesn't have this problem, and it doesn't have _ns_stack either
-            saxparser._ns_stack.append({'http://www.w3.org/XML/1998/namespace':'xml'})
-        try:
-            saxparser.parse(source)
-        except Exception, e:
-            if _debug:
-                import traceback
-                traceback.print_stack()
-                traceback.print_exc()
-                sys.stderr.write('xml parsing failed\n')
-            result['bozo'] = 1
-            result['bozo_exception'] = feedparser.exc or e
-            use_strict_parser = 0
-    if not use_strict_parser:
-        feedparser = _LooseFeedParser(baseuri, baselang, known_encoding and 'utf-8' or '')
-        feedparser.feed(data)
-    result['feed'] = feedparser.feeddata
-    result['entries'] = feedparser.entries
-    result['version'] = result['version'] or feedparser.version
-    result['namespaces'] = feedparser.namespacesInUse
-    return result
-
-if __name__ == '__main__':
-    if not sys.argv[1:]:
-        print __doc__
-        sys.exit(0)
-    else:
-        urls = sys.argv[1:]
-    zopeCompatibilityHack()
-    from pprint import pprint
-    for url in urls:
-        print url
-        print
-        result = parse(url)
-        pprint(result)
-        print
-
-#REVISION HISTORY
-#1.0 - 9/27/2002 - MAP - fixed namespace processing on prefixed RSS 2.0 elements,
-#  added Simon Fell's test suite
-#1.1 - 9/29/2002 - MAP - fixed infinite loop on incomplete CDATA sections
-#2.0 - 10/19/2002
-#  JD - use inchannel to watch out for image and textinput elements which can
-#  also contain title, link, and description elements
-#  JD - check for isPermaLink='false' attribute on guid elements
-#  JD - replaced openAnything with open_resource supporting ETag and
-#  If-Modified-Since request headers
-#  JD - parse now accepts etag, modified, agent, and referrer optional
-#  arguments
-#  JD - modified parse to return a dictionary instead of a tuple so that any
-#  etag or modified information can be returned and cached by the caller
-#2.0.1 - 10/21/2002 - MAP - changed parse() so that if we don't get anything
-#  because of etag/modified, return the old etag/modified to the caller to
-#  indicate why nothing is being returned
-#2.0.2 - 10/21/2002 - JB - added the inchannel to the if statement, otherwise its
-#  useless.  Fixes the problem JD was addressing by adding it.
-#2.1 - 11/14/2002 - MAP - added gzip support
-#2.2 - 1/27/2003 - MAP - added attribute support, admin:generatorAgent.
-#  start_admingeneratoragent is an example of how to handle elements with
-#  only attributes, no content.
-#2.3 - 6/11/2003 - MAP - added USER_AGENT for default (if caller doesn't specify);
-#  also, make sure we send the User-Agent even if urllib2 isn't available.
-#  Match any variation of backend.userland.com/rss namespace.
-#2.3.1 - 6/12/2003 - MAP - if item has both link and guid, return both as-is.
-#2.4 - 7/9/2003 - MAP - added preliminary Pie/Atom/Echo support based on Sam Ruby's
-#  snapshot of July 1 <http://www.intertwingly.net/blog/1506.html>; changed
-#  project name
-#2.5 - 7/25/2003 - MAP - changed to Python license (all contributors agree);
-#  removed unnecessary urllib code -- urllib2 should always be available anyway;
-#  return actual url, status, and full HTTP headers (as result['url'],
-#  result['status'], and result['headers']) if parsing a remote feed over HTTP --
-#  this should pass all the HTTP tests at <http://diveintomark.org/tests/client/http/>;
-#  added the latest namespace-of-the-week for RSS 2.0
-#2.5.1 - 7/26/2003 - RMK - clear opener.addheaders so we only send our custom
-#  User-Agent (otherwise urllib2 sends two, which confuses some servers)
-#2.5.2 - 7/28/2003 - MAP - entity-decode inline xml properly; added support for
-#  inline <xhtml:body> and <xhtml:div> as used in some RSS 2.0 feeds
-#2.5.3 - 8/6/2003 - TvdV - patch to track whether we're inside an image or
-#  textInput, and also to return the character encoding (if specified)
-#2.6 - 1/1/2004 - MAP - dc:author support (MarekK); fixed bug tracking
-#  nested divs within content (JohnD); fixed missing sys import (JohanS);
-#  fixed regular expression to capture XML character encoding (Andrei);
-#  added support for Atom 0.3-style links; fixed bug with textInput tracking;
-#  added support for cloud (MartijnP); added support for multiple
-#  category/dc:subject (MartijnP); normalize content model: 'description' gets
-#  description (which can come from description, summary, or full content if no
-#  description), 'content' gets dict of base/language/type/value (which can come
-#  from content:encoded, xhtml:body, content, or fullitem);
-#  fixed bug matching arbitrary Userland namespaces; added xml:base and xml:lang
-#  tracking; fixed bug tracking unknown tags; fixed bug tracking content when
-#  <content> element is not in default namespace (like Pocketsoap feed);
-#  resolve relative URLs in link, guid, docs, url, comments, wfw:comment,
-#  wfw:commentRSS; resolve relative URLs within embedded HTML markup in
-#  description, xhtml:body, content, content:encoded, title, subtitle,
-#  summary, info, tagline, and copyright; added support for pingback and
-#  trackback namespaces
-#2.7 - 1/5/2004 - MAP - really added support for trackback and pingback
-#  namespaces, as opposed to 2.6 when I said I did but didn't really;
-#  sanitize HTML markup within some elements; added mxTidy support (if
-#  installed) to tidy HTML markup within some elements; fixed indentation
-#  bug in _parse_date (FazalM); use socket.setdefaulttimeout if available
-#  (FazalM); universal date parsing and normalization (FazalM): 'created', modified',
-#  'issued' are parsed into 9-tuple date format and stored in 'created_parsed',
-#  'modified_parsed', and 'issued_parsed'; 'date' is duplicated in 'modified'
-#  and vice-versa; 'date_parsed' is duplicated in 'modified_parsed' and vice-versa
-#2.7.1 - 1/9/2004 - MAP - fixed bug handling &quot; and &apos;.  fixed memory
-#  leak not closing url opener (JohnD); added dc:publisher support (MarekK);
-#  added admin:errorReportsTo support (MarekK); Python 2.1 dict support (MarekK)
-#2.7.4 - 1/14/2004 - MAP - added workaround for improperly formed <br/> tags in
-#  encoded HTML (skadz); fixed unicode handling in normalize_attrs (ChrisL);
-#  fixed relative URI processing for guid (skadz); added ICBM support; added
-#  base64 support
-#2.7.5 - 1/15/2004 - MAP - added workaround for malformed DOCTYPE (seen on many
-#  blogspot.com sites); added _debug variable
-#2.7.6 - 1/16/2004 - MAP - fixed bug with StringIO importing
-#3.0b3 - 1/23/2004 - MAP - parse entire feed with real XML parser (if available);
-#  added several new supported namespaces; fixed bug tracking naked markup in
-#  description; added support for enclosure; added support for source; re-added
-#  support for cloud which got dropped somehow; added support for expirationDate
-#3.0b4 - 1/26/2004 - MAP - fixed xml:lang inheritance; fixed multiple bugs tracking
-#  xml:base URI, one for documents that don't define one explicitly and one for
-#  documents that define an outer and an inner xml:base that goes out of scope
-#  before the end of the document
-#3.0b5 - 1/26/2004 - MAP - fixed bug parsing multiple links at feed level
-#3.0b6 - 1/27/2004 - MAP - added feed type and version detection, result['version']
-#  will be one of SUPPORTED_VERSIONS.keys() or empty string if unrecognized;
-#  added support for creativeCommons:license and cc:license; added support for
-#  full Atom content model in title, tagline, info, copyright, summary; fixed bug
-#  with gzip encoding (not always telling server we support it when we do)
-#3.0b7 - 1/28/2004 - MAP - support Atom-style author element in author_detail
-#  (dictionary of 'name', 'url', 'email'); map author to author_detail if author
-#  contains name + email address
-#3.0b8 - 1/28/2004 - MAP - added support for contributor
-#3.0b9 - 1/29/2004 - MAP - fixed check for presence of dict function; added
-#  support for summary
-#3.0b10 - 1/31/2004 - MAP - incorporated ISO-8601 date parsing routines from
-#  xml.util.iso8601
-#3.0b11 - 2/2/2004 - MAP - added 'rights' to list of elements that can contain
-#  dangerous markup; fiddled with decodeEntities (not right); liberalized
-#  date parsing even further
-#3.0b12 - 2/6/2004 - MAP - fiddled with decodeEntities (still not right);
-#  added support to Atom 0.2 subtitle; added support for Atom content model
-#  in copyright; better sanitizing of dangerous HTML elements with end tags
-#  (script, frameset)
-#3.0b13 - 2/8/2004 - MAP - better handling of empty HTML tags (br, hr, img,
-#  etc.) in embedded markup, in either HTML or XHTML form (<br>, <br/>, <br />)
-#3.0b14 - 2/8/2004 - MAP - fixed CDATA handling in non-wellformed feeds under
-#  Python 2.1
-#3.0b15 - 2/11/2004 - MAP - fixed bug resolving relative links in wfw:commentRSS;
-#  fixed bug capturing author and contributor URL; fixed bug resolving relative
-#  links in author and contributor URL; fixed bug resolvin relative links in
-#  generator URL; added support for recognizing RSS 1.0; passed Simon Fell's
-#  namespace tests, and included them permanently in the test suite with his
-#  permission; fixed namespace handling under Python 2.1
-#3.0b16 - 2/12/2004 - MAP - fixed support for RSS 0.90 (broken in b15)
-#3.0b17 - 2/13/2004 - MAP - determine character encoding as per RFC 3023
-#3.0b18 - 2/17/2004 - MAP - always map description to summary_detail (Andrei);
-#  use libxml2 (if available)
-#3.0b19 - 3/15/2004 - MAP - fixed bug exploding author information when author
-#  name was in parentheses; removed ultra-problematic mxTidy support; patch to
-#  workaround crash in PyXML/expat when encountering invalid entities
-#  (MarkMoraes); support for textinput/textInput
-#3.0b20 - 4/7/2004 - MAP - added CDF support
-#3.0b21 - 4/14/2004 - MAP - added Hot RSS support
-#3.0b22 - 4/19/2004 - MAP - changed 'channel' to 'feed', 'item' to 'entries' in
-#  results dict; changed results dict to allow getting values with results.key
-#  as well as results[key]; work around embedded illformed HTML with half
-#  a DOCTYPE; work around malformed Content-Type header; if character encoding
-#  is wrong, try several common ones before falling back to regexes (if this
-#  works, bozo_exception is set to CharacterEncodingOverride); fixed character
-#  encoding issues in BaseHTMLProcessor by tracking encoding and converting
-#  from Unicode to raw strings before feeding data to sgmllib.SGMLParser;
-#  convert each value in results to Unicode (if possible), even if using
-#  regex-based parsing
-#3.0b23 - 4/21/2004 - MAP - fixed UnicodeDecodeError for feeds that contain
-#  high-bit characters in attributes in embedded HTML in description (thanks
-#  Thijs van de Vossen); moved guid, date, and date_parsed to mapped keys in
-#  FeedParserDict; tweaked FeedParserDict.has_key to return True if asking
-#  about a mapped key
-#3.0fc1 - 4/23/2004 - MAP - made results.entries[0].links[0] and
-#  results.entries[0].enclosures[0] into FeedParserDict; fixed typo that could
-#  cause the same encoding to be tried twice (even if it failed the first time);
-#  fixed DOCTYPE stripping when DOCTYPE contained entity declarations;
-#  better textinput and image tracking in illformed RSS 1.0 feeds
-#3.0fc2 - 5/10/2004 - MAP - added and passed Sam's amp tests; added and passed
-#  my blink tag tests
-#3.0fc3 - 6/18/2004 - MAP - fixed bug in _changeEncodingDeclaration that
-#  failed to parse utf-16 encoded feeds; made source into a FeedParserDict;
-#  duplicate admin:generatorAgent/@rdf:resource in generator_detail.url;
-#  added support for image; refactored parse() fallback logic to try other
-#  encodings if SAX parsing fails (previously it would only try other encodings
-#  if re-encoding failed); remove unichr madness in normalize_attrs now that
-#  we're properly tracking encoding in and out of BaseHTMLProcessor; set
-#  feed.language from root-level xml:lang; set entry.id from rdf:about;
-#  send Accept header
-#3.0 - 6/21/2004 - MAP - don't try iso-8859-1 (can't distinguish between
-#  iso-8859-1 and windows-1252 anyway, and most incorrectly marked feeds are
-#  windows-1252); fixed regression that could cause the same encoding to be
-#  tried twice (even if it failed the first time)
-#3.0.1 - 6/22/2004 - MAP - default to us-ascii for all text/* content types;
-#  recover from malformed content-type header parameter with no equals sign
-#  ('text/xml; charset:iso-8859-1')
-#3.1 - 6/28/2004 - MAP - added and passed tests for converting HTML entities
-#  to Unicode equivalents in illformed feeds (aaronsw); added and
-#  passed tests for converting character entities to Unicode equivalents
-#  in illformed feeds (aaronsw); test for valid parsers when setting
-#  XML_AVAILABLE; make version and encoding available when server returns
-#  a 304; add handlers parameter to pass arbitrary urllib2 handlers (like
-#  digest auth or proxy support); add code to parse username/password
-#  out of url and send as basic authentication; expose downloading-related
-#  exceptions in bozo_exception (aaronsw); added __contains__ method to
-#  FeedParserDict (aaronsw); added publisher_detail (aaronsw)
-#3.2 - 7/3/2004 - MAP - use cjkcodecs and iconv_codec if available; always
-#  convert feed to UTF-8 before passing to XML parser; completely revamped
-#  logic for determining character encoding and attempting XML parsing
-#  (much faster); increased default timeout to 20 seconds; test for presence
-#  of Location header on redirects; added tests for many alternate character
-#  encodings; support various EBCDIC encodings; support UTF-16BE and
-#  UTF16-LE with or without a BOM; support UTF-8 with a BOM; support
-#  UTF-32BE and UTF-32LE with or without a BOM; fixed crashing bug if no
-#  XML parsers are available; added support for 'Content-encoding: deflate';
-#  send blank 'Accept-encoding: ' header if neither gzip nor zlib modules
-#  are available
-#3.3 - 7/15/2004 - MAP - optimize EBCDIC to ASCII conversion; fix obscure
-#  problem tracking xml:base and xml:lang if element declares it, child
-#  doesn't, first grandchild redeclares it, and second grandchild doesn't;
-#  refactored date parsing; defined public registerDateHandler so callers
-#  can add support for additional date formats at runtime; added support
-#  for OnBlog, Nate, MSSQL, Greek, and Hungarian dates (ytrewq1); added
-#  zopeCompatibilityHack() which turns FeedParserDict into a regular
-#  dictionary, required for Zope compatibility, and also makes command-
-#  line debugging easier because pprint module formats real dictionaries
-#  better than dictionary-like objects; added NonXMLContentType exception,
-#  which is stored in bozo_exception when a feed is served with a non-XML
-#  media type such as 'text/plain'; respect Content-Language as default
-#  language if not xml:lang is present; cloud dict is now FeedParserDict;
-#  generator dict is now FeedParserDict; better tracking of xml:lang,
-#  including support for xml:lang='' to unset the current language;
-#  recognize RSS 1.0 feeds even when RSS 1.0 namespace is not the default
-#  namespace; don't overwrite final status on redirects (scenarios:
-#  redirecting to a URL that returns 304, redirecting to a URL that
-#  redirects to another URL with a different type of redirect); add
-#  support for HTTP 303 redirects
-#4.0 - MAP - support for relative URIs in xml:base attribute; fixed
-#  encoding issue with mxTidy (phopkins); preliminary support for RFC 3229;
-#  support for Atom 1.0; support for iTunes extensions; new 'tags' for
-#  categories/keywords/etc. as array of dict
-#  {'term': term, 'scheme': scheme, 'label': label} to match Atom 1.0
-#  terminology; parse RFC 822-style dates with no time; lots of other
-#  bug fixes
-#4.1 - MAP - removed socket timeout; added support for chardet library
index f2e3917..964141b 100644 (file)
@@ -1,9 +1,23 @@
 --find-links=http://stigma.nowoczesnapolska.org.pl/pypi/
 --find-links=http://www.pythonware.com/products/pil/
 
-Django==1.1.1
-librarian==1.2.6
-lxml==2.2.2
-Imaging==1.1.6
-mutagen==1.17
-MySQL-python>=1.2,<2.0
\ No newline at end of file
+# django
+Django>=1.1.1,<1.2
+South>=0.4 # migrations for django
+django-pagination>=1.0
+django-rosetta>=0.5.3
+django-maintenancemode>=0.9
+
+# Feedparser 
+Feedparser>=4.1
+
+# PIL 
+Imaging>=1.1.6
+mutagen>=1.17
+sorl-thumbnail>=3.2
+
+# home-brewed & dependencies
+librarian>=1.2.6
+lxml>=2.2.2
+
+# MySQL-python>=1.2,<2.0
index 6114713..247fc2c 100644 (file)
@@ -1,4 +1,8 @@
+#!/usr/bin/env python
 # -*- coding: utf-8 -*-
+# This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
 from lxml import etree
 from slughifi import slughifi
 from django.core.management import setup_environ
index 671212d..0bc190e 100644 (file)
@@ -1,3 +1,8 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
 import sys
 sys.path.insert(0, '../apps')
 sys.path.insert(0, '../lib')
index 3ac4192..9cc721b 100755 (executable)
@@ -1,5 +1,8 @@
 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
+# This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
 from django.core.management import setup_environ
 from wolnelektury import settings
 import sys
index e72512b..bea5cda 100755 (executable)
@@ -1,4 +1,8 @@
 #!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
 import sys
 
 from django.core.management import setup_environ
index 245ed1d..e83a07f 100755 (executable)
@@ -1,5 +1,8 @@
 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
+# This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
 from django.core.management import setup_environ
 from wolnelektury import settings
 import sys
index 41d3f3d..c53cfd3 100644 (file)
@@ -1,3 +1,8 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
 import httplib2
 from poster.encode import multipart_encode
 from poster.streaminghttp import register_openers
diff --git a/wolnelektury.vhost.tmpl b/wolnelektury.vhost.tmpl
new file mode 100644 (file)
index 0000000..6fac756
--- /dev/null
@@ -0,0 +1,23 @@
+<VirtualHost *:80>
+    ServerName $DOMAIN
+    ServerAdmin $ADMIN_EMAIL
+
+    WSGIDaemonProcess $PROJECT_NAME user=$WSGI_USER group=$WSGI_USER processes=$WSGI_PROCESSES threads=$WSGI_THREADS display-name=%{GROUP}
+    WSGIProcessGroup $PROJECT_NAME
+
+    WSGIScriptAlias / $WSGI_TARGET
+    <Directory $WSGI_DIR>
+        Order allow,deny
+        allow from all
+    </Directory>
+
+    Alias /media $MEDIA_ROOT
+    <Directory $MEDIA_ROOT >
+        Order allow,deny
+        Allow from all
+    </Directory>
+
+    LogLevel warn
+    ErrorLog /var/log/apache2/$PROJECT_NAME/error.log
+    CustomLog /var/log/apache2/$PROJECT_NAME/access.log combined
+</VirtualHost>
diff --git a/wolnelektury.wsgi.tmpl b/wolnelektury.wsgi.tmpl
new file mode 100644 (file)
index 0000000..6b772f3
--- /dev/null
@@ -0,0 +1,24 @@
+#!$PYTHON
+import site
+site.addsitedir('$PYTHON_SITE')
+
+import os
+from os.path import abspath, dirname, join
+import sys
+
+# Redirect sys.stdout to sys.stderr for bad libraries like geopy that use
+# print statements for optional import exceptions.
+sys.stdout = sys.stderr
+
+# Add apps and lib directories to PYTHONPATH
+sys.path = [
+    '$PROJECT_ROOT',
+       '$PROJECT_ROOT/lib',
+       '$PROJECT_ROOT/apps',
+] + sys.path
+
+# Run Django
+os.environ['DJANGO_SETTINGS_MODULE'] = '$PROJECT_NAME.settings'
+
+from django.core.handlers.wsgi import WSGIHandler
+application = WSGIHandler()
diff --git a/wolnelektury/context_processors.py b/wolnelektury/context_processors.py
new file mode 100644 (file)
index 0000000..0cbf605
--- /dev/null
@@ -0,0 +1,6 @@
+
+def extra_settings(request):
+    from django.conf import settings
+    return {
+        'STATIC_URL': settings.STATIC_URL,
+    }
diff --git a/wolnelektury/locale/de/LC_MESSAGES/django.mo b/wolnelektury/locale/de/LC_MESSAGES/django.mo
new file mode 100644 (file)
index 0000000..c757a97
Binary files /dev/null and b/wolnelektury/locale/de/LC_MESSAGES/django.mo differ
diff --git a/wolnelektury/locale/de/LC_MESSAGES/django.po b/wolnelektury/locale/de/LC_MESSAGES/django.po
new file mode 100644 (file)
index 0000000..fbd8e43
--- /dev/null
@@ -0,0 +1,737 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2010-05-19 15:39+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: settings.py:37
+msgid "German"
+msgstr ""
+
+#: settings.py:38
+msgid "English"
+msgstr ""
+
+#: settings.py:39
+msgid "Polish"
+msgstr ""
+
+#: settings.py:40
+msgid "Lithuanian"
+msgstr ""
+
+#: settings.py:41
+msgid "French"
+msgstr ""
+
+#: settings.py:42
+msgid "Russian"
+msgstr ""
+
+#: settings.py:43
+msgid "Spanish"
+msgstr ""
+
+#: templates/404.html:6 templates/404.html.py:15
+msgid "Page does not exist"
+msgstr ""
+
+#: templates/404.html:17
+msgid ""
+"We are sorry, but this page does not exist. Please check if you entered "
+"correct address or go to "
+msgstr ""
+
+#: templates/404.html:17
+msgid "main page"
+msgstr ""
+
+#: templates/500.html:6 templates/500.html.py:54
+msgid "Server error"
+msgstr ""
+
+#: templates/500.html:55
+msgid ""
+"<p>The Wolnelektury.pl site is currently unavailable. Meanwhile, visit our "
+"<a href='http://nowoczesnapolska.org.pl'>blog</a>.</p> <p>Inform our <a "
+"href='mailto:fundacja@nowoczesnapolska.org.pl'>administrators</a> about the "
+"error.</p>"
+msgstr ""
+
+#: templates/503.html:6 templates/503.html.py:54
+msgid "Service unavailable"
+msgstr ""
+
+#: templates/503.html:56
+msgid "The Wolnelektury.pl site is currently unavailable due to maintainance."
+msgstr ""
+
+#: templates/base.html:20
+msgid ""
+"Internet Explorer cannot display this site properly. Click here to read "
+"more..."
+msgstr ""
+
+#: templates/base.html:28 templates/catalogue/folded_tag_list.html:13
+#: templates/catalogue/main_page.html:43 templates/catalogue/main_page.html:48
+#: templates/catalogue/main_page.html:87
+#: templates/catalogue/main_page.html:270
+#: templates/catalogue/main_page.html:279
+msgid "See more"
+msgstr ""
+
+#: templates/base.html:37
+msgid "Welcome"
+msgstr ""
+
+#: templates/base.html:38
+msgid "Your shelves"
+msgstr ""
+
+#: templates/base.html:40
+msgid "Administration"
+msgstr ""
+
+#: templates/base.html:42
+msgid "Logout"
+msgstr ""
+
+#: templates/base.html:45 templates/base.html.py:91 templates/base.html:95
+#: templates/base.html.py:99 templates/auth/login.html:4
+#: templates/auth/login.html.py:7 templates/auth/login.html:12
+#: templates/auth/login.html.py:15
+msgid "Sign in"
+msgstr ""
+
+#: templates/base.html:45 templates/base.html.py:91 templates/base.html:99
+#: templates/base.html.py:103 templates/auth/login.html:7
+#: templates/auth/login.html.py:21 templates/auth/login.html:23
+msgid "Register"
+msgstr ""
+
+#: templates/base.html:55
+msgid "Choose your interface language: "
+msgstr ""
+
+#: templates/base.html:60
+msgid "Choose language"
+msgstr ""
+
+#: templates/base.html:72
+msgid ""
+"\n"
+"\t\t\t\tWolne Lektury is a project lead by <a href=\"http://nowoczesnapolska."
+"org.pl/\">Modern Poland Foundation</a>.\n"
+"\t\t\t\tDigital reproductions are made by <a href=\"http://www.bn.org.pl/"
+"\">The National Library</a>, based on TNL resources. \n"
+"\t\t\t\tHosting <a href=\"http://eo.pl/\">EO Networks</a>.\n"
+"\t\t\t\t"
+msgstr ""
+
+#: templates/base.html:79
+msgid ""
+"\n"
+"\t\t\t\tModern Poland Foundation, 00-514 Warsaw, ul. Marszałkowska 84/92 "
+"lok. 125, tel/fax: (22) 621-30-17\n"
+"                e-mail: <a href=\"mailto:fundacja@nowoczesnapolska.org.pl"
+"\">fundacja@nowoczesnapolska.org.pl</a>\n"
+"\t\t\t\t"
+msgstr ""
+
+#: templates/base.html:88 templates/base.html.py:109
+#: templates/catalogue/book_detail.html:129
+#: templates/catalogue/book_fragments.html:33
+#: templates/catalogue/book_stub_detail.html:31
+#: templates/catalogue/search_no_hits.html:23
+#: templates/catalogue/tagged_object_list.html:141
+msgid "Close"
+msgstr ""
+
+#: templates/base.html:111 templates/catalogue/book_detail.html:131
+#: templates/catalogue/book_fragments.html:35
+#: templates/catalogue/book_stub_detail.html:33
+#: templates/catalogue/search_no_hits.html:25
+#: templates/catalogue/tagged_object_list.html:143
+msgid "Loading"
+msgstr ""
+
+#: templates/admin/base_site.html:4 templates/admin/base_site.html.py:7
+msgid "Site administration"
+msgstr ""
+
+#: templates/admin/base_site.html:8
+msgid "Translations"
+msgstr ""
+
+#: templates/admin/catalogue/book/change_list.html:6
+msgid "Import book"
+msgstr ""
+
+#: templates/auth/login.html:4
+msgid "Register on"
+msgstr ""
+
+#: templates/auth/login.html:9 templates/catalogue/book_detail.html:12
+#: templates/catalogue/book_fragments.html:12
+#: templates/catalogue/book_list.html:12
+#: templates/catalogue/book_stub_detail.html:12
+#: templates/catalogue/breadcrumbs.html:9
+#: templates/catalogue/main_page.html:13
+#: templates/lessons/document_detail.html:9
+#: templates/lessons/document_list.html:51
+msgid "Search"
+msgstr ""
+
+#: templates/auth/login.html:9 templates/catalogue/book_detail.html:12
+#: templates/catalogue/book_fragments.html:12
+#: templates/catalogue/book_list.html:12
+#: templates/catalogue/book_stub_detail.html:12
+#: templates/catalogue/main_page.html:13
+#: templates/catalogue/tagged_object_list.html:41
+#: templates/lessons/document_detail.html:9
+#: templates/lessons/document_list.html:51
+msgid "or"
+msgstr ""
+
+#: templates/auth/login.html:9 templates/catalogue/book_detail.html:12
+#: templates/catalogue/book_list.html:12
+#: templates/catalogue/book_stub_detail.html:12
+#: templates/lessons/document_list.html:51
+msgid "return to main page"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:5
+msgid "on WolneLektury.pl"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:17
+msgid "Work is licensed under "
+msgstr ""
+
+#: templates/catalogue/book_detail.html:19
+msgid "Based on"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:24
+#: templates/catalogue/tagged_object_list.html:27
+msgid "Hide description"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:27
+msgid "Put a book"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:27
+msgid "on the shelf!"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:31
+msgid "Read online"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:34
+msgid "Download PDF"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:37
+msgid "Download ODT"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:40
+msgid "Download TXT"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:45
+msgid "Artist"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:47
+msgid "Director"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:51
+msgid "Download MP3"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:52
+msgid "Download Ogg Vorbis"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:79
+msgid "Details"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:82
+msgid "Author"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:88
+msgid "Epoch"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:94
+msgid "Kind"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:100
+msgid "Genre"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:106
+msgid "Other resources"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:108
+msgid "Book on project's wiki"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:109
+msgid "Source of the book"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:111
+msgid "Book description on Lektury.Gazeta.pl"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:114
+msgid "Book description on Wikipedia"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:119
+msgid "Work's themes "
+msgstr ""
+
+#: templates/catalogue/book_fragments.html:5
+#: templates/catalogue/book_fragments.html:10
+msgid "Theme"
+msgstr ""
+
+#: templates/catalogue/book_fragments.html:5
+#: templates/catalogue/book_fragments.html:10
+msgid "in work "
+msgstr ""
+
+#: templates/catalogue/book_fragments.html:12
+msgid "return to book's page"
+msgstr ""
+
+#: templates/catalogue/book_fragments.html:26
+msgid "See description"
+msgstr ""
+
+#: templates/catalogue/book_fragments.html:26
+msgid "of the book "
+msgstr ""
+
+#: templates/catalogue/book_list.html:7
+msgid "Alphabetical listing of works on WolneLektury.pl"
+msgstr ""
+
+#: templates/catalogue/book_list.html:10
+msgid "Alphabetical listing of works"
+msgstr ""
+
+#: templates/catalogue/book_sets.html:2
+msgid "Put a book on the shelf!"
+msgstr ""
+
+#: templates/catalogue/book_sets.html:4
+msgid "You do not have any shelves. You can create one below, if you want to."
+msgstr ""
+
+#: templates/catalogue/book_sets.html:9 templates/catalogue/book_short.html:4
+msgid "Put on the shelf!"
+msgstr ""
+
+#: templates/catalogue/book_sets.html:16
+#: templates/catalogue/fragment_sets.html:16
+msgid "Create new shelf"
+msgstr ""
+
+#: templates/catalogue/book_short.html:14
+msgid "Jump to"
+msgstr ""
+
+#: templates/catalogue/book_short.html:16
+msgid "Categories"
+msgstr ""
+
+#: templates/catalogue/book_stub_detail.html:17
+msgid ""
+"This work is in public domain and will be published on Internet school "
+"library of Wolne Lektury soon."
+msgstr ""
+
+#: templates/catalogue/book_stub_detail.html:20
+msgid ""
+"This work will become part of public domain and will be allowed to be "
+"published without restrictions in"
+msgstr ""
+
+#: templates/catalogue/book_stub_detail.html:22
+msgid "Find out why Internet libraries can't publish this work."
+msgstr ""
+
+#: templates/catalogue/book_stub_detail.html:24
+msgid "This work is copyrighted."
+msgstr ""
+
+#: templates/catalogue/book_text.html:17
+msgid "Table of contents"
+msgstr ""
+
+#: templates/catalogue/book_text.html:18
+#: templates/catalogue/tagged_object_list.html:132
+msgid "Themes"
+msgstr ""
+
+#: templates/catalogue/folded_tag_list.html:4
+msgid "Show full category"
+msgstr ""
+
+#: templates/catalogue/folded_tag_list.html:22
+#: templates/catalogue/main_page.html:250
+msgid "Hide"
+msgstr ""
+
+#: templates/catalogue/fragment_sets.html:2
+msgid "Shelves containing fragment"
+msgstr ""
+
+#: templates/catalogue/fragment_sets.html:4
+#: templates/catalogue/main_page.html:28
+msgid "You do not own any shelves. You can create one below, if you want to."
+msgstr ""
+
+#: templates/catalogue/fragment_sets.html:9
+msgid "Save all shelves"
+msgstr ""
+
+#: templates/catalogue/fragment_short.html:6
+msgid "Expand fragment"
+msgstr ""
+
+#: templates/catalogue/fragment_short.html:12
+msgid "Hide fragment"
+msgstr ""
+
+#: templates/catalogue/fragment_short.html:17
+msgid "See in a book"
+msgstr ""
+
+#: templates/catalogue/main_page.html:13
+msgid "check list of books"
+msgstr ""
+
+#: templates/catalogue/main_page.html:13
+msgid "in our repository"
+msgstr ""
+
+#: templates/catalogue/main_page.html:17
+msgid "Browse books by categories"
+msgstr ""
+
+#: templates/catalogue/main_page.html:19
+#: templates/catalogue/user_shelves.html:2
+msgid "Your shelves with books"
+msgstr ""
+
+#: templates/catalogue/main_page.html:24
+msgid "delete"
+msgstr ""
+
+#: templates/catalogue/main_page.html:33
+#: templates/catalogue/user_shelves.html:15
+msgid "Create shelf"
+msgstr ""
+
+#: templates/catalogue/main_page.html:37
+msgid ""
+"Create your own book set. You can share it with friends by sending them link "
+"to your shelf."
+msgstr ""
+
+#: templates/catalogue/main_page.html:38
+msgid "You need to "
+msgstr ""
+
+#: templates/catalogue/main_page.html:38
+msgid "sign in"
+msgstr ""
+
+#: templates/catalogue/main_page.html:38
+msgid "to manage your shelves."
+msgstr ""
+
+#: templates/catalogue/main_page.html:41
+#: templates/lessons/document_list.html:49
+msgid "Hand-outs for teachers"
+msgstr ""
+
+#: templates/catalogue/main_page.html:42
+msgid ""
+"Lessons' prospects and other ideas for using Wolnelektury.pl for teaching."
+msgstr ""
+
+#: templates/catalogue/main_page.html:47
+msgid ""
+"are professional recordings of literary texts from our repository, available "
+"on free license in MP3 and Ogg Vorbis formats as well as in DAISY system."
+msgstr ""
+
+#: templates/catalogue/main_page.html:54
+#: templates/catalogue/tagged_object_list.html:114
+msgid "Authors"
+msgstr ""
+
+#: templates/catalogue/main_page.html:58
+#: templates/catalogue/tagged_object_list.html:118
+msgid "Kinds"
+msgstr ""
+
+#: templates/catalogue/main_page.html:62
+#: templates/catalogue/tagged_object_list.html:122
+msgid "Genres"
+msgstr ""
+
+#: templates/catalogue/main_page.html:66
+#: templates/catalogue/tagged_object_list.html:126
+msgid "Epochs"
+msgstr ""
+
+#: templates/catalogue/main_page.html:72
+msgid "Themes and topics"
+msgstr ""
+
+#: templates/catalogue/main_page.html:75
+msgid "Themes groups"
+msgstr ""
+
+#: templates/catalogue/main_page.html:260
+msgid "News"
+msgstr ""
+
+#: templates/catalogue/main_page.html:264
+msgid "See our blog"
+msgstr ""
+
+#: templates/catalogue/main_page.html:267
+msgid "You can help us!"
+msgstr ""
+
+#: templates/catalogue/main_page.html:268
+msgid ""
+"We try our best to elaborate works appended to our library. It is possible "
+"only due to support of our volunteers."
+msgstr ""
+
+#: templates/catalogue/main_page.html:269
+msgid ""
+"We invite people who want to take part in developing Internet school library "
+"Wolne Lektury."
+msgstr ""
+
+#: templates/catalogue/main_page.html:273
+msgid "About us"
+msgstr ""
+
+#: templates/catalogue/main_page.html:275
+msgid ""
+"\n"
+"\t\t\tInternet library with school readings “Wolne Lektury” (<a href="
+"\"http://wolnelektury.pl\">www.wolnelektury.pl</a>) is a project made by "
+"Modern Poland Foundation. It started in 2007 and shares school readings, "
+"which are recommended by Ministry of National Education and are in public "
+"domain.\n"
+"\t\t\t"
+msgstr ""
+
+#: templates/catalogue/search_no_hits.html:5
+msgid "Search in WolneLektury.pl"
+msgstr ""
+
+#: templates/catalogue/search_no_hits.html:15
+msgid "Sorry! Search cirteria did not match any resources."
+msgstr ""
+
+#: templates/catalogue/search_no_hits.html:17
+msgid ""
+"Search engine supports following criteria: title, author, theme/topic, "
+"epoch, kind and genre.\n"
+"\t\tAs for now we do not support full text search."
+msgstr ""
+
+#: templates/catalogue/tag_list.html:4
+msgid "See full category"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:15
+msgid "Your shelf is empty"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:16
+msgid ""
+"You can put a book on a shelf by entering page of the reading and clicking "
+"'Put on the shelf'."
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:31
+msgid "Download all books from this shelf"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:35
+msgid "Choose books' formats which you want to download:"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:36
+#: templates/catalogue/tagged_object_list.html:37
+#: templates/catalogue/tagged_object_list.html:38
+msgid "for reading"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:36
+msgid "and printing using"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:37
+msgid "and editing using"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:38
+msgid "on small displays, for example mobile phones"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:39
+#: templates/catalogue/tagged_object_list.html:40
+msgid "for listening"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:39
+msgid "on favourite MP3 player"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:40
+msgid "open format"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:40
+msgid "Xiph.org Foundation"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:41
+#: templates/lessons/ajax_document_detail.html:3
+#: templates/lessons/document_detail.html:13
+msgid "Download"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:41
+msgid "Updating list of books' formats on the shelf"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:41
+msgid "cancel"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:46
+msgid "Share this shelf"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:48
+msgid ""
+"Copy this link and share it with other people to let them see your shelf."
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:57
+msgid "Read work's study of this author on Lektury.Gazeta.pl"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:60
+msgid "Read study of epoch"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:60
+msgid "on Lektury.Gazeta.pl"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:67
+msgid "Read article about this author on Wikipedia"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:70
+msgid "Read article about epoch"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:70
+msgid "on Wikipedia"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:80
+msgid "Delete"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:88
+msgid "This author's works are copyrighted."
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:91
+msgid ""
+"This author's works are in public domain and will be published on Internet "
+"school library of Wolne Lektury soon."
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:95
+msgid ""
+"This author's works will become part of public domain and will be allowed to "
+"be published without restrictions in"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:97
+msgid "Find out why Internet libraries can't publish this author's works."
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:102
+msgid "No works of this author found."
+msgstr ""
+
+#: templates/catalogue/user_shelves.html:6
+msgid "remove"
+msgstr ""
+
+#: templates/catalogue/user_shelves.html:10
+msgid "You do not own any shelves. You can create one below if you want to"
+msgstr ""
+
+#: templates/lessons/ajax_document_detail.html:3
+#: templates/lessons/document_detail.html:13
+msgid "author"
+msgstr ""
+
+#: templates/lessons/document_detail.html:9
+msgid "return to list of materials"
+msgstr ""
+
+#: templates/lessons/document_list.html:7
+msgid "Hand-outs for teachers on "
+msgstr ""
+
+#: templates/pagination/pagination.html:5
+#: templates/pagination/pagination.html:7
+msgid "previous"
+msgstr ""
+
+#: templates/pagination/pagination.html:21
+#: templates/pagination/pagination.html:23
+msgid "next"
+msgstr ""
diff --git a/wolnelektury/locale/en/LC_MESSAGES/django.mo b/wolnelektury/locale/en/LC_MESSAGES/django.mo
new file mode 100644 (file)
index 0000000..c757a97
Binary files /dev/null and b/wolnelektury/locale/en/LC_MESSAGES/django.mo differ
diff --git a/wolnelektury/locale/en/LC_MESSAGES/django.po b/wolnelektury/locale/en/LC_MESSAGES/django.po
new file mode 100644 (file)
index 0000000..fbd8e43
--- /dev/null
@@ -0,0 +1,737 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2010-05-19 15:39+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: settings.py:37
+msgid "German"
+msgstr ""
+
+#: settings.py:38
+msgid "English"
+msgstr ""
+
+#: settings.py:39
+msgid "Polish"
+msgstr ""
+
+#: settings.py:40
+msgid "Lithuanian"
+msgstr ""
+
+#: settings.py:41
+msgid "French"
+msgstr ""
+
+#: settings.py:42
+msgid "Russian"
+msgstr ""
+
+#: settings.py:43
+msgid "Spanish"
+msgstr ""
+
+#: templates/404.html:6 templates/404.html.py:15
+msgid "Page does not exist"
+msgstr ""
+
+#: templates/404.html:17
+msgid ""
+"We are sorry, but this page does not exist. Please check if you entered "
+"correct address or go to "
+msgstr ""
+
+#: templates/404.html:17
+msgid "main page"
+msgstr ""
+
+#: templates/500.html:6 templates/500.html.py:54
+msgid "Server error"
+msgstr ""
+
+#: templates/500.html:55
+msgid ""
+"<p>The Wolnelektury.pl site is currently unavailable. Meanwhile, visit our "
+"<a href='http://nowoczesnapolska.org.pl'>blog</a>.</p> <p>Inform our <a "
+"href='mailto:fundacja@nowoczesnapolska.org.pl'>administrators</a> about the "
+"error.</p>"
+msgstr ""
+
+#: templates/503.html:6 templates/503.html.py:54
+msgid "Service unavailable"
+msgstr ""
+
+#: templates/503.html:56
+msgid "The Wolnelektury.pl site is currently unavailable due to maintainance."
+msgstr ""
+
+#: templates/base.html:20
+msgid ""
+"Internet Explorer cannot display this site properly. Click here to read "
+"more..."
+msgstr ""
+
+#: templates/base.html:28 templates/catalogue/folded_tag_list.html:13
+#: templates/catalogue/main_page.html:43 templates/catalogue/main_page.html:48
+#: templates/catalogue/main_page.html:87
+#: templates/catalogue/main_page.html:270
+#: templates/catalogue/main_page.html:279
+msgid "See more"
+msgstr ""
+
+#: templates/base.html:37
+msgid "Welcome"
+msgstr ""
+
+#: templates/base.html:38
+msgid "Your shelves"
+msgstr ""
+
+#: templates/base.html:40
+msgid "Administration"
+msgstr ""
+
+#: templates/base.html:42
+msgid "Logout"
+msgstr ""
+
+#: templates/base.html:45 templates/base.html.py:91 templates/base.html:95
+#: templates/base.html.py:99 templates/auth/login.html:4
+#: templates/auth/login.html.py:7 templates/auth/login.html:12
+#: templates/auth/login.html.py:15
+msgid "Sign in"
+msgstr ""
+
+#: templates/base.html:45 templates/base.html.py:91 templates/base.html:99
+#: templates/base.html.py:103 templates/auth/login.html:7
+#: templates/auth/login.html.py:21 templates/auth/login.html:23
+msgid "Register"
+msgstr ""
+
+#: templates/base.html:55
+msgid "Choose your interface language: "
+msgstr ""
+
+#: templates/base.html:60
+msgid "Choose language"
+msgstr ""
+
+#: templates/base.html:72
+msgid ""
+"\n"
+"\t\t\t\tWolne Lektury is a project lead by <a href=\"http://nowoczesnapolska."
+"org.pl/\">Modern Poland Foundation</a>.\n"
+"\t\t\t\tDigital reproductions are made by <a href=\"http://www.bn.org.pl/"
+"\">The National Library</a>, based on TNL resources. \n"
+"\t\t\t\tHosting <a href=\"http://eo.pl/\">EO Networks</a>.\n"
+"\t\t\t\t"
+msgstr ""
+
+#: templates/base.html:79
+msgid ""
+"\n"
+"\t\t\t\tModern Poland Foundation, 00-514 Warsaw, ul. Marszałkowska 84/92 "
+"lok. 125, tel/fax: (22) 621-30-17\n"
+"                e-mail: <a href=\"mailto:fundacja@nowoczesnapolska.org.pl"
+"\">fundacja@nowoczesnapolska.org.pl</a>\n"
+"\t\t\t\t"
+msgstr ""
+
+#: templates/base.html:88 templates/base.html.py:109
+#: templates/catalogue/book_detail.html:129
+#: templates/catalogue/book_fragments.html:33
+#: templates/catalogue/book_stub_detail.html:31
+#: templates/catalogue/search_no_hits.html:23
+#: templates/catalogue/tagged_object_list.html:141
+msgid "Close"
+msgstr ""
+
+#: templates/base.html:111 templates/catalogue/book_detail.html:131
+#: templates/catalogue/book_fragments.html:35
+#: templates/catalogue/book_stub_detail.html:33
+#: templates/catalogue/search_no_hits.html:25
+#: templates/catalogue/tagged_object_list.html:143
+msgid "Loading"
+msgstr ""
+
+#: templates/admin/base_site.html:4 templates/admin/base_site.html.py:7
+msgid "Site administration"
+msgstr ""
+
+#: templates/admin/base_site.html:8
+msgid "Translations"
+msgstr ""
+
+#: templates/admin/catalogue/book/change_list.html:6
+msgid "Import book"
+msgstr ""
+
+#: templates/auth/login.html:4
+msgid "Register on"
+msgstr ""
+
+#: templates/auth/login.html:9 templates/catalogue/book_detail.html:12
+#: templates/catalogue/book_fragments.html:12
+#: templates/catalogue/book_list.html:12
+#: templates/catalogue/book_stub_detail.html:12
+#: templates/catalogue/breadcrumbs.html:9
+#: templates/catalogue/main_page.html:13
+#: templates/lessons/document_detail.html:9
+#: templates/lessons/document_list.html:51
+msgid "Search"
+msgstr ""
+
+#: templates/auth/login.html:9 templates/catalogue/book_detail.html:12
+#: templates/catalogue/book_fragments.html:12
+#: templates/catalogue/book_list.html:12
+#: templates/catalogue/book_stub_detail.html:12
+#: templates/catalogue/main_page.html:13
+#: templates/catalogue/tagged_object_list.html:41
+#: templates/lessons/document_detail.html:9
+#: templates/lessons/document_list.html:51
+msgid "or"
+msgstr ""
+
+#: templates/auth/login.html:9 templates/catalogue/book_detail.html:12
+#: templates/catalogue/book_list.html:12
+#: templates/catalogue/book_stub_detail.html:12
+#: templates/lessons/document_list.html:51
+msgid "return to main page"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:5
+msgid "on WolneLektury.pl"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:17
+msgid "Work is licensed under "
+msgstr ""
+
+#: templates/catalogue/book_detail.html:19
+msgid "Based on"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:24
+#: templates/catalogue/tagged_object_list.html:27
+msgid "Hide description"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:27
+msgid "Put a book"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:27
+msgid "on the shelf!"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:31
+msgid "Read online"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:34
+msgid "Download PDF"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:37
+msgid "Download ODT"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:40
+msgid "Download TXT"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:45
+msgid "Artist"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:47
+msgid "Director"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:51
+msgid "Download MP3"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:52
+msgid "Download Ogg Vorbis"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:79
+msgid "Details"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:82
+msgid "Author"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:88
+msgid "Epoch"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:94
+msgid "Kind"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:100
+msgid "Genre"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:106
+msgid "Other resources"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:108
+msgid "Book on project's wiki"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:109
+msgid "Source of the book"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:111
+msgid "Book description on Lektury.Gazeta.pl"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:114
+msgid "Book description on Wikipedia"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:119
+msgid "Work's themes "
+msgstr ""
+
+#: templates/catalogue/book_fragments.html:5
+#: templates/catalogue/book_fragments.html:10
+msgid "Theme"
+msgstr ""
+
+#: templates/catalogue/book_fragments.html:5
+#: templates/catalogue/book_fragments.html:10
+msgid "in work "
+msgstr ""
+
+#: templates/catalogue/book_fragments.html:12
+msgid "return to book's page"
+msgstr ""
+
+#: templates/catalogue/book_fragments.html:26
+msgid "See description"
+msgstr ""
+
+#: templates/catalogue/book_fragments.html:26
+msgid "of the book "
+msgstr ""
+
+#: templates/catalogue/book_list.html:7
+msgid "Alphabetical listing of works on WolneLektury.pl"
+msgstr ""
+
+#: templates/catalogue/book_list.html:10
+msgid "Alphabetical listing of works"
+msgstr ""
+
+#: templates/catalogue/book_sets.html:2
+msgid "Put a book on the shelf!"
+msgstr ""
+
+#: templates/catalogue/book_sets.html:4
+msgid "You do not have any shelves. You can create one below, if you want to."
+msgstr ""
+
+#: templates/catalogue/book_sets.html:9 templates/catalogue/book_short.html:4
+msgid "Put on the shelf!"
+msgstr ""
+
+#: templates/catalogue/book_sets.html:16
+#: templates/catalogue/fragment_sets.html:16
+msgid "Create new shelf"
+msgstr ""
+
+#: templates/catalogue/book_short.html:14
+msgid "Jump to"
+msgstr ""
+
+#: templates/catalogue/book_short.html:16
+msgid "Categories"
+msgstr ""
+
+#: templates/catalogue/book_stub_detail.html:17
+msgid ""
+"This work is in public domain and will be published on Internet school "
+"library of Wolne Lektury soon."
+msgstr ""
+
+#: templates/catalogue/book_stub_detail.html:20
+msgid ""
+"This work will become part of public domain and will be allowed to be "
+"published without restrictions in"
+msgstr ""
+
+#: templates/catalogue/book_stub_detail.html:22
+msgid "Find out why Internet libraries can't publish this work."
+msgstr ""
+
+#: templates/catalogue/book_stub_detail.html:24
+msgid "This work is copyrighted."
+msgstr ""
+
+#: templates/catalogue/book_text.html:17
+msgid "Table of contents"
+msgstr ""
+
+#: templates/catalogue/book_text.html:18
+#: templates/catalogue/tagged_object_list.html:132
+msgid "Themes"
+msgstr ""
+
+#: templates/catalogue/folded_tag_list.html:4
+msgid "Show full category"
+msgstr ""
+
+#: templates/catalogue/folded_tag_list.html:22
+#: templates/catalogue/main_page.html:250
+msgid "Hide"
+msgstr ""
+
+#: templates/catalogue/fragment_sets.html:2
+msgid "Shelves containing fragment"
+msgstr ""
+
+#: templates/catalogue/fragment_sets.html:4
+#: templates/catalogue/main_page.html:28
+msgid "You do not own any shelves. You can create one below, if you want to."
+msgstr ""
+
+#: templates/catalogue/fragment_sets.html:9
+msgid "Save all shelves"
+msgstr ""
+
+#: templates/catalogue/fragment_short.html:6
+msgid "Expand fragment"
+msgstr ""
+
+#: templates/catalogue/fragment_short.html:12
+msgid "Hide fragment"
+msgstr ""
+
+#: templates/catalogue/fragment_short.html:17
+msgid "See in a book"
+msgstr ""
+
+#: templates/catalogue/main_page.html:13
+msgid "check list of books"
+msgstr ""
+
+#: templates/catalogue/main_page.html:13
+msgid "in our repository"
+msgstr ""
+
+#: templates/catalogue/main_page.html:17
+msgid "Browse books by categories"
+msgstr ""
+
+#: templates/catalogue/main_page.html:19
+#: templates/catalogue/user_shelves.html:2
+msgid "Your shelves with books"
+msgstr ""
+
+#: templates/catalogue/main_page.html:24
+msgid "delete"
+msgstr ""
+
+#: templates/catalogue/main_page.html:33
+#: templates/catalogue/user_shelves.html:15
+msgid "Create shelf"
+msgstr ""
+
+#: templates/catalogue/main_page.html:37
+msgid ""
+"Create your own book set. You can share it with friends by sending them link "
+"to your shelf."
+msgstr ""
+
+#: templates/catalogue/main_page.html:38
+msgid "You need to "
+msgstr ""
+
+#: templates/catalogue/main_page.html:38
+msgid "sign in"
+msgstr ""
+
+#: templates/catalogue/main_page.html:38
+msgid "to manage your shelves."
+msgstr ""
+
+#: templates/catalogue/main_page.html:41
+#: templates/lessons/document_list.html:49
+msgid "Hand-outs for teachers"
+msgstr ""
+
+#: templates/catalogue/main_page.html:42
+msgid ""
+"Lessons' prospects and other ideas for using Wolnelektury.pl for teaching."
+msgstr ""
+
+#: templates/catalogue/main_page.html:47
+msgid ""
+"are professional recordings of literary texts from our repository, available "
+"on free license in MP3 and Ogg Vorbis formats as well as in DAISY system."
+msgstr ""
+
+#: templates/catalogue/main_page.html:54
+#: templates/catalogue/tagged_object_list.html:114
+msgid "Authors"
+msgstr ""
+
+#: templates/catalogue/main_page.html:58
+#: templates/catalogue/tagged_object_list.html:118
+msgid "Kinds"
+msgstr ""
+
+#: templates/catalogue/main_page.html:62
+#: templates/catalogue/tagged_object_list.html:122
+msgid "Genres"
+msgstr ""
+
+#: templates/catalogue/main_page.html:66
+#: templates/catalogue/tagged_object_list.html:126
+msgid "Epochs"
+msgstr ""
+
+#: templates/catalogue/main_page.html:72
+msgid "Themes and topics"
+msgstr ""
+
+#: templates/catalogue/main_page.html:75
+msgid "Themes groups"
+msgstr ""
+
+#: templates/catalogue/main_page.html:260
+msgid "News"
+msgstr ""
+
+#: templates/catalogue/main_page.html:264
+msgid "See our blog"
+msgstr ""
+
+#: templates/catalogue/main_page.html:267
+msgid "You can help us!"
+msgstr ""
+
+#: templates/catalogue/main_page.html:268
+msgid ""
+"We try our best to elaborate works appended to our library. It is possible "
+"only due to support of our volunteers."
+msgstr ""
+
+#: templates/catalogue/main_page.html:269
+msgid ""
+"We invite people who want to take part in developing Internet school library "
+"Wolne Lektury."
+msgstr ""
+
+#: templates/catalogue/main_page.html:273
+msgid "About us"
+msgstr ""
+
+#: templates/catalogue/main_page.html:275
+msgid ""
+"\n"
+"\t\t\tInternet library with school readings “Wolne Lektury” (<a href="
+"\"http://wolnelektury.pl\">www.wolnelektury.pl</a>) is a project made by "
+"Modern Poland Foundation. It started in 2007 and shares school readings, "
+"which are recommended by Ministry of National Education and are in public "
+"domain.\n"
+"\t\t\t"
+msgstr ""
+
+#: templates/catalogue/search_no_hits.html:5
+msgid "Search in WolneLektury.pl"
+msgstr ""
+
+#: templates/catalogue/search_no_hits.html:15
+msgid "Sorry! Search cirteria did not match any resources."
+msgstr ""
+
+#: templates/catalogue/search_no_hits.html:17
+msgid ""
+"Search engine supports following criteria: title, author, theme/topic, "
+"epoch, kind and genre.\n"
+"\t\tAs for now we do not support full text search."
+msgstr ""
+
+#: templates/catalogue/tag_list.html:4
+msgid "See full category"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:15
+msgid "Your shelf is empty"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:16
+msgid ""
+"You can put a book on a shelf by entering page of the reading and clicking "
+"'Put on the shelf'."
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:31
+msgid "Download all books from this shelf"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:35
+msgid "Choose books' formats which you want to download:"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:36
+#: templates/catalogue/tagged_object_list.html:37
+#: templates/catalogue/tagged_object_list.html:38
+msgid "for reading"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:36
+msgid "and printing using"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:37
+msgid "and editing using"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:38
+msgid "on small displays, for example mobile phones"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:39
+#: templates/catalogue/tagged_object_list.html:40
+msgid "for listening"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:39
+msgid "on favourite MP3 player"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:40
+msgid "open format"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:40
+msgid "Xiph.org Foundation"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:41
+#: templates/lessons/ajax_document_detail.html:3
+#: templates/lessons/document_detail.html:13
+msgid "Download"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:41
+msgid "Updating list of books' formats on the shelf"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:41
+msgid "cancel"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:46
+msgid "Share this shelf"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:48
+msgid ""
+"Copy this link and share it with other people to let them see your shelf."
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:57
+msgid "Read work's study of this author on Lektury.Gazeta.pl"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:60
+msgid "Read study of epoch"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:60
+msgid "on Lektury.Gazeta.pl"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:67
+msgid "Read article about this author on Wikipedia"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:70
+msgid "Read article about epoch"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:70
+msgid "on Wikipedia"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:80
+msgid "Delete"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:88
+msgid "This author's works are copyrighted."
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:91
+msgid ""
+"This author's works are in public domain and will be published on Internet "
+"school library of Wolne Lektury soon."
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:95
+msgid ""
+"This author's works will become part of public domain and will be allowed to "
+"be published without restrictions in"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:97
+msgid "Find out why Internet libraries can't publish this author's works."
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:102
+msgid "No works of this author found."
+msgstr ""
+
+#: templates/catalogue/user_shelves.html:6
+msgid "remove"
+msgstr ""
+
+#: templates/catalogue/user_shelves.html:10
+msgid "You do not own any shelves. You can create one below if you want to"
+msgstr ""
+
+#: templates/lessons/ajax_document_detail.html:3
+#: templates/lessons/document_detail.html:13
+msgid "author"
+msgstr ""
+
+#: templates/lessons/document_detail.html:9
+msgid "return to list of materials"
+msgstr ""
+
+#: templates/lessons/document_list.html:7
+msgid "Hand-outs for teachers on "
+msgstr ""
+
+#: templates/pagination/pagination.html:5
+#: templates/pagination/pagination.html:7
+msgid "previous"
+msgstr ""
+
+#: templates/pagination/pagination.html:21
+#: templates/pagination/pagination.html:23
+msgid "next"
+msgstr ""
diff --git a/wolnelektury/locale/es/LC_MESSAGES/django.mo b/wolnelektury/locale/es/LC_MESSAGES/django.mo
new file mode 100644 (file)
index 0000000..c757a97
Binary files /dev/null and b/wolnelektury/locale/es/LC_MESSAGES/django.mo differ
diff --git a/wolnelektury/locale/es/LC_MESSAGES/django.po b/wolnelektury/locale/es/LC_MESSAGES/django.po
new file mode 100644 (file)
index 0000000..fbd8e43
--- /dev/null
@@ -0,0 +1,737 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2010-05-19 15:39+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: settings.py:37
+msgid "German"
+msgstr ""
+
+#: settings.py:38
+msgid "English"
+msgstr ""
+
+#: settings.py:39
+msgid "Polish"
+msgstr ""
+
+#: settings.py:40
+msgid "Lithuanian"
+msgstr ""
+
+#: settings.py:41
+msgid "French"
+msgstr ""
+
+#: settings.py:42
+msgid "Russian"
+msgstr ""
+
+#: settings.py:43
+msgid "Spanish"
+msgstr ""
+
+#: templates/404.html:6 templates/404.html.py:15
+msgid "Page does not exist"
+msgstr ""
+
+#: templates/404.html:17
+msgid ""
+"We are sorry, but this page does not exist. Please check if you entered "
+"correct address or go to "
+msgstr ""
+
+#: templates/404.html:17
+msgid "main page"
+msgstr ""
+
+#: templates/500.html:6 templates/500.html.py:54
+msgid "Server error"
+msgstr ""
+
+#: templates/500.html:55
+msgid ""
+"<p>The Wolnelektury.pl site is currently unavailable. Meanwhile, visit our "
+"<a href='http://nowoczesnapolska.org.pl'>blog</a>.</p> <p>Inform our <a "
+"href='mailto:fundacja@nowoczesnapolska.org.pl'>administrators</a> about the "
+"error.</p>"
+msgstr ""
+
+#: templates/503.html:6 templates/503.html.py:54
+msgid "Service unavailable"
+msgstr ""
+
+#: templates/503.html:56
+msgid "The Wolnelektury.pl site is currently unavailable due to maintainance."
+msgstr ""
+
+#: templates/base.html:20
+msgid ""
+"Internet Explorer cannot display this site properly. Click here to read "
+"more..."
+msgstr ""
+
+#: templates/base.html:28 templates/catalogue/folded_tag_list.html:13
+#: templates/catalogue/main_page.html:43 templates/catalogue/main_page.html:48
+#: templates/catalogue/main_page.html:87
+#: templates/catalogue/main_page.html:270
+#: templates/catalogue/main_page.html:279
+msgid "See more"
+msgstr ""
+
+#: templates/base.html:37
+msgid "Welcome"
+msgstr ""
+
+#: templates/base.html:38
+msgid "Your shelves"
+msgstr ""
+
+#: templates/base.html:40
+msgid "Administration"
+msgstr ""
+
+#: templates/base.html:42
+msgid "Logout"
+msgstr ""
+
+#: templates/base.html:45 templates/base.html.py:91 templates/base.html:95
+#: templates/base.html.py:99 templates/auth/login.html:4
+#: templates/auth/login.html.py:7 templates/auth/login.html:12
+#: templates/auth/login.html.py:15
+msgid "Sign in"
+msgstr ""
+
+#: templates/base.html:45 templates/base.html.py:91 templates/base.html:99
+#: templates/base.html.py:103 templates/auth/login.html:7
+#: templates/auth/login.html.py:21 templates/auth/login.html:23
+msgid "Register"
+msgstr ""
+
+#: templates/base.html:55
+msgid "Choose your interface language: "
+msgstr ""
+
+#: templates/base.html:60
+msgid "Choose language"
+msgstr ""
+
+#: templates/base.html:72
+msgid ""
+"\n"
+"\t\t\t\tWolne Lektury is a project lead by <a href=\"http://nowoczesnapolska."
+"org.pl/\">Modern Poland Foundation</a>.\n"
+"\t\t\t\tDigital reproductions are made by <a href=\"http://www.bn.org.pl/"
+"\">The National Library</a>, based on TNL resources. \n"
+"\t\t\t\tHosting <a href=\"http://eo.pl/\">EO Networks</a>.\n"
+"\t\t\t\t"
+msgstr ""
+
+#: templates/base.html:79
+msgid ""
+"\n"
+"\t\t\t\tModern Poland Foundation, 00-514 Warsaw, ul. Marszałkowska 84/92 "
+"lok. 125, tel/fax: (22) 621-30-17\n"
+"                e-mail: <a href=\"mailto:fundacja@nowoczesnapolska.org.pl"
+"\">fundacja@nowoczesnapolska.org.pl</a>\n"
+"\t\t\t\t"
+msgstr ""
+
+#: templates/base.html:88 templates/base.html.py:109
+#: templates/catalogue/book_detail.html:129
+#: templates/catalogue/book_fragments.html:33
+#: templates/catalogue/book_stub_detail.html:31
+#: templates/catalogue/search_no_hits.html:23
+#: templates/catalogue/tagged_object_list.html:141
+msgid "Close"
+msgstr ""
+
+#: templates/base.html:111 templates/catalogue/book_detail.html:131
+#: templates/catalogue/book_fragments.html:35
+#: templates/catalogue/book_stub_detail.html:33
+#: templates/catalogue/search_no_hits.html:25
+#: templates/catalogue/tagged_object_list.html:143
+msgid "Loading"
+msgstr ""
+
+#: templates/admin/base_site.html:4 templates/admin/base_site.html.py:7
+msgid "Site administration"
+msgstr ""
+
+#: templates/admin/base_site.html:8
+msgid "Translations"
+msgstr ""
+
+#: templates/admin/catalogue/book/change_list.html:6
+msgid "Import book"
+msgstr ""
+
+#: templates/auth/login.html:4
+msgid "Register on"
+msgstr ""
+
+#: templates/auth/login.html:9 templates/catalogue/book_detail.html:12
+#: templates/catalogue/book_fragments.html:12
+#: templates/catalogue/book_list.html:12
+#: templates/catalogue/book_stub_detail.html:12
+#: templates/catalogue/breadcrumbs.html:9
+#: templates/catalogue/main_page.html:13
+#: templates/lessons/document_detail.html:9
+#: templates/lessons/document_list.html:51
+msgid "Search"
+msgstr ""
+
+#: templates/auth/login.html:9 templates/catalogue/book_detail.html:12
+#: templates/catalogue/book_fragments.html:12
+#: templates/catalogue/book_list.html:12
+#: templates/catalogue/book_stub_detail.html:12
+#: templates/catalogue/main_page.html:13
+#: templates/catalogue/tagged_object_list.html:41
+#: templates/lessons/document_detail.html:9
+#: templates/lessons/document_list.html:51
+msgid "or"
+msgstr ""
+
+#: templates/auth/login.html:9 templates/catalogue/book_detail.html:12
+#: templates/catalogue/book_list.html:12
+#: templates/catalogue/book_stub_detail.html:12
+#: templates/lessons/document_list.html:51
+msgid "return to main page"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:5
+msgid "on WolneLektury.pl"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:17
+msgid "Work is licensed under "
+msgstr ""
+
+#: templates/catalogue/book_detail.html:19
+msgid "Based on"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:24
+#: templates/catalogue/tagged_object_list.html:27
+msgid "Hide description"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:27
+msgid "Put a book"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:27
+msgid "on the shelf!"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:31
+msgid "Read online"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:34
+msgid "Download PDF"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:37
+msgid "Download ODT"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:40
+msgid "Download TXT"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:45
+msgid "Artist"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:47
+msgid "Director"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:51
+msgid "Download MP3"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:52
+msgid "Download Ogg Vorbis"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:79
+msgid "Details"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:82
+msgid "Author"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:88
+msgid "Epoch"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:94
+msgid "Kind"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:100
+msgid "Genre"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:106
+msgid "Other resources"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:108
+msgid "Book on project's wiki"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:109
+msgid "Source of the book"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:111
+msgid "Book description on Lektury.Gazeta.pl"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:114
+msgid "Book description on Wikipedia"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:119
+msgid "Work's themes "
+msgstr ""
+
+#: templates/catalogue/book_fragments.html:5
+#: templates/catalogue/book_fragments.html:10
+msgid "Theme"
+msgstr ""
+
+#: templates/catalogue/book_fragments.html:5
+#: templates/catalogue/book_fragments.html:10
+msgid "in work "
+msgstr ""
+
+#: templates/catalogue/book_fragments.html:12
+msgid "return to book's page"
+msgstr ""
+
+#: templates/catalogue/book_fragments.html:26
+msgid "See description"
+msgstr ""
+
+#: templates/catalogue/book_fragments.html:26
+msgid "of the book "
+msgstr ""
+
+#: templates/catalogue/book_list.html:7
+msgid "Alphabetical listing of works on WolneLektury.pl"
+msgstr ""
+
+#: templates/catalogue/book_list.html:10
+msgid "Alphabetical listing of works"
+msgstr ""
+
+#: templates/catalogue/book_sets.html:2
+msgid "Put a book on the shelf!"
+msgstr ""
+
+#: templates/catalogue/book_sets.html:4
+msgid "You do not have any shelves. You can create one below, if you want to."
+msgstr ""
+
+#: templates/catalogue/book_sets.html:9 templates/catalogue/book_short.html:4
+msgid "Put on the shelf!"
+msgstr ""
+
+#: templates/catalogue/book_sets.html:16
+#: templates/catalogue/fragment_sets.html:16
+msgid "Create new shelf"
+msgstr ""
+
+#: templates/catalogue/book_short.html:14
+msgid "Jump to"
+msgstr ""
+
+#: templates/catalogue/book_short.html:16
+msgid "Categories"
+msgstr ""
+
+#: templates/catalogue/book_stub_detail.html:17
+msgid ""
+"This work is in public domain and will be published on Internet school "
+"library of Wolne Lektury soon."
+msgstr ""
+
+#: templates/catalogue/book_stub_detail.html:20
+msgid ""
+"This work will become part of public domain and will be allowed to be "
+"published without restrictions in"
+msgstr ""
+
+#: templates/catalogue/book_stub_detail.html:22
+msgid "Find out why Internet libraries can't publish this work."
+msgstr ""
+
+#: templates/catalogue/book_stub_detail.html:24
+msgid "This work is copyrighted."
+msgstr ""
+
+#: templates/catalogue/book_text.html:17
+msgid "Table of contents"
+msgstr ""
+
+#: templates/catalogue/book_text.html:18
+#: templates/catalogue/tagged_object_list.html:132
+msgid "Themes"
+msgstr ""
+
+#: templates/catalogue/folded_tag_list.html:4
+msgid "Show full category"
+msgstr ""
+
+#: templates/catalogue/folded_tag_list.html:22
+#: templates/catalogue/main_page.html:250
+msgid "Hide"
+msgstr ""
+
+#: templates/catalogue/fragment_sets.html:2
+msgid "Shelves containing fragment"
+msgstr ""
+
+#: templates/catalogue/fragment_sets.html:4
+#: templates/catalogue/main_page.html:28
+msgid "You do not own any shelves. You can create one below, if you want to."
+msgstr ""
+
+#: templates/catalogue/fragment_sets.html:9
+msgid "Save all shelves"
+msgstr ""
+
+#: templates/catalogue/fragment_short.html:6
+msgid "Expand fragment"
+msgstr ""
+
+#: templates/catalogue/fragment_short.html:12
+msgid "Hide fragment"
+msgstr ""
+
+#: templates/catalogue/fragment_short.html:17
+msgid "See in a book"
+msgstr ""
+
+#: templates/catalogue/main_page.html:13
+msgid "check list of books"
+msgstr ""
+
+#: templates/catalogue/main_page.html:13
+msgid "in our repository"
+msgstr ""
+
+#: templates/catalogue/main_page.html:17
+msgid "Browse books by categories"
+msgstr ""
+
+#: templates/catalogue/main_page.html:19
+#: templates/catalogue/user_shelves.html:2
+msgid "Your shelves with books"
+msgstr ""
+
+#: templates/catalogue/main_page.html:24
+msgid "delete"
+msgstr ""
+
+#: templates/catalogue/main_page.html:33
+#: templates/catalogue/user_shelves.html:15
+msgid "Create shelf"
+msgstr ""
+
+#: templates/catalogue/main_page.html:37
+msgid ""
+"Create your own book set. You can share it with friends by sending them link "
+"to your shelf."
+msgstr ""
+
+#: templates/catalogue/main_page.html:38
+msgid "You need to "
+msgstr ""
+
+#: templates/catalogue/main_page.html:38
+msgid "sign in"
+msgstr ""
+
+#: templates/catalogue/main_page.html:38
+msgid "to manage your shelves."
+msgstr ""
+
+#: templates/catalogue/main_page.html:41
+#: templates/lessons/document_list.html:49
+msgid "Hand-outs for teachers"
+msgstr ""
+
+#: templates/catalogue/main_page.html:42
+msgid ""
+"Lessons' prospects and other ideas for using Wolnelektury.pl for teaching."
+msgstr ""
+
+#: templates/catalogue/main_page.html:47
+msgid ""
+"are professional recordings of literary texts from our repository, available "
+"on free license in MP3 and Ogg Vorbis formats as well as in DAISY system."
+msgstr ""
+
+#: templates/catalogue/main_page.html:54
+#: templates/catalogue/tagged_object_list.html:114
+msgid "Authors"
+msgstr ""
+
+#: templates/catalogue/main_page.html:58
+#: templates/catalogue/tagged_object_list.html:118
+msgid "Kinds"
+msgstr ""
+
+#: templates/catalogue/main_page.html:62
+#: templates/catalogue/tagged_object_list.html:122
+msgid "Genres"
+msgstr ""
+
+#: templates/catalogue/main_page.html:66
+#: templates/catalogue/tagged_object_list.html:126
+msgid "Epochs"
+msgstr ""
+
+#: templates/catalogue/main_page.html:72
+msgid "Themes and topics"
+msgstr ""
+
+#: templates/catalogue/main_page.html:75
+msgid "Themes groups"
+msgstr ""
+
+#: templates/catalogue/main_page.html:260
+msgid "News"
+msgstr ""
+
+#: templates/catalogue/main_page.html:264
+msgid "See our blog"
+msgstr ""
+
+#: templates/catalogue/main_page.html:267
+msgid "You can help us!"
+msgstr ""
+
+#: templates/catalogue/main_page.html:268
+msgid ""
+"We try our best to elaborate works appended to our library. It is possible "
+"only due to support of our volunteers."
+msgstr ""
+
+#: templates/catalogue/main_page.html:269
+msgid ""
+"We invite people who want to take part in developing Internet school library "
+"Wolne Lektury."
+msgstr ""
+
+#: templates/catalogue/main_page.html:273
+msgid "About us"
+msgstr ""
+
+#: templates/catalogue/main_page.html:275
+msgid ""
+"\n"
+"\t\t\tInternet library with school readings “Wolne Lektury” (<a href="
+"\"http://wolnelektury.pl\">www.wolnelektury.pl</a>) is a project made by "
+"Modern Poland Foundation. It started in 2007 and shares school readings, "
+"which are recommended by Ministry of National Education and are in public "
+"domain.\n"
+"\t\t\t"
+msgstr ""
+
+#: templates/catalogue/search_no_hits.html:5
+msgid "Search in WolneLektury.pl"
+msgstr ""
+
+#: templates/catalogue/search_no_hits.html:15
+msgid "Sorry! Search cirteria did not match any resources."
+msgstr ""
+
+#: templates/catalogue/search_no_hits.html:17
+msgid ""
+"Search engine supports following criteria: title, author, theme/topic, "
+"epoch, kind and genre.\n"
+"\t\tAs for now we do not support full text search."
+msgstr ""
+
+#: templates/catalogue/tag_list.html:4
+msgid "See full category"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:15
+msgid "Your shelf is empty"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:16
+msgid ""
+"You can put a book on a shelf by entering page of the reading and clicking "
+"'Put on the shelf'."
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:31
+msgid "Download all books from this shelf"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:35
+msgid "Choose books' formats which you want to download:"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:36
+#: templates/catalogue/tagged_object_list.html:37
+#: templates/catalogue/tagged_object_list.html:38
+msgid "for reading"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:36
+msgid "and printing using"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:37
+msgid "and editing using"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:38
+msgid "on small displays, for example mobile phones"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:39
+#: templates/catalogue/tagged_object_list.html:40
+msgid "for listening"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:39
+msgid "on favourite MP3 player"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:40
+msgid "open format"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:40
+msgid "Xiph.org Foundation"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:41
+#: templates/lessons/ajax_document_detail.html:3
+#: templates/lessons/document_detail.html:13
+msgid "Download"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:41
+msgid "Updating list of books' formats on the shelf"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:41
+msgid "cancel"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:46
+msgid "Share this shelf"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:48
+msgid ""
+"Copy this link and share it with other people to let them see your shelf."
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:57
+msgid "Read work's study of this author on Lektury.Gazeta.pl"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:60
+msgid "Read study of epoch"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:60
+msgid "on Lektury.Gazeta.pl"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:67
+msgid "Read article about this author on Wikipedia"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:70
+msgid "Read article about epoch"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:70
+msgid "on Wikipedia"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:80
+msgid "Delete"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:88
+msgid "This author's works are copyrighted."
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:91
+msgid ""
+"This author's works are in public domain and will be published on Internet "
+"school library of Wolne Lektury soon."
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:95
+msgid ""
+"This author's works will become part of public domain and will be allowed to "
+"be published without restrictions in"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:97
+msgid "Find out why Internet libraries can't publish this author's works."
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:102
+msgid "No works of this author found."
+msgstr ""
+
+#: templates/catalogue/user_shelves.html:6
+msgid "remove"
+msgstr ""
+
+#: templates/catalogue/user_shelves.html:10
+msgid "You do not own any shelves. You can create one below if you want to"
+msgstr ""
+
+#: templates/lessons/ajax_document_detail.html:3
+#: templates/lessons/document_detail.html:13
+msgid "author"
+msgstr ""
+
+#: templates/lessons/document_detail.html:9
+msgid "return to list of materials"
+msgstr ""
+
+#: templates/lessons/document_list.html:7
+msgid "Hand-outs for teachers on "
+msgstr ""
+
+#: templates/pagination/pagination.html:5
+#: templates/pagination/pagination.html:7
+msgid "previous"
+msgstr ""
+
+#: templates/pagination/pagination.html:21
+#: templates/pagination/pagination.html:23
+msgid "next"
+msgstr ""
diff --git a/wolnelektury/locale/fr/LC_MESSAGES/django.mo b/wolnelektury/locale/fr/LC_MESSAGES/django.mo
new file mode 100644 (file)
index 0000000..c757a97
Binary files /dev/null and b/wolnelektury/locale/fr/LC_MESSAGES/django.mo differ
diff --git a/wolnelektury/locale/fr/LC_MESSAGES/django.po b/wolnelektury/locale/fr/LC_MESSAGES/django.po
new file mode 100644 (file)
index 0000000..fbd8e43
--- /dev/null
@@ -0,0 +1,737 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2010-05-19 15:39+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: settings.py:37
+msgid "German"
+msgstr ""
+
+#: settings.py:38
+msgid "English"
+msgstr ""
+
+#: settings.py:39
+msgid "Polish"
+msgstr ""
+
+#: settings.py:40
+msgid "Lithuanian"
+msgstr ""
+
+#: settings.py:41
+msgid "French"
+msgstr ""
+
+#: settings.py:42
+msgid "Russian"
+msgstr ""
+
+#: settings.py:43
+msgid "Spanish"
+msgstr ""
+
+#: templates/404.html:6 templates/404.html.py:15
+msgid "Page does not exist"
+msgstr ""
+
+#: templates/404.html:17
+msgid ""
+"We are sorry, but this page does not exist. Please check if you entered "
+"correct address or go to "
+msgstr ""
+
+#: templates/404.html:17
+msgid "main page"
+msgstr ""
+
+#: templates/500.html:6 templates/500.html.py:54
+msgid "Server error"
+msgstr ""
+
+#: templates/500.html:55
+msgid ""
+"<p>The Wolnelektury.pl site is currently unavailable. Meanwhile, visit our "
+"<a href='http://nowoczesnapolska.org.pl'>blog</a>.</p> <p>Inform our <a "
+"href='mailto:fundacja@nowoczesnapolska.org.pl'>administrators</a> about the "
+"error.</p>"
+msgstr ""
+
+#: templates/503.html:6 templates/503.html.py:54
+msgid "Service unavailable"
+msgstr ""
+
+#: templates/503.html:56
+msgid "The Wolnelektury.pl site is currently unavailable due to maintainance."
+msgstr ""
+
+#: templates/base.html:20
+msgid ""
+"Internet Explorer cannot display this site properly. Click here to read "
+"more..."
+msgstr ""
+
+#: templates/base.html:28 templates/catalogue/folded_tag_list.html:13
+#: templates/catalogue/main_page.html:43 templates/catalogue/main_page.html:48
+#: templates/catalogue/main_page.html:87
+#: templates/catalogue/main_page.html:270
+#: templates/catalogue/main_page.html:279
+msgid "See more"
+msgstr ""
+
+#: templates/base.html:37
+msgid "Welcome"
+msgstr ""
+
+#: templates/base.html:38
+msgid "Your shelves"
+msgstr ""
+
+#: templates/base.html:40
+msgid "Administration"
+msgstr ""
+
+#: templates/base.html:42
+msgid "Logout"
+msgstr ""
+
+#: templates/base.html:45 templates/base.html.py:91 templates/base.html:95
+#: templates/base.html.py:99 templates/auth/login.html:4
+#: templates/auth/login.html.py:7 templates/auth/login.html:12
+#: templates/auth/login.html.py:15
+msgid "Sign in"
+msgstr ""
+
+#: templates/base.html:45 templates/base.html.py:91 templates/base.html:99
+#: templates/base.html.py:103 templates/auth/login.html:7
+#: templates/auth/login.html.py:21 templates/auth/login.html:23
+msgid "Register"
+msgstr ""
+
+#: templates/base.html:55
+msgid "Choose your interface language: "
+msgstr ""
+
+#: templates/base.html:60
+msgid "Choose language"
+msgstr ""
+
+#: templates/base.html:72
+msgid ""
+"\n"
+"\t\t\t\tWolne Lektury is a project lead by <a href=\"http://nowoczesnapolska."
+"org.pl/\">Modern Poland Foundation</a>.\n"
+"\t\t\t\tDigital reproductions are made by <a href=\"http://www.bn.org.pl/"
+"\">The National Library</a>, based on TNL resources. \n"
+"\t\t\t\tHosting <a href=\"http://eo.pl/\">EO Networks</a>.\n"
+"\t\t\t\t"
+msgstr ""
+
+#: templates/base.html:79
+msgid ""
+"\n"
+"\t\t\t\tModern Poland Foundation, 00-514 Warsaw, ul. Marszałkowska 84/92 "
+"lok. 125, tel/fax: (22) 621-30-17\n"
+"                e-mail: <a href=\"mailto:fundacja@nowoczesnapolska.org.pl"
+"\">fundacja@nowoczesnapolska.org.pl</a>\n"
+"\t\t\t\t"
+msgstr ""
+
+#: templates/base.html:88 templates/base.html.py:109
+#: templates/catalogue/book_detail.html:129
+#: templates/catalogue/book_fragments.html:33
+#: templates/catalogue/book_stub_detail.html:31
+#: templates/catalogue/search_no_hits.html:23
+#: templates/catalogue/tagged_object_list.html:141
+msgid "Close"
+msgstr ""
+
+#: templates/base.html:111 templates/catalogue/book_detail.html:131
+#: templates/catalogue/book_fragments.html:35
+#: templates/catalogue/book_stub_detail.html:33
+#: templates/catalogue/search_no_hits.html:25
+#: templates/catalogue/tagged_object_list.html:143
+msgid "Loading"
+msgstr ""
+
+#: templates/admin/base_site.html:4 templates/admin/base_site.html.py:7
+msgid "Site administration"
+msgstr ""
+
+#: templates/admin/base_site.html:8
+msgid "Translations"
+msgstr ""
+
+#: templates/admin/catalogue/book/change_list.html:6
+msgid "Import book"
+msgstr ""
+
+#: templates/auth/login.html:4
+msgid "Register on"
+msgstr ""
+
+#: templates/auth/login.html:9 templates/catalogue/book_detail.html:12
+#: templates/catalogue/book_fragments.html:12
+#: templates/catalogue/book_list.html:12
+#: templates/catalogue/book_stub_detail.html:12
+#: templates/catalogue/breadcrumbs.html:9
+#: templates/catalogue/main_page.html:13
+#: templates/lessons/document_detail.html:9
+#: templates/lessons/document_list.html:51
+msgid "Search"
+msgstr ""
+
+#: templates/auth/login.html:9 templates/catalogue/book_detail.html:12
+#: templates/catalogue/book_fragments.html:12
+#: templates/catalogue/book_list.html:12
+#: templates/catalogue/book_stub_detail.html:12
+#: templates/catalogue/main_page.html:13
+#: templates/catalogue/tagged_object_list.html:41
+#: templates/lessons/document_detail.html:9
+#: templates/lessons/document_list.html:51
+msgid "or"
+msgstr ""
+
+#: templates/auth/login.html:9 templates/catalogue/book_detail.html:12
+#: templates/catalogue/book_list.html:12
+#: templates/catalogue/book_stub_detail.html:12
+#: templates/lessons/document_list.html:51
+msgid "return to main page"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:5
+msgid "on WolneLektury.pl"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:17
+msgid "Work is licensed under "
+msgstr ""
+
+#: templates/catalogue/book_detail.html:19
+msgid "Based on"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:24
+#: templates/catalogue/tagged_object_list.html:27
+msgid "Hide description"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:27
+msgid "Put a book"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:27
+msgid "on the shelf!"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:31
+msgid "Read online"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:34
+msgid "Download PDF"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:37
+msgid "Download ODT"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:40
+msgid "Download TXT"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:45
+msgid "Artist"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:47
+msgid "Director"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:51
+msgid "Download MP3"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:52
+msgid "Download Ogg Vorbis"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:79
+msgid "Details"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:82
+msgid "Author"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:88
+msgid "Epoch"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:94
+msgid "Kind"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:100
+msgid "Genre"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:106
+msgid "Other resources"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:108
+msgid "Book on project's wiki"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:109
+msgid "Source of the book"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:111
+msgid "Book description on Lektury.Gazeta.pl"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:114
+msgid "Book description on Wikipedia"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:119
+msgid "Work's themes "
+msgstr ""
+
+#: templates/catalogue/book_fragments.html:5
+#: templates/catalogue/book_fragments.html:10
+msgid "Theme"
+msgstr ""
+
+#: templates/catalogue/book_fragments.html:5
+#: templates/catalogue/book_fragments.html:10
+msgid "in work "
+msgstr ""
+
+#: templates/catalogue/book_fragments.html:12
+msgid "return to book's page"
+msgstr ""
+
+#: templates/catalogue/book_fragments.html:26
+msgid "See description"
+msgstr ""
+
+#: templates/catalogue/book_fragments.html:26
+msgid "of the book "
+msgstr ""
+
+#: templates/catalogue/book_list.html:7
+msgid "Alphabetical listing of works on WolneLektury.pl"
+msgstr ""
+
+#: templates/catalogue/book_list.html:10
+msgid "Alphabetical listing of works"
+msgstr ""
+
+#: templates/catalogue/book_sets.html:2
+msgid "Put a book on the shelf!"
+msgstr ""
+
+#: templates/catalogue/book_sets.html:4
+msgid "You do not have any shelves. You can create one below, if you want to."
+msgstr ""
+
+#: templates/catalogue/book_sets.html:9 templates/catalogue/book_short.html:4
+msgid "Put on the shelf!"
+msgstr ""
+
+#: templates/catalogue/book_sets.html:16
+#: templates/catalogue/fragment_sets.html:16
+msgid "Create new shelf"
+msgstr ""
+
+#: templates/catalogue/book_short.html:14
+msgid "Jump to"
+msgstr ""
+
+#: templates/catalogue/book_short.html:16
+msgid "Categories"
+msgstr ""
+
+#: templates/catalogue/book_stub_detail.html:17
+msgid ""
+"This work is in public domain and will be published on Internet school "
+"library of Wolne Lektury soon."
+msgstr ""
+
+#: templates/catalogue/book_stub_detail.html:20
+msgid ""
+"This work will become part of public domain and will be allowed to be "
+"published without restrictions in"
+msgstr ""
+
+#: templates/catalogue/book_stub_detail.html:22
+msgid "Find out why Internet libraries can't publish this work."
+msgstr ""
+
+#: templates/catalogue/book_stub_detail.html:24
+msgid "This work is copyrighted."
+msgstr ""
+
+#: templates/catalogue/book_text.html:17
+msgid "Table of contents"
+msgstr ""
+
+#: templates/catalogue/book_text.html:18
+#: templates/catalogue/tagged_object_list.html:132
+msgid "Themes"
+msgstr ""
+
+#: templates/catalogue/folded_tag_list.html:4
+msgid "Show full category"
+msgstr ""
+
+#: templates/catalogue/folded_tag_list.html:22
+#: templates/catalogue/main_page.html:250
+msgid "Hide"
+msgstr ""
+
+#: templates/catalogue/fragment_sets.html:2
+msgid "Shelves containing fragment"
+msgstr ""
+
+#: templates/catalogue/fragment_sets.html:4
+#: templates/catalogue/main_page.html:28
+msgid "You do not own any shelves. You can create one below, if you want to."
+msgstr ""
+
+#: templates/catalogue/fragment_sets.html:9
+msgid "Save all shelves"
+msgstr ""
+
+#: templates/catalogue/fragment_short.html:6
+msgid "Expand fragment"
+msgstr ""
+
+#: templates/catalogue/fragment_short.html:12
+msgid "Hide fragment"
+msgstr ""
+
+#: templates/catalogue/fragment_short.html:17
+msgid "See in a book"
+msgstr ""
+
+#: templates/catalogue/main_page.html:13
+msgid "check list of books"
+msgstr ""
+
+#: templates/catalogue/main_page.html:13
+msgid "in our repository"
+msgstr ""
+
+#: templates/catalogue/main_page.html:17
+msgid "Browse books by categories"
+msgstr ""
+
+#: templates/catalogue/main_page.html:19
+#: templates/catalogue/user_shelves.html:2
+msgid "Your shelves with books"
+msgstr ""
+
+#: templates/catalogue/main_page.html:24
+msgid "delete"
+msgstr ""
+
+#: templates/catalogue/main_page.html:33
+#: templates/catalogue/user_shelves.html:15
+msgid "Create shelf"
+msgstr ""
+
+#: templates/catalogue/main_page.html:37
+msgid ""
+"Create your own book set. You can share it with friends by sending them link "
+"to your shelf."
+msgstr ""
+
+#: templates/catalogue/main_page.html:38
+msgid "You need to "
+msgstr ""
+
+#: templates/catalogue/main_page.html:38
+msgid "sign in"
+msgstr ""
+
+#: templates/catalogue/main_page.html:38
+msgid "to manage your shelves."
+msgstr ""
+
+#: templates/catalogue/main_page.html:41
+#: templates/lessons/document_list.html:49
+msgid "Hand-outs for teachers"
+msgstr ""
+
+#: templates/catalogue/main_page.html:42
+msgid ""
+"Lessons' prospects and other ideas for using Wolnelektury.pl for teaching."
+msgstr ""
+
+#: templates/catalogue/main_page.html:47
+msgid ""
+"are professional recordings of literary texts from our repository, available "
+"on free license in MP3 and Ogg Vorbis formats as well as in DAISY system."
+msgstr ""
+
+#: templates/catalogue/main_page.html:54
+#: templates/catalogue/tagged_object_list.html:114
+msgid "Authors"
+msgstr ""
+
+#: templates/catalogue/main_page.html:58
+#: templates/catalogue/tagged_object_list.html:118
+msgid "Kinds"
+msgstr ""
+
+#: templates/catalogue/main_page.html:62
+#: templates/catalogue/tagged_object_list.html:122
+msgid "Genres"
+msgstr ""
+
+#: templates/catalogue/main_page.html:66
+#: templates/catalogue/tagged_object_list.html:126
+msgid "Epochs"
+msgstr ""
+
+#: templates/catalogue/main_page.html:72
+msgid "Themes and topics"
+msgstr ""
+
+#: templates/catalogue/main_page.html:75
+msgid "Themes groups"
+msgstr ""
+
+#: templates/catalogue/main_page.html:260
+msgid "News"
+msgstr ""
+
+#: templates/catalogue/main_page.html:264
+msgid "See our blog"
+msgstr ""
+
+#: templates/catalogue/main_page.html:267
+msgid "You can help us!"
+msgstr ""
+
+#: templates/catalogue/main_page.html:268
+msgid ""
+"We try our best to elaborate works appended to our library. It is possible "
+"only due to support of our volunteers."
+msgstr ""
+
+#: templates/catalogue/main_page.html:269
+msgid ""
+"We invite people who want to take part in developing Internet school library "
+"Wolne Lektury."
+msgstr ""
+
+#: templates/catalogue/main_page.html:273
+msgid "About us"
+msgstr ""
+
+#: templates/catalogue/main_page.html:275
+msgid ""
+"\n"
+"\t\t\tInternet library with school readings “Wolne Lektury” (<a href="
+"\"http://wolnelektury.pl\">www.wolnelektury.pl</a>) is a project made by "
+"Modern Poland Foundation. It started in 2007 and shares school readings, "
+"which are recommended by Ministry of National Education and are in public "
+"domain.\n"
+"\t\t\t"
+msgstr ""
+
+#: templates/catalogue/search_no_hits.html:5
+msgid "Search in WolneLektury.pl"
+msgstr ""
+
+#: templates/catalogue/search_no_hits.html:15
+msgid "Sorry! Search cirteria did not match any resources."
+msgstr ""
+
+#: templates/catalogue/search_no_hits.html:17
+msgid ""
+"Search engine supports following criteria: title, author, theme/topic, "
+"epoch, kind and genre.\n"
+"\t\tAs for now we do not support full text search."
+msgstr ""
+
+#: templates/catalogue/tag_list.html:4
+msgid "See full category"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:15
+msgid "Your shelf is empty"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:16
+msgid ""
+"You can put a book on a shelf by entering page of the reading and clicking "
+"'Put on the shelf'."
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:31
+msgid "Download all books from this shelf"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:35
+msgid "Choose books' formats which you want to download:"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:36
+#: templates/catalogue/tagged_object_list.html:37
+#: templates/catalogue/tagged_object_list.html:38
+msgid "for reading"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:36
+msgid "and printing using"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:37
+msgid "and editing using"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:38
+msgid "on small displays, for example mobile phones"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:39
+#: templates/catalogue/tagged_object_list.html:40
+msgid "for listening"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:39
+msgid "on favourite MP3 player"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:40
+msgid "open format"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:40
+msgid "Xiph.org Foundation"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:41
+#: templates/lessons/ajax_document_detail.html:3
+#: templates/lessons/document_detail.html:13
+msgid "Download"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:41
+msgid "Updating list of books' formats on the shelf"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:41
+msgid "cancel"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:46
+msgid "Share this shelf"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:48
+msgid ""
+"Copy this link and share it with other people to let them see your shelf."
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:57
+msgid "Read work's study of this author on Lektury.Gazeta.pl"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:60
+msgid "Read study of epoch"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:60
+msgid "on Lektury.Gazeta.pl"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:67
+msgid "Read article about this author on Wikipedia"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:70
+msgid "Read article about epoch"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:70
+msgid "on Wikipedia"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:80
+msgid "Delete"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:88
+msgid "This author's works are copyrighted."
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:91
+msgid ""
+"This author's works are in public domain and will be published on Internet "
+"school library of Wolne Lektury soon."
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:95
+msgid ""
+"This author's works will become part of public domain and will be allowed to "
+"be published without restrictions in"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:97
+msgid "Find out why Internet libraries can't publish this author's works."
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:102
+msgid "No works of this author found."
+msgstr ""
+
+#: templates/catalogue/user_shelves.html:6
+msgid "remove"
+msgstr ""
+
+#: templates/catalogue/user_shelves.html:10
+msgid "You do not own any shelves. You can create one below if you want to"
+msgstr ""
+
+#: templates/lessons/ajax_document_detail.html:3
+#: templates/lessons/document_detail.html:13
+msgid "author"
+msgstr ""
+
+#: templates/lessons/document_detail.html:9
+msgid "return to list of materials"
+msgstr ""
+
+#: templates/lessons/document_list.html:7
+msgid "Hand-outs for teachers on "
+msgstr ""
+
+#: templates/pagination/pagination.html:5
+#: templates/pagination/pagination.html:7
+msgid "previous"
+msgstr ""
+
+#: templates/pagination/pagination.html:21
+#: templates/pagination/pagination.html:23
+msgid "next"
+msgstr ""
diff --git a/wolnelektury/locale/lt/LC_MESSAGES/django.mo b/wolnelektury/locale/lt/LC_MESSAGES/django.mo
new file mode 100644 (file)
index 0000000..c757a97
Binary files /dev/null and b/wolnelektury/locale/lt/LC_MESSAGES/django.mo differ
diff --git a/wolnelektury/locale/lt/LC_MESSAGES/django.po b/wolnelektury/locale/lt/LC_MESSAGES/django.po
new file mode 100644 (file)
index 0000000..fbd8e43
--- /dev/null
@@ -0,0 +1,737 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2010-05-19 15:39+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: settings.py:37
+msgid "German"
+msgstr ""
+
+#: settings.py:38
+msgid "English"
+msgstr ""
+
+#: settings.py:39
+msgid "Polish"
+msgstr ""
+
+#: settings.py:40
+msgid "Lithuanian"
+msgstr ""
+
+#: settings.py:41
+msgid "French"
+msgstr ""
+
+#: settings.py:42
+msgid "Russian"
+msgstr ""
+
+#: settings.py:43
+msgid "Spanish"
+msgstr ""
+
+#: templates/404.html:6 templates/404.html.py:15
+msgid "Page does not exist"
+msgstr ""
+
+#: templates/404.html:17
+msgid ""
+"We are sorry, but this page does not exist. Please check if you entered "
+"correct address or go to "
+msgstr ""
+
+#: templates/404.html:17
+msgid "main page"
+msgstr ""
+
+#: templates/500.html:6 templates/500.html.py:54
+msgid "Server error"
+msgstr ""
+
+#: templates/500.html:55
+msgid ""
+"<p>The Wolnelektury.pl site is currently unavailable. Meanwhile, visit our "
+"<a href='http://nowoczesnapolska.org.pl'>blog</a>.</p> <p>Inform our <a "
+"href='mailto:fundacja@nowoczesnapolska.org.pl'>administrators</a> about the "
+"error.</p>"
+msgstr ""
+
+#: templates/503.html:6 templates/503.html.py:54
+msgid "Service unavailable"
+msgstr ""
+
+#: templates/503.html:56
+msgid "The Wolnelektury.pl site is currently unavailable due to maintainance."
+msgstr ""
+
+#: templates/base.html:20
+msgid ""
+"Internet Explorer cannot display this site properly. Click here to read "
+"more..."
+msgstr ""
+
+#: templates/base.html:28 templates/catalogue/folded_tag_list.html:13
+#: templates/catalogue/main_page.html:43 templates/catalogue/main_page.html:48
+#: templates/catalogue/main_page.html:87
+#: templates/catalogue/main_page.html:270
+#: templates/catalogue/main_page.html:279
+msgid "See more"
+msgstr ""
+
+#: templates/base.html:37
+msgid "Welcome"
+msgstr ""
+
+#: templates/base.html:38
+msgid "Your shelves"
+msgstr ""
+
+#: templates/base.html:40
+msgid "Administration"
+msgstr ""
+
+#: templates/base.html:42
+msgid "Logout"
+msgstr ""
+
+#: templates/base.html:45 templates/base.html.py:91 templates/base.html:95
+#: templates/base.html.py:99 templates/auth/login.html:4
+#: templates/auth/login.html.py:7 templates/auth/login.html:12
+#: templates/auth/login.html.py:15
+msgid "Sign in"
+msgstr ""
+
+#: templates/base.html:45 templates/base.html.py:91 templates/base.html:99
+#: templates/base.html.py:103 templates/auth/login.html:7
+#: templates/auth/login.html.py:21 templates/auth/login.html:23
+msgid "Register"
+msgstr ""
+
+#: templates/base.html:55
+msgid "Choose your interface language: "
+msgstr ""
+
+#: templates/base.html:60
+msgid "Choose language"
+msgstr ""
+
+#: templates/base.html:72
+msgid ""
+"\n"
+"\t\t\t\tWolne Lektury is a project lead by <a href=\"http://nowoczesnapolska."
+"org.pl/\">Modern Poland Foundation</a>.\n"
+"\t\t\t\tDigital reproductions are made by <a href=\"http://www.bn.org.pl/"
+"\">The National Library</a>, based on TNL resources. \n"
+"\t\t\t\tHosting <a href=\"http://eo.pl/\">EO Networks</a>.\n"
+"\t\t\t\t"
+msgstr ""
+
+#: templates/base.html:79
+msgid ""
+"\n"
+"\t\t\t\tModern Poland Foundation, 00-514 Warsaw, ul. Marszałkowska 84/92 "
+"lok. 125, tel/fax: (22) 621-30-17\n"
+"                e-mail: <a href=\"mailto:fundacja@nowoczesnapolska.org.pl"
+"\">fundacja@nowoczesnapolska.org.pl</a>\n"
+"\t\t\t\t"
+msgstr ""
+
+#: templates/base.html:88 templates/base.html.py:109
+#: templates/catalogue/book_detail.html:129
+#: templates/catalogue/book_fragments.html:33
+#: templates/catalogue/book_stub_detail.html:31
+#: templates/catalogue/search_no_hits.html:23
+#: templates/catalogue/tagged_object_list.html:141
+msgid "Close"
+msgstr ""
+
+#: templates/base.html:111 templates/catalogue/book_detail.html:131
+#: templates/catalogue/book_fragments.html:35
+#: templates/catalogue/book_stub_detail.html:33
+#: templates/catalogue/search_no_hits.html:25
+#: templates/catalogue/tagged_object_list.html:143
+msgid "Loading"
+msgstr ""
+
+#: templates/admin/base_site.html:4 templates/admin/base_site.html.py:7
+msgid "Site administration"
+msgstr ""
+
+#: templates/admin/base_site.html:8
+msgid "Translations"
+msgstr ""
+
+#: templates/admin/catalogue/book/change_list.html:6
+msgid "Import book"
+msgstr ""
+
+#: templates/auth/login.html:4
+msgid "Register on"
+msgstr ""
+
+#: templates/auth/login.html:9 templates/catalogue/book_detail.html:12
+#: templates/catalogue/book_fragments.html:12
+#: templates/catalogue/book_list.html:12
+#: templates/catalogue/book_stub_detail.html:12
+#: templates/catalogue/breadcrumbs.html:9
+#: templates/catalogue/main_page.html:13
+#: templates/lessons/document_detail.html:9
+#: templates/lessons/document_list.html:51
+msgid "Search"
+msgstr ""
+
+#: templates/auth/login.html:9 templates/catalogue/book_detail.html:12
+#: templates/catalogue/book_fragments.html:12
+#: templates/catalogue/book_list.html:12
+#: templates/catalogue/book_stub_detail.html:12
+#: templates/catalogue/main_page.html:13
+#: templates/catalogue/tagged_object_list.html:41
+#: templates/lessons/document_detail.html:9
+#: templates/lessons/document_list.html:51
+msgid "or"
+msgstr ""
+
+#: templates/auth/login.html:9 templates/catalogue/book_detail.html:12
+#: templates/catalogue/book_list.html:12
+#: templates/catalogue/book_stub_detail.html:12
+#: templates/lessons/document_list.html:51
+msgid "return to main page"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:5
+msgid "on WolneLektury.pl"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:17
+msgid "Work is licensed under "
+msgstr ""
+
+#: templates/catalogue/book_detail.html:19
+msgid "Based on"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:24
+#: templates/catalogue/tagged_object_list.html:27
+msgid "Hide description"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:27
+msgid "Put a book"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:27
+msgid "on the shelf!"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:31
+msgid "Read online"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:34
+msgid "Download PDF"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:37
+msgid "Download ODT"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:40
+msgid "Download TXT"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:45
+msgid "Artist"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:47
+msgid "Director"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:51
+msgid "Download MP3"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:52
+msgid "Download Ogg Vorbis"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:79
+msgid "Details"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:82
+msgid "Author"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:88
+msgid "Epoch"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:94
+msgid "Kind"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:100
+msgid "Genre"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:106
+msgid "Other resources"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:108
+msgid "Book on project's wiki"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:109
+msgid "Source of the book"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:111
+msgid "Book description on Lektury.Gazeta.pl"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:114
+msgid "Book description on Wikipedia"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:119
+msgid "Work's themes "
+msgstr ""
+
+#: templates/catalogue/book_fragments.html:5
+#: templates/catalogue/book_fragments.html:10
+msgid "Theme"
+msgstr ""
+
+#: templates/catalogue/book_fragments.html:5
+#: templates/catalogue/book_fragments.html:10
+msgid "in work "
+msgstr ""
+
+#: templates/catalogue/book_fragments.html:12
+msgid "return to book's page"
+msgstr ""
+
+#: templates/catalogue/book_fragments.html:26
+msgid "See description"
+msgstr ""
+
+#: templates/catalogue/book_fragments.html:26
+msgid "of the book "
+msgstr ""
+
+#: templates/catalogue/book_list.html:7
+msgid "Alphabetical listing of works on WolneLektury.pl"
+msgstr ""
+
+#: templates/catalogue/book_list.html:10
+msgid "Alphabetical listing of works"
+msgstr ""
+
+#: templates/catalogue/book_sets.html:2
+msgid "Put a book on the shelf!"
+msgstr ""
+
+#: templates/catalogue/book_sets.html:4
+msgid "You do not have any shelves. You can create one below, if you want to."
+msgstr ""
+
+#: templates/catalogue/book_sets.html:9 templates/catalogue/book_short.html:4
+msgid "Put on the shelf!"
+msgstr ""
+
+#: templates/catalogue/book_sets.html:16
+#: templates/catalogue/fragment_sets.html:16
+msgid "Create new shelf"
+msgstr ""
+
+#: templates/catalogue/book_short.html:14
+msgid "Jump to"
+msgstr ""
+
+#: templates/catalogue/book_short.html:16
+msgid "Categories"
+msgstr ""
+
+#: templates/catalogue/book_stub_detail.html:17
+msgid ""
+"This work is in public domain and will be published on Internet school "
+"library of Wolne Lektury soon."
+msgstr ""
+
+#: templates/catalogue/book_stub_detail.html:20
+msgid ""
+"This work will become part of public domain and will be allowed to be "
+"published without restrictions in"
+msgstr ""
+
+#: templates/catalogue/book_stub_detail.html:22
+msgid "Find out why Internet libraries can't publish this work."
+msgstr ""
+
+#: templates/catalogue/book_stub_detail.html:24
+msgid "This work is copyrighted."
+msgstr ""
+
+#: templates/catalogue/book_text.html:17
+msgid "Table of contents"
+msgstr ""
+
+#: templates/catalogue/book_text.html:18
+#: templates/catalogue/tagged_object_list.html:132
+msgid "Themes"
+msgstr ""
+
+#: templates/catalogue/folded_tag_list.html:4
+msgid "Show full category"
+msgstr ""
+
+#: templates/catalogue/folded_tag_list.html:22
+#: templates/catalogue/main_page.html:250
+msgid "Hide"
+msgstr ""
+
+#: templates/catalogue/fragment_sets.html:2
+msgid "Shelves containing fragment"
+msgstr ""
+
+#: templates/catalogue/fragment_sets.html:4
+#: templates/catalogue/main_page.html:28
+msgid "You do not own any shelves. You can create one below, if you want to."
+msgstr ""
+
+#: templates/catalogue/fragment_sets.html:9
+msgid "Save all shelves"
+msgstr ""
+
+#: templates/catalogue/fragment_short.html:6
+msgid "Expand fragment"
+msgstr ""
+
+#: templates/catalogue/fragment_short.html:12
+msgid "Hide fragment"
+msgstr ""
+
+#: templates/catalogue/fragment_short.html:17
+msgid "See in a book"
+msgstr ""
+
+#: templates/catalogue/main_page.html:13
+msgid "check list of books"
+msgstr ""
+
+#: templates/catalogue/main_page.html:13
+msgid "in our repository"
+msgstr ""
+
+#: templates/catalogue/main_page.html:17
+msgid "Browse books by categories"
+msgstr ""
+
+#: templates/catalogue/main_page.html:19
+#: templates/catalogue/user_shelves.html:2
+msgid "Your shelves with books"
+msgstr ""
+
+#: templates/catalogue/main_page.html:24
+msgid "delete"
+msgstr ""
+
+#: templates/catalogue/main_page.html:33
+#: templates/catalogue/user_shelves.html:15
+msgid "Create shelf"
+msgstr ""
+
+#: templates/catalogue/main_page.html:37
+msgid ""
+"Create your own book set. You can share it with friends by sending them link "
+"to your shelf."
+msgstr ""
+
+#: templates/catalogue/main_page.html:38
+msgid "You need to "
+msgstr ""
+
+#: templates/catalogue/main_page.html:38
+msgid "sign in"
+msgstr ""
+
+#: templates/catalogue/main_page.html:38
+msgid "to manage your shelves."
+msgstr ""
+
+#: templates/catalogue/main_page.html:41
+#: templates/lessons/document_list.html:49
+msgid "Hand-outs for teachers"
+msgstr ""
+
+#: templates/catalogue/main_page.html:42
+msgid ""
+"Lessons' prospects and other ideas for using Wolnelektury.pl for teaching."
+msgstr ""
+
+#: templates/catalogue/main_page.html:47
+msgid ""
+"are professional recordings of literary texts from our repository, available "
+"on free license in MP3 and Ogg Vorbis formats as well as in DAISY system."
+msgstr ""
+
+#: templates/catalogue/main_page.html:54
+#: templates/catalogue/tagged_object_list.html:114
+msgid "Authors"
+msgstr ""
+
+#: templates/catalogue/main_page.html:58
+#: templates/catalogue/tagged_object_list.html:118
+msgid "Kinds"
+msgstr ""
+
+#: templates/catalogue/main_page.html:62
+#: templates/catalogue/tagged_object_list.html:122
+msgid "Genres"
+msgstr ""
+
+#: templates/catalogue/main_page.html:66
+#: templates/catalogue/tagged_object_list.html:126
+msgid "Epochs"
+msgstr ""
+
+#: templates/catalogue/main_page.html:72
+msgid "Themes and topics"
+msgstr ""
+
+#: templates/catalogue/main_page.html:75
+msgid "Themes groups"
+msgstr ""
+
+#: templates/catalogue/main_page.html:260
+msgid "News"
+msgstr ""
+
+#: templates/catalogue/main_page.html:264
+msgid "See our blog"
+msgstr ""
+
+#: templates/catalogue/main_page.html:267
+msgid "You can help us!"
+msgstr ""
+
+#: templates/catalogue/main_page.html:268
+msgid ""
+"We try our best to elaborate works appended to our library. It is possible "
+"only due to support of our volunteers."
+msgstr ""
+
+#: templates/catalogue/main_page.html:269
+msgid ""
+"We invite people who want to take part in developing Internet school library "
+"Wolne Lektury."
+msgstr ""
+
+#: templates/catalogue/main_page.html:273
+msgid "About us"
+msgstr ""
+
+#: templates/catalogue/main_page.html:275
+msgid ""
+"\n"
+"\t\t\tInternet library with school readings “Wolne Lektury” (<a href="
+"\"http://wolnelektury.pl\">www.wolnelektury.pl</a>) is a project made by "
+"Modern Poland Foundation. It started in 2007 and shares school readings, "
+"which are recommended by Ministry of National Education and are in public "
+"domain.\n"
+"\t\t\t"
+msgstr ""
+
+#: templates/catalogue/search_no_hits.html:5
+msgid "Search in WolneLektury.pl"
+msgstr ""
+
+#: templates/catalogue/search_no_hits.html:15
+msgid "Sorry! Search cirteria did not match any resources."
+msgstr ""
+
+#: templates/catalogue/search_no_hits.html:17
+msgid ""
+"Search engine supports following criteria: title, author, theme/topic, "
+"epoch, kind and genre.\n"
+"\t\tAs for now we do not support full text search."
+msgstr ""
+
+#: templates/catalogue/tag_list.html:4
+msgid "See full category"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:15
+msgid "Your shelf is empty"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:16
+msgid ""
+"You can put a book on a shelf by entering page of the reading and clicking "
+"'Put on the shelf'."
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:31
+msgid "Download all books from this shelf"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:35
+msgid "Choose books' formats which you want to download:"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:36
+#: templates/catalogue/tagged_object_list.html:37
+#: templates/catalogue/tagged_object_list.html:38
+msgid "for reading"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:36
+msgid "and printing using"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:37
+msgid "and editing using"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:38
+msgid "on small displays, for example mobile phones"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:39
+#: templates/catalogue/tagged_object_list.html:40
+msgid "for listening"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:39
+msgid "on favourite MP3 player"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:40
+msgid "open format"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:40
+msgid "Xiph.org Foundation"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:41
+#: templates/lessons/ajax_document_detail.html:3
+#: templates/lessons/document_detail.html:13
+msgid "Download"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:41
+msgid "Updating list of books' formats on the shelf"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:41
+msgid "cancel"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:46
+msgid "Share this shelf"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:48
+msgid ""
+"Copy this link and share it with other people to let them see your shelf."
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:57
+msgid "Read work's study of this author on Lektury.Gazeta.pl"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:60
+msgid "Read study of epoch"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:60
+msgid "on Lektury.Gazeta.pl"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:67
+msgid "Read article about this author on Wikipedia"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:70
+msgid "Read article about epoch"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:70
+msgid "on Wikipedia"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:80
+msgid "Delete"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:88
+msgid "This author's works are copyrighted."
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:91
+msgid ""
+"This author's works are in public domain and will be published on Internet "
+"school library of Wolne Lektury soon."
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:95
+msgid ""
+"This author's works will become part of public domain and will be allowed to "
+"be published without restrictions in"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:97
+msgid "Find out why Internet libraries can't publish this author's works."
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:102
+msgid "No works of this author found."
+msgstr ""
+
+#: templates/catalogue/user_shelves.html:6
+msgid "remove"
+msgstr ""
+
+#: templates/catalogue/user_shelves.html:10
+msgid "You do not own any shelves. You can create one below if you want to"
+msgstr ""
+
+#: templates/lessons/ajax_document_detail.html:3
+#: templates/lessons/document_detail.html:13
+msgid "author"
+msgstr ""
+
+#: templates/lessons/document_detail.html:9
+msgid "return to list of materials"
+msgstr ""
+
+#: templates/lessons/document_list.html:7
+msgid "Hand-outs for teachers on "
+msgstr ""
+
+#: templates/pagination/pagination.html:5
+#: templates/pagination/pagination.html:7
+msgid "previous"
+msgstr ""
+
+#: templates/pagination/pagination.html:21
+#: templates/pagination/pagination.html:23
+msgid "next"
+msgstr ""
index d0ea820..c06af2a 100644 (file)
Binary files a/wolnelektury/locale/pl/LC_MESSAGES/django.mo and b/wolnelektury/locale/pl/LC_MESSAGES/django.mo differ
index 4934745..c356ef2 100644 (file)
 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
 # This file is distributed under the same license as the PACKAGE package.
 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
-#
+# 
 msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2008-09-04 06:17-0500\n"
-"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"POT-Creation-Date: 2010-05-19 15:39+0200\n"
+"PO-Revision-Date: 2010-05-19 15:40\n"
+"Last-Translator: <radoslaw.czajka@nowoczesnapolska.org.pl>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
+"X-Translated-Using: django-rosetta 0.5.3\n"
 
-#: catalogue/models.py:15
-msgid "author"
-msgstr "autor"
-
-#: catalogue/models.py:16
-msgid "epoch"
-msgstr "epoka"
-
-#: catalogue/models.py:17
-msgid "kind"
-msgstr "rodzaj"
-
-#: catalogue/models.py:18
-msgid "genre"
-msgstr "gatunek"
-
-#: catalogue/models.py:19
-msgid "theme"
-msgstr "motyw"
-
-#: catalogue/models.py:20
-msgid "set"
-msgstr "zestaw"
-
-#: catalogue/models.py:34
-msgid "name"
-msgstr "nazwa"
-
-#: catalogue/models.py:35 catalogue/models.py:71
-msgid "slug"
-msgstr "slug"
-
-#: catalogue/models.py:36
-msgid "sort key"
-msgstr "klucz sortowania"
-
-#: catalogue/models.py:37
-msgid "category"
-msgstr "kategoria"
-
-#: catalogue/models.py:45 catalogue/models.py:72 catalogue/models.py:86
-#: chunks/models.py:11
-msgid "description"
-msgstr "opis"
-
-#: catalogue/models.py:54 newtagging/models.py:470
-msgid "tag"
-msgstr "tag"
+#: settings.py:37
+msgid "German"
+msgstr "niemiecki"
 
-#: catalogue/models.py:55 newtagging/admin.py:38
-msgid "tags"
-msgstr "tagi"
+#: settings.py:38
+msgid "English"
+msgstr "angielski"
 
-#: catalogue/models.py:70
-msgid "title"
-msgstr "tytuł"
+#: settings.py:39
+msgid "Polish"
+msgstr "polski"
 
-#: catalogue/models.py:73
-msgid "creation date"
-msgstr "data utworzenia"
+#: settings.py:40
+msgid "Lithuanian"
+msgstr "litewski"
 
-#: catalogue/models.py:76
-msgid "XML file"
-msgstr "plik XML"
+#: settings.py:41
+msgid "French"
+msgstr "francuski"
 
-#: catalogue/models.py:77
-msgid "PDF file"
-msgstr "plik PDF"
+#: settings.py:42
+msgid "Russian"
+msgstr "rosyjski"
 
-#: catalogue/models.py:78
-msgid "ODT file"
-msgstr "plik ODT"
+#: settings.py:43
+msgid "Spanish"
+msgstr "hiszpański"
 
-#: catalogue/models.py:79
-msgid "HTML file"
-msgstr "plik HTML"
+#: templates/404.html:6 templates/404.html.py:15
+msgid "Page does not exist"
+msgstr "Podana strona nie istnieje"
 
-#: catalogue/models.py:162
-msgid "book"
-msgstr "książka"
+#: templates/404.html:17
+msgid "We are sorry, but this page does not exist. Please check if you entered correct address or go to "
+msgstr "Przepraszamy, ale ta strona nie istnieje. Sprawdź czy podałeś dobry adres, lub przejdź do"
 
-#: catalogue/models.py:163
-msgid "books"
-msgstr "książki"
+#: templates/404.html:17
+msgid "main page"
+msgstr "strony głównej"
 
-#: catalogue/models.py:180
-msgid "fragment"
-msgstr "fragment"
+#: templates/500.html:6 templates/500.html.py:54
+msgid "Server error"
+msgstr "Błąd serwera"
 
-#: catalogue/models.py:181
-msgid "fragments"
-msgstr "fragmenty"
+#: templates/500.html:55
+msgid "<p>The Wolnelektury.pl site is currently unavailable. Meanwhile, visit our <a href='http://nowoczesnapolska.org.pl'>blog</a>.</p> <p>Inform our <a href='mailto:fundacja@nowoczesnapolska.org.pl'>administrators</a> about the error.</p>"
+msgstr ""
+"<p>Serwis Wolnelektury.pl jest chwilowo niedostępny. Odwiedź naszego <a href='http://nowoczesnapolska.org.pl'>bloga</a></p>\n"
+"<p>Powiadom <a href='mailto:fundacja@nowoczesnapolska.org.pl'>administratorów</a> o błędzie.</p>"
+
+#: templates/503.html:6 templates/503.html.py:54
+msgid "Service unavailable"
+msgstr "Serwis niedostępny"
+
+#: templates/503.html:56
+msgid "The Wolnelektury.pl site is currently unavailable due to maintainance."
+msgstr "Serwis Wolnelektury.pl jest obecnie niedostępny z powodu prac konserwacyjnych."
+
+#: templates/base.html:20
+msgid "Internet Explorer cannot display this site properly. Click here to read more..."
+msgstr "Internet Explorer nie potrafi poprawnie wyświetlić tej strony. Kliknij tutaj, aby dowiedzieć się więcej..."
+
+#: templates/base.html:28 templates/catalogue/folded_tag_list.html:13
+#: templates/catalogue/main_page.html:43 templates/catalogue/main_page.html:48
+#: templates/catalogue/main_page.html:87
+#: templates/catalogue/main_page.html:270
+#: templates/catalogue/main_page.html:279
+msgid "See more"
+msgstr "Zobacz więcej"
+
+#: templates/base.html:37
+msgid "Welcome"
+msgstr "Witaj"
+
+#: templates/base.html:38
+msgid "Your shelves"
+msgstr "Twoje półki"
+
+#: templates/base.html:40
+msgid "Administration"
+msgstr "Administracja"
+
+#: templates/base.html:42
+msgid "Logout"
+msgstr "Wyloguj"
+
+#: templates/base.html:45 templates/base.html.py:91 templates/base.html:95
+#: templates/base.html.py:99 templates/auth/login.html:4
+#: templates/auth/login.html.py:7 templates/auth/login.html:12
+#: templates/auth/login.html.py:15
+msgid "Sign in"
+msgstr "Zaloguj się"
+
+#: templates/base.html:45 templates/base.html.py:91 templates/base.html:99
+#: templates/base.html.py:103 templates/auth/login.html:7
+#: templates/auth/login.html.py:21 templates/auth/login.html:23
+msgid "Register"
+msgstr "Załóż konto"
+
+#: templates/base.html:55
+msgid "Choose your interface language: "
+msgstr "Wybierz język interfejsu:"
+
+#: templates/base.html:60
+msgid "Choose language"
+msgstr "Wybierz język"
+
+#: templates/base.html:72
+msgid ""
+"\n"
+"\t\t\t\tWolne Lektury is a project lead by <a href=\"http://nowoczesnapolska.org.pl/\">Modern Poland Foundation</a>.\n"
+"\t\t\t\tDigital reproductions are made by <a href=\"http://www.bn.org.pl/\">The National Library</a>, based on TNL resources. \n"
+"\t\t\t\tHosting <a href=\"http://eo.pl/\">EO Networks</a>.\n"
+"\t\t\t\t"
+msgstr ""
+"\n"
+"Wolne Lektury to projekt prowadzony przez <a href=\"http://nowoczesnapolska.org.pl/\">Fundację Nowoczesna Polska</a>. \n"
+"Reprodukcje cyfrowe wykonane przez <a href=\"http://www.bn.org.pl/\">Bibliotekę Narodową</a> z egzemplarzy pochodzących ze zbiorów BN.\n"
+"Hosting <a href=\"http://eo.pl/\">EO Networks</a>. "
 
-#: chunks/models.py:10
-msgid "key"
-msgstr "klucz"
+#: templates/base.html:79
+msgid ""
+"\n"
+"\t\t\t\tModern Poland Foundation, 00-514 Warsaw, ul. Marszałkowska 84/92 lok. 125, tel/fax: (22) 621-30-17\n"
+"                e-mail: <a href=\"mailto:fundacja@nowoczesnapolska.org.pl\">fundacja@nowoczesnapolska.org.pl</a>\n"
+"\t\t\t\t"
+msgstr ""
+"\n"
+"Fundacja Nowoczesna Polska, 00-514 Warszawa, ul. Marszałkowska 84/92 lok. 125, tel/fax: (22) 621-30-17, e-mail: <a href=\"mailto:fundacja@nowoczesnapolska.org.pl\">fundacja@nowoczesnapolska.org.pl</a>"
+
+#: templates/base.html:88 templates/base.html.py:109
+#: templates/catalogue/book_detail.html:129
+#: templates/catalogue/book_fragments.html:33
+#: templates/catalogue/book_stub_detail.html:31
+#: templates/catalogue/search_no_hits.html:23
+#: templates/catalogue/tagged_object_list.html:141
+msgid "Close"
+msgstr "Zamknij"
+
+#: templates/base.html:111 templates/catalogue/book_detail.html:131
+#: templates/catalogue/book_fragments.html:35
+#: templates/catalogue/book_stub_detail.html:33
+#: templates/catalogue/search_no_hits.html:25
+#: templates/catalogue/tagged_object_list.html:143
+msgid "Loading"
+msgstr "Ładowanie"
+
+#: templates/admin/base_site.html:4 templates/admin/base_site.html.py:7
+msgid "Site administration"
+msgstr "Administracja stroną"
+
+#: templates/admin/base_site.html:8
+msgid "Translations"
+msgstr "Tłumaczenia"
+
+#: templates/admin/catalogue/book/change_list.html:6
+msgid "Import book"
+msgstr "Importuj książkę"
+
+#: templates/auth/login.html:4
+msgid "Register on"
+msgstr "Zarejestruj się w"
+
+#: templates/auth/login.html:9 templates/catalogue/book_detail.html:12
+#: templates/catalogue/book_fragments.html:12
+#: templates/catalogue/book_list.html:12
+#: templates/catalogue/book_stub_detail.html:12
+#: templates/catalogue/breadcrumbs.html:9
+#: templates/catalogue/main_page.html:13
+#: templates/lessons/document_detail.html:9
+#: templates/lessons/document_list.html:51
+msgid "Search"
+msgstr "Szukaj"
+
+#: templates/auth/login.html:9 templates/catalogue/book_detail.html:12
+#: templates/catalogue/book_fragments.html:12
+#: templates/catalogue/book_list.html:12
+#: templates/catalogue/book_stub_detail.html:12
+#: templates/catalogue/main_page.html:13
+#: templates/catalogue/tagged_object_list.html:41
+#: templates/lessons/document_detail.html:9
+#: templates/lessons/document_list.html:51
+msgid "or"
+msgstr "lub"
+
+#: templates/auth/login.html:9 templates/catalogue/book_detail.html:12
+#: templates/catalogue/book_list.html:12
+#: templates/catalogue/book_stub_detail.html:12
+#: templates/lessons/document_list.html:51
+msgid "return to main page"
+msgstr "wróć do strony głównej"
+
+#: templates/catalogue/book_detail.html:5
+msgid "on WolneLektury.pl"
+msgstr "w WolneLektury.pl"
+
+#: templates/catalogue/book_detail.html:17
+msgid "Work is licensed under "
+msgstr "Utwór jest udostępniony na licencji"
+
+#: templates/catalogue/book_detail.html:19
+msgid "Based on"
+msgstr "Na podstawie"
+
+#: templates/catalogue/book_detail.html:24
+#: templates/catalogue/tagged_object_list.html:27
+msgid "Hide description"
+msgstr "Zwiń opis"
+
+#: templates/catalogue/book_detail.html:27
+msgid "Put a book"
+msgstr "Wrzuć lekturę"
+
+#: templates/catalogue/book_detail.html:27
+msgid "on the shelf!"
+msgstr "na półkę!"
+
+#: templates/catalogue/book_detail.html:31
+msgid "Read online"
+msgstr "Czytaj online"
+
+#: templates/catalogue/book_detail.html:34
+msgid "Download PDF"
+msgstr "Pobierz plik PDF"
+
+#: templates/catalogue/book_detail.html:37
+msgid "Download ODT"
+msgstr "Pobierz plik ODT"
+
+#: templates/catalogue/book_detail.html:40
+msgid "Download TXT"
+msgstr "Pobierz plik TXT"
+
+#: templates/catalogue/book_detail.html:45
+msgid "Artist"
+msgstr "Czyta"
+
+#: templates/catalogue/book_detail.html:47
+msgid "Director"
+msgstr "Reżyseruje"
+
+#: templates/catalogue/book_detail.html:51
+msgid "Download MP3"
+msgstr "Pobierz plik MP3"
+
+#: templates/catalogue/book_detail.html:52
+msgid "Download Ogg Vorbis"
+msgstr "Pobierz plik Ogg Vorbis"
+
+#: templates/catalogue/book_detail.html:79
+msgid "Details"
+msgstr "O utworze"
+
+#: templates/catalogue/book_detail.html:82
+msgid "Author"
+msgstr "Autor"
+
+#: templates/catalogue/book_detail.html:88
+msgid "Epoch"
+msgstr "Epoka"
+
+#: templates/catalogue/book_detail.html:94
+msgid "Kind"
+msgstr "Rodzaj"
+
+#: templates/catalogue/book_detail.html:100
+msgid "Genre"
+msgstr "Gatunek"
+
+#: templates/catalogue/book_detail.html:106
+msgid "Other resources"
+msgstr "W innych miejscach"
+
+#: templates/catalogue/book_detail.html:108
+msgid "Book on project's wiki"
+msgstr "Lektura na wiki projektu"
+
+#: templates/catalogue/book_detail.html:109
+msgid "Source of the book"
+msgstr "Źródło lektury"
+
+#: templates/catalogue/book_detail.html:111
+msgid "Book description on Lektury.Gazeta.pl"
+msgstr "Opis lektury w Lektury.Gazeta.pl"
+
+#: templates/catalogue/book_detail.html:114
+msgid "Book description on Wikipedia"
+msgstr "Opis lektury w Wikipedii"
+
+#: templates/catalogue/book_detail.html:119
+msgid "Work's themes "
+msgstr "Motywy w utworze"
+
+#: templates/catalogue/book_fragments.html:5
+#: templates/catalogue/book_fragments.html:10
+msgid "Theme"
+msgstr "Motyw"
+
+#: templates/catalogue/book_fragments.html:5
+#: templates/catalogue/book_fragments.html:10
+msgid "in work "
+msgstr "w utworze"
+
+#: templates/catalogue/book_fragments.html:12
+msgid "return to book's page"
+msgstr "wróć do strony utworu"
+
+#: templates/catalogue/book_fragments.html:26
+msgid "See description"
+msgstr "Zobacz opis"
+
+#: templates/catalogue/book_fragments.html:26
+msgid "of the book "
+msgstr "utworu"
+
+#: templates/catalogue/book_list.html:7
+msgid "Alphabetical listing of works on WolneLektury.pl"
+msgstr "Alfabetyczny spis utworów w WolneLektury.pl"
+
+#: templates/catalogue/book_list.html:10
+msgid "Alphabetical listing of works"
+msgstr "Alfabetyczny spis utworów"
+
+#: templates/catalogue/book_sets.html:2
+msgid "Put a book on the shelf!"
+msgstr "Wrzuć lekturę na półkę!"
+
+#: templates/catalogue/book_sets.html:4
+msgid "You do not have any shelves. You can create one below, if you want to."
+msgstr "Nie posiadasz żadnych półek. Jeśli chcesz, możesz utworzyć nową półkę poniżej."
+
+#: templates/catalogue/book_sets.html:9 templates/catalogue/book_short.html:4
+msgid "Put on the shelf!"
+msgstr "Wrzuć na półkę"
+
+#: templates/catalogue/book_sets.html:16
+#: templates/catalogue/fragment_sets.html:16
+msgid "Create new shelf"
+msgstr "Utwórz nową półkę"
+
+#: templates/catalogue/book_short.html:14
+msgid "Jump to"
+msgstr "Na skróty"
+
+#: templates/catalogue/book_short.html:16
+msgid "Categories"
+msgstr "Utwór w kategoriach"
+
+#: templates/catalogue/book_stub_detail.html:17
+msgid "This work is in public domain and will be published on Internet school library of Wolne Lektury soon."
+msgstr "To dzieło znajduje się w domenie publicznej i niedługo zostanie opublikowane w szkolnej bibliotece internetowej Wolne Lektury."
+
+#: templates/catalogue/book_stub_detail.html:20
+msgid "This work will become part of public domain and will be allowed to be published without restrictions in"
+msgstr "To dzieło przejdzie do zasobów domeny publicznej i będzie mogło być publikowane bez żadnych ograniczeń za"
+
+#: templates/catalogue/book_stub_detail.html:22
+msgid "Find out why Internet libraries can't publish this work."
+msgstr "Dowiedz się, dlaczego biblioteki internetowe nie mogą udostępniać dzieł tego autora."
+
+#: templates/catalogue/book_stub_detail.html:24
+msgid "This work is copyrighted."
+msgstr "To dzieło objęte jest prawem autorskim."
+
+#: templates/catalogue/book_text.html:17
+msgid "Table of contents"
+msgstr "Spis treści"
+
+#: templates/catalogue/book_text.html:18
+#: templates/catalogue/tagged_object_list.html:132
+msgid "Themes"
+msgstr "Motywy"
+
+#: templates/catalogue/folded_tag_list.html:4
+msgid "Show full category"
+msgstr "Zobacz całą kategorię"
+
+#: templates/catalogue/folded_tag_list.html:22
+#: templates/catalogue/main_page.html:250
+msgid "Hide"
+msgstr "Zwiń"
+
+#: templates/catalogue/fragment_sets.html:2
+msgid "Shelves containing fragment"
+msgstr "Półki zawierające fragment"
+
+#: templates/catalogue/fragment_sets.html:4
+#: templates/catalogue/main_page.html:28
+msgid "You do not own any shelves. You can create one below, if you want to."
+msgstr "Nie posiadasz żadnych półek. Jeśli chcesz, możesz utworzyć nową półkę poniżej."
+
+#: templates/catalogue/fragment_sets.html:9
+msgid "Save all shelves"
+msgstr "Zapisz półki"
+
+#: templates/catalogue/fragment_short.html:6
+msgid "Expand fragment"
+msgstr "Rozwiń fragment"
+
+#: templates/catalogue/fragment_short.html:12
+msgid "Hide fragment"
+msgstr "Zwiń fragment"
+
+#: templates/catalogue/fragment_short.html:17
+msgid "See in a book"
+msgstr "Zobacz w utworze"
+
+#: templates/catalogue/main_page.html:13
+msgid "check list of books"
+msgstr "zobacz spis utworów"
+
+#: templates/catalogue/main_page.html:13
+msgid "in our repository"
+msgstr "w naszym zbiorze"
+
+#: templates/catalogue/main_page.html:17
+msgid "Browse books by categories"
+msgstr "Przeglądaj lektury według wybranych kategorii"
+
+#: templates/catalogue/main_page.html:19
+#: templates/catalogue/user_shelves.html:2
+msgid "Your shelves with books"
+msgstr "Twoje półki z lekturami"
+
+#: templates/catalogue/main_page.html:24
+msgid "delete"
+msgstr "usuń"
+
+#: templates/catalogue/main_page.html:33
+#: templates/catalogue/user_shelves.html:15
+msgid "Create shelf"
+msgstr "Utwórz półkę"
+
+#: templates/catalogue/main_page.html:37
+msgid "Create your own book set. You can share it with friends by sending them link to your shelf."
+msgstr "Stwórz własny zestaw lektur. Możesz się nim później podzielić z innymi, przesyłając im link do Twojej półki."
+
+#: templates/catalogue/main_page.html:38
+msgid "You need to "
+msgstr "Aby zarządzać swoimi półkami, musisz się"
+
+#: templates/catalogue/main_page.html:38
+msgid "sign in"
+msgstr "zalogować"
+
+#: templates/catalogue/main_page.html:38
+msgid "to manage your shelves."
+msgstr "."
+
+#: templates/catalogue/main_page.html:41
+#: templates/lessons/document_list.html:49
+msgid "Hand-outs for teachers"
+msgstr "Materiały pomocnicze dla nauczycieli"
+
+#: templates/catalogue/main_page.html:42
+msgid "Lessons' prospects and other ideas for using Wolnelektury.pl for teaching."
+msgstr "Scenariusze lekcji i inne pomysły na wykorzytanie serwisu WolneLektury.pl podczas nauczania."
+
+#: templates/catalogue/main_page.html:47
+msgid "are professional recordings of literary texts from our repository, available on free license in MP3 and Ogg Vorbis formats as well as in DAISY system."
+msgstr "to profesjonalne nagrania tekstów literackich z naszego zbioru dostępne na wolnej licencji w formatach MP3, Ogg Vorbis oraz w systemie DAISY."
+
+#: templates/catalogue/main_page.html:54
+#: templates/catalogue/tagged_object_list.html:114
+msgid "Authors"
+msgstr "Autorzy"
+
+#: templates/catalogue/main_page.html:58
+#: templates/catalogue/tagged_object_list.html:118
+msgid "Kinds"
+msgstr "Rodzaje"
+
+#: templates/catalogue/main_page.html:62
+#: templates/catalogue/tagged_object_list.html:122
+msgid "Genres"
+msgstr "Gatunki"
+
+#: templates/catalogue/main_page.html:66
+#: templates/catalogue/tagged_object_list.html:126
+msgid "Epochs"
+msgstr "Epoki"
+
+#: templates/catalogue/main_page.html:72
+msgid "Themes and topics"
+msgstr "Motywy i tematy"
+
+#: templates/catalogue/main_page.html:75
+msgid "Themes groups"
+msgstr "Rodziny motywów"
+
+#: templates/catalogue/main_page.html:260
+msgid "News"
+msgstr "Aktualności"
+
+#: templates/catalogue/main_page.html:264
+msgid "See our blog"
+msgstr "Zobacz nasz blog"
+
+#: templates/catalogue/main_page.html:267
+msgid "You can help us!"
+msgstr "Możesz nam pomóc!"
+
+#: templates/catalogue/main_page.html:268
+msgid "We try our best to elaborate works appended to our library. It is possible only due to support of our volunteers."
+msgstr "Utwory włączane sukcesywnie do naszej biblioteki staramy się opracowywać jak najdokładniej. Jest to możliwe tylko dzięki współpracującym z nami wolontariuszom."
+
+#: templates/catalogue/main_page.html:269
+msgid "We invite people who want to take part in developing Internet school library Wolne Lektury."
+msgstr "Zapraszamy wszystkie osoby, które chcą współtworzyć szkolną bibliotekę internetową Wolne Lektury."
+
+#: templates/catalogue/main_page.html:273
+msgid "About us"
+msgstr "O projekcie"
+
+#: templates/catalogue/main_page.html:275
+msgid ""
+"\n"
+"\t\t\tInternet library with school readings “Wolne Lektury” (<a href=\"http://wolnelektury.pl\">www.wolnelektury.pl</a>) is a project made by Modern Poland Foundation. It started in 2007 and shares school readings, which are recommended by Ministry of National Education and are in public domain.\n"
+"\t\t\t"
+msgstr ""
+"\n"
+"Biblioteka internetowa z lekturami szkolnymi „Wolne Lektury” (<a href=\"http://wolnelektury.pl\">www.wolnelektury.pl</a>) to projekt realizowany przez Fundację Nowoczesna Polska. Działa od 2007 roku i udostępnia w swoich zbiorach lektury szkolne, które są zalecane do użytku przez Ministerstwo Edukacji Narodowej i które trafiły już do domeny publicznej."
 
-#: chunks/models.py:10
-msgid "A unique name for this chunk of content"
-msgstr "Unikalna nazwa dla tego kawałka treści"
+#: templates/catalogue/search_no_hits.html:5
+msgid "Search in WolneLektury.pl"
+msgstr "Wyszukiwanie w WolneLektury.pl"
 
-#: chunks/models.py:12
-msgid "content"
-msgstr "zawartość"
+#: templates/catalogue/search_no_hits.html:15
+msgid "Sorry! Search cirteria did not match any resources."
+msgstr "Przepraszamy! Brak wyników spełniających kryteria podane w zapytaniu."
 
-#: chunks/models.py:16
-msgid "chunk"
-msgstr "kawałek"
+#: templates/catalogue/search_no_hits.html:17
+msgid ""
+"Search engine supports following criteria: title, author, theme/topic, epoch, kind and genre.\n"
+"\t\tAs for now we do not support full text search."
+msgstr "Wyszukiwarka obsługuje takie kryteria jak tytuł, autor, motyw/temat, epoka, rodzaj i gatunek utworu. Obecnie nie obsługujemy wyszukiwania fraz w tekstach utworów."
+
+#: templates/catalogue/tag_list.html:4
+msgid "See full category"
+msgstr "Zobacz całą kategorię"
+
+#: templates/catalogue/tagged_object_list.html:15
+msgid "Your shelf is empty"
+msgstr "Twoja półka jest pusta"
+
+#: templates/catalogue/tagged_object_list.html:16
+msgid "You can put a book on a shelf by entering page of the reading and clicking 'Put on the shelf'."
+msgstr "Możesz wrzucić książkę na półkę, wchodząc na stronę danej lektury i klikając na przycisk „Na półkę!”."
+
+#: templates/catalogue/tagged_object_list.html:31
+msgid "Download all books from this shelf"
+msgstr "Pobierz wszystkie książki z tej półki"
+
+#: templates/catalogue/tagged_object_list.html:35
+msgid "Choose books' formats which you want to download:"
+msgstr "Wybierz formaty książek, które chcesz pobrać:"
+
+#: templates/catalogue/tagged_object_list.html:36
+#: templates/catalogue/tagged_object_list.html:37
+#: templates/catalogue/tagged_object_list.html:38
+msgid "for reading"
+msgstr "do czytania"
+
+#: templates/catalogue/tagged_object_list.html:36
+msgid "and printing using"
+msgstr "i drukowania przy pomocy"
+
+#: templates/catalogue/tagged_object_list.html:37
+msgid "and editing using"
+msgstr "i edytowania przy pomocy"
+
+#: templates/catalogue/tagged_object_list.html:38
+msgid "on small displays, for example mobile phones"
+msgstr "na małych ekranach, np. na komórce"
+
+#: templates/catalogue/tagged_object_list.html:39
+#: templates/catalogue/tagged_object_list.html:40
+msgid "for listening"
+msgstr "do słuchania"
+
+#: templates/catalogue/tagged_object_list.html:39
+msgid "on favourite MP3 player"
+msgstr "w ulubionym odtwarzaczu MP3"
+
+#: templates/catalogue/tagged_object_list.html:40
+msgid "open format"
+msgstr "otwarty format"
+
+#: templates/catalogue/tagged_object_list.html:40
+msgid "Xiph.org Foundation"
+msgstr "Fundacji Xiph.Org"
+
+#: templates/catalogue/tagged_object_list.html:41
+#: templates/lessons/ajax_document_detail.html:3
+#: templates/lessons/document_detail.html:13
+msgid "Download"
+msgstr "Pobierz"
+
+#: templates/catalogue/tagged_object_list.html:41
+msgid "Updating list of books' formats on the shelf"
+msgstr "Uaktualnianie listy formatów książek na półce."
+
+#: templates/catalogue/tagged_object_list.html:41
+msgid "cancel"
+msgstr "anuluj"
+
+#: templates/catalogue/tagged_object_list.html:46
+msgid "Share this shelf"
+msgstr "Podziel się tą półką"
+
+#: templates/catalogue/tagged_object_list.html:48
+msgid "Copy this link and share it with other people to let them see your shelf."
+msgstr "Skopiuj ten link i przekaż go osobom, z którymi chcesz się podzielić tą półką."
+
+#: templates/catalogue/tagged_object_list.html:57
+msgid "Read work's study of this author on Lektury.Gazeta.pl"
+msgstr "Przeczytaj omówienia utworów autora w serwisie Lektury.Gazeta.pl"
+
+#: templates/catalogue/tagged_object_list.html:60
+msgid "Read study of epoch"
+msgstr "Przeczytaj omówienia z epoki"
+
+#: templates/catalogue/tagged_object_list.html:60
+msgid "on Lektury.Gazeta.pl"
+msgstr "w serwisie Lektury.Gazeta.pl"
+
+#: templates/catalogue/tagged_object_list.html:67
+msgid "Read article about this author on Wikipedia"
+msgstr "Przeczytaj artykuł o autorze w Wikipedii"
+
+#: templates/catalogue/tagged_object_list.html:70
+msgid "Read article about epoch"
+msgstr "Przeczytaj artykuł o epoce"
+
+#: templates/catalogue/tagged_object_list.html:70
+msgid "on Wikipedia"
+msgstr "w Wikipedii"
+
+#: templates/catalogue/tagged_object_list.html:80
+msgid "Delete"
+msgstr "Usuń"
+
+#: templates/catalogue/tagged_object_list.html:88
+msgid "This author's works are copyrighted."
+msgstr "Dzieła tego autora objęte są prawem autorskim."
+
+#: templates/catalogue/tagged_object_list.html:91
+msgid "This author's works are in public domain and will be published on Internet school library of Wolne Lektury soon."
+msgstr "Dzieła tego autora znajdują się w domenie publicznej i niedługo zostaną opublikowane w szkolnej bibliotece internetowej Wolne Lektury."
+
+#: templates/catalogue/tagged_object_list.html:95
+msgid "This author's works will become part of public domain and will be allowed to be published without restrictions in"
+msgstr "Dzieła tego autora przejdą do zasobów domeny publicznej i będą mogły być publikowane bez żadnych ograniczeń za"
+
+#: templates/catalogue/tagged_object_list.html:97
+msgid "Find out why Internet libraries can't publish this author's works."
+msgstr "Dowiedz się, dlaczego biblioteki internetowe nie mogą udostępniać dzieł tego autora."
+
+#: templates/catalogue/tagged_object_list.html:102
+msgid "No works of this author found."
+msgstr "Nie znaleziono żadnych utworów."
 
-#: chunks/models.py:17
-msgid "chunks"
-msgstr "kawałki"
+#: templates/catalogue/user_shelves.html:6
+msgid "remove"
+msgstr "usuń"
 
-#: newtagging/models.py:471
-msgid "content type"
-msgstr "typ zawartości"
+#: templates/catalogue/user_shelves.html:10
+msgid "You do not own any shelves. You can create one below if you want to"
+msgstr "Nie posiadasz żadnych półek. Jeśli chcesz, możesz utworzyć półkę poniżej."
 
-#: newtagging/models.py:472
-msgid "object id"
-msgstr "id obiektu"
+#: templates/lessons/ajax_document_detail.html:3
+#: templates/lessons/document_detail.html:13
+msgid "author"
+msgstr "autor"
 
-#: newtagging/views.py:29
-msgid "tagged_object_list must be called with a queryset or a model."
-msgstr ""
+#: templates/lessons/document_detail.html:9
+msgid "return to list of materials"
+msgstr "wróć do listy materiałów"
 
-#: newtagging/views.py:31
-msgid "tagged_object_list must be called with a tag model."
-msgstr ""
+#: templates/lessons/document_list.html:7
+msgid "Hand-outs for teachers on "
+msgstr "Materiały pomocnicze dla nauczycieli w "
 
-#: newtagging/views.py:33
-msgid "tagged_object_list must be called with a tag."
-msgstr ""
+#: templates/pagination/pagination.html:5
+#: templates/pagination/pagination.html:7
+msgid "previous"
+msgstr "poprzedni"
 
-#: newtagging/views.py:37
-#, python-format
-msgid "No tags found matching \"%s\"."
-msgstr "Nie znaleziono tagów zgodnych z \"%s\"."
+#: templates/pagination/pagination.html:21
+#: templates/pagination/pagination.html:23
+msgid "next"
+msgstr "następny"
diff --git a/wolnelektury/locale/ru/LC_MESSAGES/django.mo b/wolnelektury/locale/ru/LC_MESSAGES/django.mo
new file mode 100644 (file)
index 0000000..c757a97
Binary files /dev/null and b/wolnelektury/locale/ru/LC_MESSAGES/django.mo differ
diff --git a/wolnelektury/locale/ru/LC_MESSAGES/django.po b/wolnelektury/locale/ru/LC_MESSAGES/django.po
new file mode 100644 (file)
index 0000000..fbd8e43
--- /dev/null
@@ -0,0 +1,737 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2010-05-19 15:39+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: settings.py:37
+msgid "German"
+msgstr ""
+
+#: settings.py:38
+msgid "English"
+msgstr ""
+
+#: settings.py:39
+msgid "Polish"
+msgstr ""
+
+#: settings.py:40
+msgid "Lithuanian"
+msgstr ""
+
+#: settings.py:41
+msgid "French"
+msgstr ""
+
+#: settings.py:42
+msgid "Russian"
+msgstr ""
+
+#: settings.py:43
+msgid "Spanish"
+msgstr ""
+
+#: templates/404.html:6 templates/404.html.py:15
+msgid "Page does not exist"
+msgstr ""
+
+#: templates/404.html:17
+msgid ""
+"We are sorry, but this page does not exist. Please check if you entered "
+"correct address or go to "
+msgstr ""
+
+#: templates/404.html:17
+msgid "main page"
+msgstr ""
+
+#: templates/500.html:6 templates/500.html.py:54
+msgid "Server error"
+msgstr ""
+
+#: templates/500.html:55
+msgid ""
+"<p>The Wolnelektury.pl site is currently unavailable. Meanwhile, visit our "
+"<a href='http://nowoczesnapolska.org.pl'>blog</a>.</p> <p>Inform our <a "
+"href='mailto:fundacja@nowoczesnapolska.org.pl'>administrators</a> about the "
+"error.</p>"
+msgstr ""
+
+#: templates/503.html:6 templates/503.html.py:54
+msgid "Service unavailable"
+msgstr ""
+
+#: templates/503.html:56
+msgid "The Wolnelektury.pl site is currently unavailable due to maintainance."
+msgstr ""
+
+#: templates/base.html:20
+msgid ""
+"Internet Explorer cannot display this site properly. Click here to read "
+"more..."
+msgstr ""
+
+#: templates/base.html:28 templates/catalogue/folded_tag_list.html:13
+#: templates/catalogue/main_page.html:43 templates/catalogue/main_page.html:48
+#: templates/catalogue/main_page.html:87
+#: templates/catalogue/main_page.html:270
+#: templates/catalogue/main_page.html:279
+msgid "See more"
+msgstr ""
+
+#: templates/base.html:37
+msgid "Welcome"
+msgstr ""
+
+#: templates/base.html:38
+msgid "Your shelves"
+msgstr ""
+
+#: templates/base.html:40
+msgid "Administration"
+msgstr ""
+
+#: templates/base.html:42
+msgid "Logout"
+msgstr ""
+
+#: templates/base.html:45 templates/base.html.py:91 templates/base.html:95
+#: templates/base.html.py:99 templates/auth/login.html:4
+#: templates/auth/login.html.py:7 templates/auth/login.html:12
+#: templates/auth/login.html.py:15
+msgid "Sign in"
+msgstr ""
+
+#: templates/base.html:45 templates/base.html.py:91 templates/base.html:99
+#: templates/base.html.py:103 templates/auth/login.html:7
+#: templates/auth/login.html.py:21 templates/auth/login.html:23
+msgid "Register"
+msgstr ""
+
+#: templates/base.html:55
+msgid "Choose your interface language: "
+msgstr ""
+
+#: templates/base.html:60
+msgid "Choose language"
+msgstr ""
+
+#: templates/base.html:72
+msgid ""
+"\n"
+"\t\t\t\tWolne Lektury is a project lead by <a href=\"http://nowoczesnapolska."
+"org.pl/\">Modern Poland Foundation</a>.\n"
+"\t\t\t\tDigital reproductions are made by <a href=\"http://www.bn.org.pl/"
+"\">The National Library</a>, based on TNL resources. \n"
+"\t\t\t\tHosting <a href=\"http://eo.pl/\">EO Networks</a>.\n"
+"\t\t\t\t"
+msgstr ""
+
+#: templates/base.html:79
+msgid ""
+"\n"
+"\t\t\t\tModern Poland Foundation, 00-514 Warsaw, ul. Marszałkowska 84/92 "
+"lok. 125, tel/fax: (22) 621-30-17\n"
+"                e-mail: <a href=\"mailto:fundacja@nowoczesnapolska.org.pl"
+"\">fundacja@nowoczesnapolska.org.pl</a>\n"
+"\t\t\t\t"
+msgstr ""
+
+#: templates/base.html:88 templates/base.html.py:109
+#: templates/catalogue/book_detail.html:129
+#: templates/catalogue/book_fragments.html:33
+#: templates/catalogue/book_stub_detail.html:31
+#: templates/catalogue/search_no_hits.html:23
+#: templates/catalogue/tagged_object_list.html:141
+msgid "Close"
+msgstr ""
+
+#: templates/base.html:111 templates/catalogue/book_detail.html:131
+#: templates/catalogue/book_fragments.html:35
+#: templates/catalogue/book_stub_detail.html:33
+#: templates/catalogue/search_no_hits.html:25
+#: templates/catalogue/tagged_object_list.html:143
+msgid "Loading"
+msgstr ""
+
+#: templates/admin/base_site.html:4 templates/admin/base_site.html.py:7
+msgid "Site administration"
+msgstr ""
+
+#: templates/admin/base_site.html:8
+msgid "Translations"
+msgstr ""
+
+#: templates/admin/catalogue/book/change_list.html:6
+msgid "Import book"
+msgstr ""
+
+#: templates/auth/login.html:4
+msgid "Register on"
+msgstr ""
+
+#: templates/auth/login.html:9 templates/catalogue/book_detail.html:12
+#: templates/catalogue/book_fragments.html:12
+#: templates/catalogue/book_list.html:12
+#: templates/catalogue/book_stub_detail.html:12
+#: templates/catalogue/breadcrumbs.html:9
+#: templates/catalogue/main_page.html:13
+#: templates/lessons/document_detail.html:9
+#: templates/lessons/document_list.html:51
+msgid "Search"
+msgstr ""
+
+#: templates/auth/login.html:9 templates/catalogue/book_detail.html:12
+#: templates/catalogue/book_fragments.html:12
+#: templates/catalogue/book_list.html:12
+#: templates/catalogue/book_stub_detail.html:12
+#: templates/catalogue/main_page.html:13
+#: templates/catalogue/tagged_object_list.html:41
+#: templates/lessons/document_detail.html:9
+#: templates/lessons/document_list.html:51
+msgid "or"
+msgstr ""
+
+#: templates/auth/login.html:9 templates/catalogue/book_detail.html:12
+#: templates/catalogue/book_list.html:12
+#: templates/catalogue/book_stub_detail.html:12
+#: templates/lessons/document_list.html:51
+msgid "return to main page"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:5
+msgid "on WolneLektury.pl"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:17
+msgid "Work is licensed under "
+msgstr ""
+
+#: templates/catalogue/book_detail.html:19
+msgid "Based on"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:24
+#: templates/catalogue/tagged_object_list.html:27
+msgid "Hide description"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:27
+msgid "Put a book"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:27
+msgid "on the shelf!"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:31
+msgid "Read online"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:34
+msgid "Download PDF"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:37
+msgid "Download ODT"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:40
+msgid "Download TXT"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:45
+msgid "Artist"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:47
+msgid "Director"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:51
+msgid "Download MP3"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:52
+msgid "Download Ogg Vorbis"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:79
+msgid "Details"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:82
+msgid "Author"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:88
+msgid "Epoch"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:94
+msgid "Kind"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:100
+msgid "Genre"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:106
+msgid "Other resources"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:108
+msgid "Book on project's wiki"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:109
+msgid "Source of the book"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:111
+msgid "Book description on Lektury.Gazeta.pl"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:114
+msgid "Book description on Wikipedia"
+msgstr ""
+
+#: templates/catalogue/book_detail.html:119
+msgid "Work's themes "
+msgstr ""
+
+#: templates/catalogue/book_fragments.html:5
+#: templates/catalogue/book_fragments.html:10
+msgid "Theme"
+msgstr ""
+
+#: templates/catalogue/book_fragments.html:5
+#: templates/catalogue/book_fragments.html:10
+msgid "in work "
+msgstr ""
+
+#: templates/catalogue/book_fragments.html:12
+msgid "return to book's page"
+msgstr ""
+
+#: templates/catalogue/book_fragments.html:26
+msgid "See description"
+msgstr ""
+
+#: templates/catalogue/book_fragments.html:26
+msgid "of the book "
+msgstr ""
+
+#: templates/catalogue/book_list.html:7
+msgid "Alphabetical listing of works on WolneLektury.pl"
+msgstr ""
+
+#: templates/catalogue/book_list.html:10
+msgid "Alphabetical listing of works"
+msgstr ""
+
+#: templates/catalogue/book_sets.html:2
+msgid "Put a book on the shelf!"
+msgstr ""
+
+#: templates/catalogue/book_sets.html:4
+msgid "You do not have any shelves. You can create one below, if you want to."
+msgstr ""
+
+#: templates/catalogue/book_sets.html:9 templates/catalogue/book_short.html:4
+msgid "Put on the shelf!"
+msgstr ""
+
+#: templates/catalogue/book_sets.html:16
+#: templates/catalogue/fragment_sets.html:16
+msgid "Create new shelf"
+msgstr ""
+
+#: templates/catalogue/book_short.html:14
+msgid "Jump to"
+msgstr ""
+
+#: templates/catalogue/book_short.html:16
+msgid "Categories"
+msgstr ""
+
+#: templates/catalogue/book_stub_detail.html:17
+msgid ""
+"This work is in public domain and will be published on Internet school "
+"library of Wolne Lektury soon."
+msgstr ""
+
+#: templates/catalogue/book_stub_detail.html:20
+msgid ""
+"This work will become part of public domain and will be allowed to be "
+"published without restrictions in"
+msgstr ""
+
+#: templates/catalogue/book_stub_detail.html:22
+msgid "Find out why Internet libraries can't publish this work."
+msgstr ""
+
+#: templates/catalogue/book_stub_detail.html:24
+msgid "This work is copyrighted."
+msgstr ""
+
+#: templates/catalogue/book_text.html:17
+msgid "Table of contents"
+msgstr ""
+
+#: templates/catalogue/book_text.html:18
+#: templates/catalogue/tagged_object_list.html:132
+msgid "Themes"
+msgstr ""
+
+#: templates/catalogue/folded_tag_list.html:4
+msgid "Show full category"
+msgstr ""
+
+#: templates/catalogue/folded_tag_list.html:22
+#: templates/catalogue/main_page.html:250
+msgid "Hide"
+msgstr ""
+
+#: templates/catalogue/fragment_sets.html:2
+msgid "Shelves containing fragment"
+msgstr ""
+
+#: templates/catalogue/fragment_sets.html:4
+#: templates/catalogue/main_page.html:28
+msgid "You do not own any shelves. You can create one below, if you want to."
+msgstr ""
+
+#: templates/catalogue/fragment_sets.html:9
+msgid "Save all shelves"
+msgstr ""
+
+#: templates/catalogue/fragment_short.html:6
+msgid "Expand fragment"
+msgstr ""
+
+#: templates/catalogue/fragment_short.html:12
+msgid "Hide fragment"
+msgstr ""
+
+#: templates/catalogue/fragment_short.html:17
+msgid "See in a book"
+msgstr ""
+
+#: templates/catalogue/main_page.html:13
+msgid "check list of books"
+msgstr ""
+
+#: templates/catalogue/main_page.html:13
+msgid "in our repository"
+msgstr ""
+
+#: templates/catalogue/main_page.html:17
+msgid "Browse books by categories"
+msgstr ""
+
+#: templates/catalogue/main_page.html:19
+#: templates/catalogue/user_shelves.html:2
+msgid "Your shelves with books"
+msgstr ""
+
+#: templates/catalogue/main_page.html:24
+msgid "delete"
+msgstr ""
+
+#: templates/catalogue/main_page.html:33
+#: templates/catalogue/user_shelves.html:15
+msgid "Create shelf"
+msgstr ""
+
+#: templates/catalogue/main_page.html:37
+msgid ""
+"Create your own book set. You can share it with friends by sending them link "
+"to your shelf."
+msgstr ""
+
+#: templates/catalogue/main_page.html:38
+msgid "You need to "
+msgstr ""
+
+#: templates/catalogue/main_page.html:38
+msgid "sign in"
+msgstr ""
+
+#: templates/catalogue/main_page.html:38
+msgid "to manage your shelves."
+msgstr ""
+
+#: templates/catalogue/main_page.html:41
+#: templates/lessons/document_list.html:49
+msgid "Hand-outs for teachers"
+msgstr ""
+
+#: templates/catalogue/main_page.html:42
+msgid ""
+"Lessons' prospects and other ideas for using Wolnelektury.pl for teaching."
+msgstr ""
+
+#: templates/catalogue/main_page.html:47
+msgid ""
+"are professional recordings of literary texts from our repository, available "
+"on free license in MP3 and Ogg Vorbis formats as well as in DAISY system."
+msgstr ""
+
+#: templates/catalogue/main_page.html:54
+#: templates/catalogue/tagged_object_list.html:114
+msgid "Authors"
+msgstr ""
+
+#: templates/catalogue/main_page.html:58
+#: templates/catalogue/tagged_object_list.html:118
+msgid "Kinds"
+msgstr ""
+
+#: templates/catalogue/main_page.html:62
+#: templates/catalogue/tagged_object_list.html:122
+msgid "Genres"
+msgstr ""
+
+#: templates/catalogue/main_page.html:66
+#: templates/catalogue/tagged_object_list.html:126
+msgid "Epochs"
+msgstr ""
+
+#: templates/catalogue/main_page.html:72
+msgid "Themes and topics"
+msgstr ""
+
+#: templates/catalogue/main_page.html:75
+msgid "Themes groups"
+msgstr ""
+
+#: templates/catalogue/main_page.html:260
+msgid "News"
+msgstr ""
+
+#: templates/catalogue/main_page.html:264
+msgid "See our blog"
+msgstr ""
+
+#: templates/catalogue/main_page.html:267
+msgid "You can help us!"
+msgstr ""
+
+#: templates/catalogue/main_page.html:268
+msgid ""
+"We try our best to elaborate works appended to our library. It is possible "
+"only due to support of our volunteers."
+msgstr ""
+
+#: templates/catalogue/main_page.html:269
+msgid ""
+"We invite people who want to take part in developing Internet school library "
+"Wolne Lektury."
+msgstr ""
+
+#: templates/catalogue/main_page.html:273
+msgid "About us"
+msgstr ""
+
+#: templates/catalogue/main_page.html:275
+msgid ""
+"\n"
+"\t\t\tInternet library with school readings “Wolne Lektury” (<a href="
+"\"http://wolnelektury.pl\">www.wolnelektury.pl</a>) is a project made by "
+"Modern Poland Foundation. It started in 2007 and shares school readings, "
+"which are recommended by Ministry of National Education and are in public "
+"domain.\n"
+"\t\t\t"
+msgstr ""
+
+#: templates/catalogue/search_no_hits.html:5
+msgid "Search in WolneLektury.pl"
+msgstr ""
+
+#: templates/catalogue/search_no_hits.html:15
+msgid "Sorry! Search cirteria did not match any resources."
+msgstr ""
+
+#: templates/catalogue/search_no_hits.html:17
+msgid ""
+"Search engine supports following criteria: title, author, theme/topic, "
+"epoch, kind and genre.\n"
+"\t\tAs for now we do not support full text search."
+msgstr ""
+
+#: templates/catalogue/tag_list.html:4
+msgid "See full category"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:15
+msgid "Your shelf is empty"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:16
+msgid ""
+"You can put a book on a shelf by entering page of the reading and clicking "
+"'Put on the shelf'."
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:31
+msgid "Download all books from this shelf"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:35
+msgid "Choose books' formats which you want to download:"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:36
+#: templates/catalogue/tagged_object_list.html:37
+#: templates/catalogue/tagged_object_list.html:38
+msgid "for reading"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:36
+msgid "and printing using"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:37
+msgid "and editing using"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:38
+msgid "on small displays, for example mobile phones"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:39
+#: templates/catalogue/tagged_object_list.html:40
+msgid "for listening"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:39
+msgid "on favourite MP3 player"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:40
+msgid "open format"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:40
+msgid "Xiph.org Foundation"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:41
+#: templates/lessons/ajax_document_detail.html:3
+#: templates/lessons/document_detail.html:13
+msgid "Download"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:41
+msgid "Updating list of books' formats on the shelf"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:41
+msgid "cancel"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:46
+msgid "Share this shelf"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:48
+msgid ""
+"Copy this link and share it with other people to let them see your shelf."
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:57
+msgid "Read work's study of this author on Lektury.Gazeta.pl"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:60
+msgid "Read study of epoch"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:60
+msgid "on Lektury.Gazeta.pl"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:67
+msgid "Read article about this author on Wikipedia"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:70
+msgid "Read article about epoch"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:70
+msgid "on Wikipedia"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:80
+msgid "Delete"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:88
+msgid "This author's works are copyrighted."
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:91
+msgid ""
+"This author's works are in public domain and will be published on Internet "
+"school library of Wolne Lektury soon."
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:95
+msgid ""
+"This author's works will become part of public domain and will be allowed to "
+"be published without restrictions in"
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:97
+msgid "Find out why Internet libraries can't publish this author's works."
+msgstr ""
+
+#: templates/catalogue/tagged_object_list.html:102
+msgid "No works of this author found."
+msgstr ""
+
+#: templates/catalogue/user_shelves.html:6
+msgid "remove"
+msgstr ""
+
+#: templates/catalogue/user_shelves.html:10
+msgid "You do not own any shelves. You can create one below if you want to"
+msgstr ""
+
+#: templates/lessons/ajax_document_detail.html:3
+#: templates/lessons/document_detail.html:13
+msgid "author"
+msgstr ""
+
+#: templates/lessons/document_detail.html:9
+msgid "return to list of materials"
+msgstr ""
+
+#: templates/lessons/document_list.html:7
+msgid "Hand-outs for teachers on "
+msgstr ""
+
+#: templates/pagination/pagination.html:5
+#: templates/pagination/pagination.html:7
+msgid "previous"
+msgstr ""
+
+#: templates/pagination/pagination.html:21
+#: templates/pagination/pagination.html:23
+msgid "next"
+msgstr ""
index 6b7d333..8617992 100644 (file)
@@ -5,6 +5,7 @@ PROJECT_DIR = path.abspath(path.dirname(__file__))
 
 DEBUG = False
 TEMPLATE_DEBUG = DEBUG
+MAINTENANCE_MODE = False
 
 ADMINS = [
     # ('Your Name', 'your_email@domain.com'),
@@ -30,6 +31,19 @@ TIME_ZONE = 'Europe/Warsaw Poland'
 # http://www.i18nguy.com/unicode/language-identifiers.html
 LANGUAGE_CODE = 'pl'
 
+gettext = lambda s: s
+
+LANGUAGES = (
+    ('de', gettext('German')),
+    ('en', gettext('English')),
+    ('pl', gettext('Polish')),
+    ('lt', gettext('Lithuanian')),
+    ('fr', gettext('French')),
+    ('ru', gettext('Russian')),
+    ('es', gettext('Spanish')),        
+)
+
+
 SITE_ID = 1
 
 # If you set this to False, Django will make some optimizations so as not
@@ -67,6 +81,7 @@ TEMPLATE_CONTEXT_PROCESSORS = [
     'django.core.context_processors.i18n',
     'django.core.context_processors.media',
     'django.core.context_processors.request',
+    'wolnelektury.context_processors.extra_settings',
 ]
 
 MIDDLEWARE_CLASSES = [
@@ -75,6 +90,9 @@ MIDDLEWARE_CLASSES = [
     'django.contrib.auth.middleware.AuthenticationMiddleware',
     'django.middleware.doc.XViewMiddleware',
     'pagination.middleware.PaginationMiddleware',
+    'django.middleware.locale.LocaleMiddleware',
+
+    'maintenancemode.middleware.MaintenanceModeMiddleware',
 ]
 
 ROOT_URLCONF = 'wolnelektury.urls'
@@ -95,7 +113,7 @@ INSTALLED_APPS = [
     'django.contrib.sites',
     'django.contrib.admin',
     'django.contrib.admindocs',
-    
+
     # external
     'south',
     'sorl.thumbnail',
@@ -108,6 +126,7 @@ INSTALLED_APPS = [
     'lessons',
     'piston',
     'api',
+    'rosetta',
 ]
 
 CACHE_BACKEND = 'locmem:///?max_entries=3000'
@@ -115,7 +134,7 @@ CACHE_BACKEND = 'locmem:///?max_entries=3000'
 # CSS and JavaScript file groups
 COMPRESS_CSS = {
     'all': {
-        'source_filenames': ('css/master.css', 'css/jquery.autocomplete.css', 'css/master.plain.css', 'css/sponsors.css',),
+        'source_filenames': ('css/master.css', 'css/jquery.autocomplete.css', 'css/jquery.countdown.css', 'css/master.plain.css', 'css/sponsors.css',),
         'output_filename': 'css/all.min?.css',
     },
     'book': {
@@ -131,6 +150,10 @@ COMPRESS_JS = {
     },
     'all': {
         'source_filenames': ('js/jquery.autocomplete.js', 'js/jquery.form.js', 
+            'js/jquery.countdown.js', 'js/jquery.countdown-pl.js', 
+            'js/jquery.countdown-de.js',
+            'js/jquery.countdown-es.js', 'js/jquery.countdown-lt.js',
+            'js/jquery.countdown-ru.js', 'js/jquery.countdown-fr.js',
             'js/jquery.jqmodal.js', 'js/jquery.labelify.js', 'js/catalogue.js',
             'js/jquery.cookie.js',),
         'output_filename': 'js/all?.min.js',
diff --git a/wolnelektury/static/css/jquery.countdown.css b/wolnelektury/static/css/jquery.countdown.css
new file mode 100644 (file)
index 0000000..da40686
--- /dev/null
@@ -0,0 +1,52 @@
+/* jQuery Countdown styles 1.5.7. */
+.hasCountdown {
+       /*border: 1px solid #ccc;
+       background-color: #eee;*/
+       margin: 20px 0 50px 0;
+}
+.countdown_rtl {
+       direction: rtl;
+}
+.countdown_holding span {
+       background-color: #ccc;
+}
+.countdown_row {
+       clear: both;
+       width: 100%;
+       padding: 0px 2px;
+       text-align: center;
+}
+.countdown_show1 .countdown_section {
+       width: 98%;
+}
+.countdown_show2 .countdown_section {
+       width: 48%;
+}
+.countdown_show3 .countdown_section {
+       width: 32.5%;
+}
+.countdown_show4 .countdown_section {
+       width: 24.5%;
+}
+.countdown_show5 .countdown_section {
+       width: 19.5%;
+}
+.countdown_show6 .countdown_section {
+       width: 16.25%;
+}
+.countdown_show7 .countdown_section {
+       width: 14%;
+}
+.countdown_section {
+       display: block;
+       float: left;
+       font-size: 100%;
+       text-align: center;
+}
+.countdown_amount {
+       font-size: 350%;
+}
+.countdown_descr {
+       display: block;
+       width: 100%;
+}
index b0d3542..449b15f 100644 (file)
@@ -194,10 +194,13 @@ blockquote {
 /* ============= */
 /* = Numbering = */
 /* ============= */
+.verse, .paragraph {
+       position:relative;
+}
 .anchor {
     position: absolute;
     margin: -0.25em -0.5em;
-    left: 1em;
+    left: -3em;
     color: #777;
     font-size: 12px;
     width: 2em;
@@ -317,3 +320,11 @@ em.person {
     font-variant: small-caps;
 }
 
+
+/* =================================== */
+/* = Hide some elements for printing = */
+/* =================================== */
+
+@media print {
+    #menu {display: none;}
+}
index ba6e0aa..a0e5e3c 100644 (file)
@@ -459,6 +459,36 @@ div.shown-tags p, div.all-tags p {
     color: #900;    
 }
 
+#toggle-share-shelf {
+    display: block;
+    width: 100%;
+    height: 1.5em;
+    background-color: #EEE;
+    margin-top: 0.5em;
+    padding: 0.5em 0;
+    -moz-border-radius: 4px;
+    -webkit-border-radius: 4px;
+    border-radius: 4px;
+    text-align: center;   
+    outline: none; 
+}
+#toggle-share-shelf p {
+       margin:0;
+}
+
+#share-shelf {
+    -moz-border-radius: 4px;
+    -webkit-border-radius: 4px;
+    border-radius: 4px;
+    border: 3px solid #EEE;
+    padding: 5px;
+    margin-top: -5px;
+}
+#share-shelf input {
+       width: 100%;
+}
+
+
 /* ============================ */
 /* = Books and fragments list = */
 /* ============================ */
index 9f6a9bb..f780972 100644 (file)
@@ -5,4 +5,4 @@
 
 .sponsors-sponsor-logo {
     float: left;
-}c
\ No newline at end of file
+}
\ No newline at end of file
index 519280a..ad9768e 100644 (file)
@@ -1,3 +1,47 @@
+var LOCALE_TEXTS = {
+       "pl": {
+               "DELETE_SHELF": "Czy na pewno usunąć półkę",
+               "HIDE_DESCRIPTION": "Zwiń opis",
+               "EXPAND_DESCRIPTION": "Rozwiń opis",
+               "LOADING": "Ładowanie",                
+       },
+       "fr": {
+               "DELETE_SHELF": "Translate me!",
+               "HIDE_DESCRIPTION": "Translate me!",
+               "EXPAND_DESCRIPTION": "Translate me!",
+               "LOADING": "Translate me!",                             
+       },
+       "ru": {
+               "DELETE_SHELF": "Translate me!",
+               "HIDE_DESCRIPTION": "Translate me!",
+               "EXPAND_DESCRIPTION": "Translate me!",
+               "LOADING": "Translate me!",                             
+       },
+       "en": {
+               "DELETE_SHELF": "Translate me!",
+               "HIDE_DESCRIPTION": "Translate me!",
+               "EXPAND_DESCRIPTION": "Translate me!",
+               "LOADING": "Translate me!",             
+       }, 
+       "ru": {
+               "DELETE_SHELF": "Translate me!",
+               "HIDE_DESCRIPTION": "Translate me!",
+               "EXPAND_DESCRIPTION": "Translate me!",
+               "LOADING": "Translate me!",             
+       },
+       "es": {
+               "DELETE_SHELF": "Translate me!",
+               "HIDE_DESCRIPTION": "Translate me!",
+               "EXPAND_DESCRIPTION": "Translate me!",
+               "LOADING": "Translate me!",                             
+       },
+       "lt":{
+               "DELETE_SHELF": "Translate me!",
+               "HIDE_DESCRIPTION": "Translate me!",
+               "EXPAND_DESCRIPTION": "Translate me!",
+               "LOADING": "Translate me!",                             
+       }
+}
 var BANNER_TEXTS = [
     'Przekaż 1% żeby ukryć ten baner.',
     'Jak dobrze wydać 1% swojego podatku? <strong>Poradnik dla opornych</strong>.',
@@ -40,6 +84,20 @@ function changeBannerText() {
     }
 }
 
+function autocomplete_result_handler(event, item) {
+    $(event.target).closest('form').submit();
+}
+function serverTime() { 
+    var time = null; 
+    $.ajax({url: '/katalog/zegar/', 
+        async: false, dataType: 'text', 
+        success: function(text) { 
+            time = new Date(text);
+        }, error: function(http, message, exc) {
+            time = new Date(); 
+    }}); 
+    return time; 
+}
 
 (function($) {
     $(function() {
@@ -65,14 +123,16 @@ function changeBannerText() {
                     function() { $(this).css({background: '#FFF'}); }
                 ).click(function() {
                     $(this).fadeOut(function() {
-                        $(this).prev().fadeIn()
+                        $(this).prev().fadeIn();
                     });
+                    return false;
                 })
             }
         });
         
         $('.fragment-short-text').click(function() {
             $(this).fadeOut(function() { $(this).next().fadeIn() });
+            return false;
         }).hover(
             function() { $(this).css({background: '#F3F3F3', cursor: 'pointer'}); },
             function() { $(this).css({background: '#FFF'}); }
@@ -157,7 +217,7 @@ function changeBannerText() {
         $('.delete-shelf').click(function() { 
             var link = $(this);
             var shelf_name = $('.visit-shelf', link.parent()).text();
-            if (confirm('Czy na pewno usunąć półkę ' + shelf_name + '?')) {
+            if (confirm(LOCALE_TEXTS[LANGUAGE_CODE]['DELETE_SHELF']+ ' '+ shelf_name + '?')) {
                 $.post(link.attr('href'), function(data, textStatus) {
                     link.parent().remove();
                 });
@@ -195,7 +255,7 @@ function changeBannerText() {
                 $('.delete-shelf').click(function() {
                     var link = $(this);
                     var shelf_name = $('.visit-shelf', link.parent()).text();
-                    if (confirm('Czy na pewno usunąć półkę ' + shelf_name + '?')) {
+                    if (confirm(LOCALE_TEXTS[LANGUAGE_CODE]['DELETE_SHELF'] + ' ' + shelf_name + '?')) {
                         $.post(link.attr('href'), function(data, textStatus) {
                             link.parent().remove();
                         });
@@ -219,14 +279,25 @@ function changeBannerText() {
             if ($('#description').hasClass('hidden')) {
                 $('#description').slideDown('fast').removeClass('hidden');
                 $.cookie('description-state', 'opened', {path: '/', expires: 30});
-                $('p', this).html('Zwiń opis ▲');
+                $('p', this).html(LOCALE_TEXTS[LANGUAGE_CODE]['HIDE_DESCRIPTION'] + ' ▲');
             } else {
                 $('#description').slideUp('fast').addClass('hidden');
                 $.cookie('description-state', 'closed', {path: '/', expires: 30});
-                $('p', this).html('Rozwiń opis ▼');
+                $('p', this).html(LOCALE_TEXTS[LANGUAGE_CODE]['EXPAND_DESCRIPTION'] + ' ▼');
             }
         });
-    
+
+        $('#toggle-share-shelf').hover(
+            function() { $(this).css({background: '#F3F3F3', cursor: 'pointer'}); },
+            function() { $(this).css({background: '#EEE'}); }
+        ).click(function() {
+            if ($('#share-shelf').hasClass('hidden')) {
+                $('#share-shelf').slideDown('fast').removeClass('hidden');
+            } else {
+                $('#share-shelf').slideUp('fast').addClass('hidden');
+            }
+        });
+
         var target = $('#set-window div.target');
     
         $('#set-window').jqm({
@@ -236,7 +307,7 @@ function changeBannerText() {
             trigger: 'a.jqm-trigger', 
             onShow: function(hash) { 
                 var offset = $(hash.t).offset();
-                target.html('<p><img src="/static/img/indicator.gif" /> Ładowanie</p>');
+                target.html('<p><img src="/static/img/indicator.gif" />'+LOCALE_TEXTS[LANGUAGE_CODE]['DELETE_SHELF']+'</p>');
                 hash.w.css({position: 'absolute', left: offset.left, top: offset.top}).show() },
             onLoad: function(hash) { 
                 $('form', hash.w).ajaxForm({
@@ -256,8 +327,11 @@ function changeBannerText() {
         
         if ($.cookie('description-state') == 'closed') {
             $('#description').hide().addClass('hidden');
-            $('#toggle-description p').html('Rozwiń opis ▼');
+            $('#toggle-description p').html(LOCALE_TEXTS[LANGUAGE_CODE]['EXPAND_DESCRIPTION']+' ▼');
         }
+               
+        $('#share-shelf').hide().addClass('hidden');
+               $('#share-shelf input').focus(function(){this.select();});
                 
         $('#user-info').show();
         changeBannerText();
@@ -268,7 +342,7 @@ function changeBannerText() {
             $('#download-shelf-menu').slideDown('fast');
             
             if (!formatsDownloaded) {
-                // Pobierz dane o formatach
+                // Get info about the formats
                 formatsDownloaded = true;
                 $.ajax({
                     url: $('#download-formats-form').attr('data-formats-feed'),
index 5ad9178..4e425e8 100644 (file)
@@ -1,14 +1,18 @@
 /*
- * Autocomplete - jQuery plugin 1.0.2
+ * jQuery Autocomplete plugin 1.1
  *
- * Copyright (c) 2007 Dylan Verheul, Dan G. Switzer, Anjesh Tuladhar, Jörn Zaefferer
+ * Copyright (c) 2009 Jörn Zaefferer
  *
  * Dual licensed under the MIT and GPL licenses:
  *   http://www.opensource.org/licenses/mit-license.php
  *   http://www.gnu.org/licenses/gpl.html
  *
- * Revision: $Id: jquery.autocomplete.js 5747 2008-06-25 18:30:55Z joern.zaefferer $
- *
+ * Revision: $Id: jquery.autocomplete.js 15 2009-08-22 10:30:27Z joern.zaefferer $
+ */
+
+/*
+ * Modified by Radek Czajka, Fundacja Nowoczesna Polska, 2010-05-10:
+ *   escape regex for word start checking in matchSubset
  */
 
 ;(function($) {
@@ -90,6 +94,9 @@ $.Autocompleter = function(input, options) {
        
        // only opera doesn't trigger keydown multiple times while pressed, others don't work with keypress at all
        $input.bind(($.browser.opera ? "keypress" : "keydown") + ".autocomplete", function(event) {
+               // a keypress means the input has focus
+               // avoids issue where input had focus before the autocomplete was applied
+               hasFocus = 1;
                // track last key pressed
                lastKeyPressCode = event.keyCode;
                switch(event.keyCode) {
@@ -209,7 +216,21 @@ $.Autocompleter = function(input, options) {
                if ( options.multiple ) {
                        var words = trimWords($input.val());
                        if ( words.length > 1 ) {
-                               v = words.slice(0, words.length - 1).join( options.multipleSeparator ) + options.multipleSeparator + v;
+                               var seperator = options.multipleSeparator.length;
+                               var cursorAt = $(input).selection().start;
+                               var wordAt, progress = 0;
+                               $.each(words, function(i, word) {
+                                       progress += word.length;
+                                       if (cursorAt <= progress) {
+                                               wordAt = i;
+                                               return false;
+                                       }
+                                       progress += seperator;
+                               });
+                               words[wordAt] = v;
+                               // TODO this should set the cursor to the right position, but it gets overriden somewhere
+                               //$.Autocompleter.Selection(input, progress + seperator, progress + seperator);
+                               v = words.join( options.multipleSeparator );
                        }
                        v += options.multipleSeparator;
                }
@@ -246,22 +267,27 @@ $.Autocompleter = function(input, options) {
        };
        
        function trimWords(value) {
-               if ( !value ) {
+               if (!value)
                        return [""];
-               }
-               var words = value.split( options.multipleSeparator );
-               var result = [];
-               $.each(words, function(i, value) {
-                       if ( $.trim(value) )
-                               result[i] = $.trim(value);
+               if (!options.multiple)
+                       return [$.trim(value)];
+               return $.map(value.split(options.multipleSeparator), function(word) {
+                       return $.trim(value).length ? $.trim(word) : null;
                });
-               return result;
        }
        
        function lastWord(value) {
                if ( !options.multiple )
                        return value;
                var words = trimWords(value);
+               if (words.length == 1) 
+                       return words[0];
+               var cursorAt = $(input).selection().start;
+               if (cursorAt == value.length) {
+                       words = trimWords(value)
+               } else {
+                       words = trimWords(value.replace(value.substring(cursorAt), ""));
+               }
                return words[words.length - 1];
        }
        
@@ -275,7 +301,7 @@ $.Autocompleter = function(input, options) {
                        // fill in the value (keep the case the user has typed)
                        $input.val($input.val() + sValue.substring(lastWord(previousValue).length));
                        // select the portion of the value not typed by the user (so the next character will erase)
-                       $.Autocompleter.Selection(input, previousValue.length, previousValue.length + sValue.length);
+                       $(input).selection(previousValue.length, previousValue.length + sValue.length);
                }
        };
 
@@ -299,15 +325,14 @@ $.Autocompleter = function(input, options) {
                                                        var words = trimWords($input.val()).slice(0, -1);
                                                        $input.val( words.join(options.multipleSeparator) + (words.length ? options.multipleSeparator : "") );
                                                }
-                                               else
+                                               else {
                                                        $input.val( "" );
+                                                       $input.trigger("result", null);
+                                               }
                                        }
                                }
                        );
                }
-               if (wasVisible)
-                       // position cursor at end of input field
-                       $.Autocompleter.Selection(input, input.value.length, input.value.length);
        };
 
        function receiveData(q, data) {
@@ -405,8 +430,22 @@ $.Autocompleter.defaults = {
        width: 0,
        multiple: false,
        multipleSeparator: ", ",
+    regex_escape: function(term) {
+        term = term.replace(/([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi, "\\$1");
+        /* no polish diacritics; should be more locale-aware */
+        term = term.replace(/a/g, '[aą]')
+                .replace(/c/g, '[cć]')
+                .replace(/e/g, '[eę]')
+                .replace(/l/g, '[lł]')
+                .replace(/n/g, '[nń]')
+                .replace(/o/g, '[oó]')
+                .replace(/s/g, '[sś]')
+                .replace(/z/g, '[zźż]');
+        return term;
+    },
        highlight: function(value, term) {
-               return value.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + term.replace(/([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi, "\\$1") + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "<strong>$1</strong>");
+               term = $.Autocompleter.defaults.regex_escape(term);
+               return value.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + term + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "<strong>$1</strong>");
        },
     scroll: true,
     scrollHeight: 180
@@ -421,6 +460,10 @@ $.Autocompleter.Cache = function(options) {
                if (!options.matchCase) 
                        s = s.toLowerCase();
                var i = s.indexOf(sub);
+               if (options.matchContains == "word"){
+                       query = $.Autocompleter.defaults.regex_escape(sub.toLowerCase());
+                       i = s.toLowerCase().search("\\b" + query);
+               }
                if (i == -1) return false;
                return i == 0 || options.matchContains;
        };
@@ -738,22 +781,48 @@ $.Autocompleter.Select = function (options, input, select, config) {
        };
 };
 
-$.Autocompleter.Selection = function(field, start, end) {
-       if( field.createTextRange ){
-               var selRange = field.createTextRange();
-               selRange.collapse(true);
-               selRange.moveStart("character", start);
-               selRange.moveEnd("character", end);
-               selRange.select();
-       } else if( field.setSelectionRange ){
-               field.setSelectionRange(start, end);
-       } else {
-               if( field.selectionStart ){
-                       field.selectionStart = start;
-                       field.selectionEnd = end;
+$.fn.selection = function(start, end) {
+       if (start !== undefined) {
+               return this.each(function() {
+                       if( this.createTextRange ){
+                               var selRange = this.createTextRange();
+                               if (end === undefined || start == end) {
+                                       selRange.move("character", start);
+                                       selRange.select();
+                               } else {
+                                       selRange.collapse(true);
+                                       selRange.moveStart("character", start);
+                                       selRange.moveEnd("character", end);
+                                       selRange.select();
+                               }
+                       } else if( this.setSelectionRange ){
+                               this.setSelectionRange(start, end);
+                       } else if( this.selectionStart ){
+                               this.selectionStart = start;
+                               this.selectionEnd = end;
+                       }
+               });
+       }
+       var field = this[0];
+       if ( field.createTextRange ) {
+               var range = document.selection.createRange(),
+                       orig = field.value,
+                       teststring = "<->",
+                       textLength = range.text.length;
+               range.text = teststring;
+               var caretAt = field.value.indexOf(teststring);
+               field.value = orig;
+               this.selection(caretAt, caretAt + textLength);
+               return {
+                       start: caretAt,
+                       end: caretAt + textLength
+               }
+       } else if( field.selectionStart !== undefined ){
+               return {
+                       start: field.selectionStart,
+                       end: field.selectionEnd
                }
        }
-       field.focus();
 };
 
 })(jQuery);
\ No newline at end of file
diff --git a/wolnelektury/static/js/jquery.countdown-de.js b/wolnelektury/static/js/jquery.countdown-de.js
new file mode 100644 (file)
index 0000000..a578c9f
--- /dev/null
@@ -0,0 +1,11 @@
+/* http://keith-wood.name/countdown.html
+   German initialisation for the jQuery countdown extension
+   Written by Keith Wood (kbwood@virginbroadband.com.au) Jan 2008. */
+(function($) {
+       $.countdown.regional['de'] = {
+               labels: ['Jahren', 'Monate', 'Wochen', 'Tage', 'Stunden', 'Minuten', 'Sekunden'],
+               labels1: ['Jahre', 'Monat', 'Woche', 'Tag', 'Stunde', 'Minute', 'Sekunde'],
+               compactLabels: ['J', 'M', 'W', 'T'],
+               timeSeparator: ':', isRTL: false};
+       //$.countdown.setDefaults($.countdown.regional['de']);
+})(jQuery);
diff --git a/wolnelektury/static/js/jquery.countdown-es.js b/wolnelektury/static/js/jquery.countdown-es.js
new file mode 100644 (file)
index 0000000..07fea6d
--- /dev/null
@@ -0,0 +1,11 @@
+/* http://keith-wood.name/countdown.html
+ * Spanish initialisation for the jQuery countdown extension
+ * Written by Sergio Carracedo Martinez webmaster@neodisenoweb.com (2008) */
+(function($) {
+       $.countdown.regional['es'] = {
+               labels: ['Años', 'Meses', 'Semanas', 'Dias', 'Horas', 'Minutos', 'Segundos'],
+               labels1: ['Años', 'Meses', 'Semanas', 'Dias', 'Horas', 'Minutos', 'Segundos'],
+               compactLabels: ['a', 'm', 's', 'g'],
+               timeSeparator: ':', isRTL: false};
+       //$.countdown.setDefaults($.countdown.regional['es']);
+})(jQuery);
diff --git a/wolnelektury/static/js/jquery.countdown-fr.js b/wolnelektury/static/js/jquery.countdown-fr.js
new file mode 100644 (file)
index 0000000..45a5990
--- /dev/null
@@ -0,0 +1,11 @@
+/* http://keith-wood.name/countdown.html
+   French initialisation for the jQuery countdown extension
+   Written by Keith Wood (kbwood{at}iinet.com.au) Jan 2008. */
+(function($) {
+       $.countdown.regional['fr'] = {
+               labels: ['Années', 'Mois', 'Semaines', 'Jours', 'Heures', 'Minutes', 'Secondes'],
+               labels1: ['Année', 'Mois', 'Semaine', 'Jour', 'Heure', 'Minute', 'Seconde'],
+               compactLabels: ['a', 'm', 's', 'j'],
+               timeSeparator: ':', isRTL: false};
+       //$.countdown.setDefaults($.countdown.regional['fr']);
+})(jQuery);
diff --git a/wolnelektury/static/js/jquery.countdown-lt.js b/wolnelektury/static/js/jquery.countdown-lt.js
new file mode 100644 (file)
index 0000000..37afdd4
--- /dev/null
@@ -0,0 +1,11 @@
+/* http://keith-wood.name/countdown.html
+ * Lithuanian localisation for the jQuery countdown extension
+ * Written by Moacir P. de Sá Pereira (moacir{at}gmail.com) (2009) */
+(function($) {
+       $.countdown.regional['lt'] = {
+               labels: ['Metų', 'Mėnesių', 'Savaičių', 'Dienų', 'Valandų', 'Minučių', 'Sekundžių'],
+               labels1: ['Metai', 'Mėnuo', 'Savaitė', 'Diena', 'Valanda', 'Minutė', 'Sekundė'],
+               compactLabels: ['m', 'm', 's', 'd'],
+               timeSeparator: ':', isRTL: false};
+       //$.countdown.setDefaults($.countdown.regional['lt']);
+})(jQuery);
diff --git a/wolnelektury/static/js/jquery.countdown-pl.js b/wolnelektury/static/js/jquery.countdown-pl.js
new file mode 100644 (file)
index 0000000..c881a3d
--- /dev/null
@@ -0,0 +1,19 @@
+/* http://keith-wood.name/countdown.html\r
+ * Polish initialisation for the jQuery countdown extension\r
+ * Written by Pawel Lewtak lewtak@gmail.com (2008) \r
+ * and Radek Czajka radoslaw.czajka@nowoczesnapolska.org.pl (2010) */\r
+(function($) {\r
+       $.countdown.regional['pl'] = {\r
+               labels: ['lat', 'miesięcy', 'tygodni', 'dni', 'godzin', 'minut', 'sekund'],\r
+               labels1: ['rok', 'miesiąc', 'tydzień', 'dzień', 'godzina', 'minuta', 'sekunda'],\r
+        labels2: ['lata', 'miesiące', 'tygodnie', 'dni', 'godziny', 'minuty', 'sekundy'],\r
+               compactLabels: ['l', 'm', 't', 'd'],\r
+               compactLabels1: ['r', 'm', 't', 'd'],\r
+        compactLabels2: ['l', 'm', 't', 'd'],\r
+               timeSeparator: ':', isRTL: false,\r
+               which: function(n){\r
+                       return n==1 ? 1 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 2 : 0;\r
+               }\r
+       };\r
+       //$.countdown.setDefaults($.countdown.regional['pl']);\r
+})(jQuery);
\ No newline at end of file
diff --git a/wolnelektury/static/js/jquery.countdown-ru.js b/wolnelektury/static/js/jquery.countdown-ru.js
new file mode 100644 (file)
index 0000000..83303b3
--- /dev/null
@@ -0,0 +1,11 @@
+/* http://keith-wood.name/countdown.html
+ * Russian initialisation for the jQuery countdown extension
+ * Written by Dominus i3rixon@gmail.com (2008) */
+(function($) {
+       $.countdown.regional['ru'] = {
+               labels: ['Лет', 'Месяцев', 'Недель', 'Дней', 'Часов', 'Минут', 'Секунд'],
+               labels1: ['Год', 'Месяц', 'Неделя', 'День', 'Час', 'Минута', 'Секунда'],
+               compactLabels: ['l', 'm', 'n', 'd'], compactLabels1: ['g', 'm', 'n', 'd'],
+               timeSeparator: ':', isRTL: false};
+       //$.countdown.setDefaults($.countdown.regional['ru']);
+})(jQuery);
diff --git a/wolnelektury/static/js/jquery.countdown.js b/wolnelektury/static/js/jquery.countdown.js
new file mode 100644 (file)
index 0000000..d00dca0
--- /dev/null
@@ -0,0 +1,735 @@
+/* http://keith-wood.name/countdown.html
+   Countdown for jQuery v1.5.7.
+   Written by Keith Wood (kbwood{at}iinet.com.au) January 2008.
+   Dual licensed under the GPL (http://dev.jquery.com/browser/trunk/jquery/GPL-LICENSE.txt) and 
+   MIT (http://dev.jquery.com/browser/trunk/jquery/MIT-LICENSE.txt) licenses. 
+   Please attribute the author if you use it. */
+
+/* Modified by Radek Czajka, Fundacja Nowoczesna Polska (radoslaw.czajka(at)nowoczesnapolska.org.pl) */
+
+/* Display a countdown timer.
+   Attach it with options like:
+   $('div selector').countdown(
+       {until: new Date(2009, 1 - 1, 1, 0, 0, 0), onExpiry: happyNewYear}); */
+
+(function($) { // Hide scope, no $ conflict
+
+/* Countdown manager. */
+function Countdown() {
+       this.regional = []; // Available regional settings, indexed by language code
+       this.regional[''] = { // Default regional settings
+               // The display texts for the counters
+               labels: ['Years', 'Months', 'Weeks', 'Days', 'Hours', 'Minutes', 'Seconds'],
+               // The display texts for the counters if only one
+               labels1: ['Year', 'Month', 'Week', 'Day', 'Hour', 'Minute', 'Second'],
+               compactLabels: ['y', 'm', 'w', 'd'], // The compact texts for the counters
+               timeSeparator: ':', // Separator for time periods
+               isRTL: false, // True for right-to-left languages, false for left-to-right
+               which: function(n) {return n}
+       };
+       this._defaults = {
+               until: null, // new Date(year, mth - 1, day, hr, min, sec) - date/time to count down to
+                       // or numeric for seconds offset, or string for unit offset(s):
+                       // 'Y' years, 'O' months, 'W' weeks, 'D' days, 'H' hours, 'M' minutes, 'S' seconds
+               since: null, // new Date(year, mth - 1, day, hr, min, sec) - date/time to count up from
+                       // or numeric for seconds offset, or string for unit offset(s):
+                       // 'Y' years, 'O' months, 'W' weeks, 'D' days, 'H' hours, 'M' minutes, 'S' seconds
+               timezone: null, // The timezone (hours or minutes from GMT) for the target times,
+                       // or null for client local
+               serverSync: null, // A function to retrieve the current server time for synchronisation
+               format: 'dHMS', // Format for display - upper case for always, lower case only if non-zero,
+                       // 'Y' years, 'O' months, 'W' weeks, 'D' days, 'H' hours, 'M' minutes, 'S' seconds
+               layout: '', // Build your own layout for the countdown
+               compact: false, // True to display in a compact format, false for an expanded one
+               description: '', // The description displayed for the countdown
+               expiryUrl: '', // A URL to load upon expiry, replacing the current page
+               expiryText: '', // Text to display upon expiry, replacing the countdown
+               alwaysExpire: false, // True to trigger onExpiry even if never counted down
+               onExpiry: null, // Callback when the countdown expires -
+                       // receives no parameters and 'this' is the containing division
+               onTick: null, // Callback when the countdown is updated -
+                       // receives int[7] being the breakdown by period (based on format)
+                       // and 'this' is the containing division
+               tickInterval: 1 // Interval (seconds) between onTick callbacks
+       };
+       $.extend(this._defaults, this.regional['']);
+       this._serverSyncs = [];
+}
+
+var PROP_NAME = 'countdown';
+
+var Y = 0; // Years
+var O = 1; // Months
+var W = 2; // Weeks
+var D = 3; // Days
+var H = 4; // Hours
+var M = 5; // Minutes
+var S = 6; // Seconds
+
+$.extend(Countdown.prototype, {
+       /* Class name added to elements to indicate already configured with countdown. */
+       markerClassName: 'hasCountdown',
+       
+       /* Shared timer for all countdowns. */
+       _timer: setInterval(function() { $.countdown._updateTargets(); }, 980),
+       /* List of currently active countdown targets. */
+       _timerTargets: [],
+       
+       /* Override the default settings for all instances of the countdown widget.
+          @param  options  (object) the new settings to use as defaults */
+       setDefaults: function(options) {
+               this._resetExtraLabels(this._defaults, options);
+               extendRemove(this._defaults, options || {});
+       },
+
+       /* Convert a date/time to UTC.
+          @param  tz     (number) the hour or minute offset from GMT, e.g. +9, -360
+          @param  year   (Date) the date/time in that timezone or
+                         (number) the year in that timezone
+          @param  month  (number, optional) the month (0 - 11) (omit if year is a Date)
+          @param  day    (number, optional) the day (omit if year is a Date)
+          @param  hours  (number, optional) the hour (omit if year is a Date)
+          @param  mins   (number, optional) the minute (omit if year is a Date)
+          @param  secs   (number, optional) the second (omit if year is a Date)
+          @param  ms     (number, optional) the millisecond (omit if year is a Date)
+          @return  (Date) the equivalent UTC date/time */
+       UTCDate: function(tz, year, month, day, hours, mins, secs, ms) {
+               if (typeof year == 'object' && year.constructor == Date) {
+                       ms = year.getMilliseconds();
+                       secs = year.getSeconds();
+                       mins = year.getMinutes();
+                       hours = year.getHours();
+                       day = year.getDate();
+                       month = year.getMonth();
+                       year = year.getFullYear();
+               }
+               var d = new Date();
+               d.setUTCFullYear(year);
+               d.setUTCDate(1);
+               d.setUTCMonth(month || 0);
+               d.setUTCDate(day || 1);
+               d.setUTCHours(hours || 0);
+               d.setUTCMinutes((mins || 0) - (Math.abs(tz) < 30 ? tz * 60 : tz));
+               d.setUTCSeconds(secs || 0);
+               d.setUTCMilliseconds(ms || 0);
+               return d;
+       },
+
+       /* Convert a set of periods into seconds.
+          Averaged for months and years.
+          @param  periods  (number[7]) the periods per year/month/week/day/hour/minute/second
+          @return  (number) the corresponding number of seconds */
+       periodsToSeconds: function(periods) {
+               return periods[0] * 31557600 + periods[1] * 2629800 + periods[2] * 604800 +
+                       periods[3] * 86400 + periods[4] * 3600 + periods[5] * 60 + periods[6];
+       },
+
+       /* Retrieve one or more settings values.
+          @param  name  (string, optional) the name of the setting to retrieve
+                        or 'all' for all instance settings or omit for all default settings
+          @return  (any) the requested setting(s) */
+       _settingsCountdown: function(target, name) {
+               if (!name) {
+                       return $.countdown._defaults;
+               }
+               var inst = $.data(target, PROP_NAME);
+               return (name == 'all' ? inst.options : inst.options[name]);
+       },
+
+       /* Attach the countdown widget to a div.
+          @param  target   (element) the containing division
+          @param  options  (object) the initial settings for the countdown */
+       _attachCountdown: function(target, options) {
+               var $target = $(target);
+               if ($target.hasClass(this.markerClassName)) {
+                       return;
+               }
+               $target.addClass(this.markerClassName);
+               var inst = {options: $.extend({}, options),
+                       _periods: [0, 0, 0, 0, 0, 0, 0]};
+               $.data(target, PROP_NAME, inst);
+               this._changeCountdown(target);
+       },
+
+       /* Add a target to the list of active ones.
+          @param  target  (element) the countdown target */
+       _addTarget: function(target) {
+               if (!this._hasTarget(target)) {
+                       this._timerTargets.push(target);
+               }
+       },
+
+       /* See if a target is in the list of active ones.
+          @param  target  (element) the countdown target
+          @return  (boolean) true if present, false if not */
+       _hasTarget: function(target) {
+               return ($.inArray(target, this._timerTargets) > -1);
+       },
+
+       /* Remove a target from the list of active ones.
+          @param  target  (element) the countdown target */
+       _removeTarget: function(target) {
+               this._timerTargets = $.map(this._timerTargets,
+                       function(value) { return (value == target ? null : value); }); // delete entry
+       },
+
+       /* Update each active timer target. */
+       _updateTargets: function() {
+               for (var i = this._timerTargets.length - 1; i >= 0; i--) {
+                       this._updateCountdown(this._timerTargets[i]);
+               }
+       },
+
+       /* Redisplay the countdown with an updated display.
+          @param  target  (jQuery) the containing division
+          @param  inst    (object) the current settings for this instance */
+       _updateCountdown: function(target, inst) {
+               var $target = $(target);
+               inst = inst || $.data(target, PROP_NAME);
+               if (!inst) {
+                       return;
+               }
+               $target.html(this._generateHTML(inst));
+               $target[(this._get(inst, 'isRTL') ? 'add' : 'remove') + 'Class']('countdown_rtl');
+               var onTick = this._get(inst, 'onTick');
+               if (onTick) {
+                       var periods = inst._hold != 'lap' ? inst._periods :
+                               this._calculatePeriods(inst, inst._show, new Date());
+                       var tickInterval = this._get(inst, 'tickInterval');
+                       if (tickInterval == 1 || this.periodsToSeconds(periods) % tickInterval == 0) {
+                               onTick.apply(target, [periods]);
+                       }
+               }
+               var expired = inst._hold != 'pause' &&
+                       (inst._since ? inst._now.getTime() < inst._since.getTime() :
+                       inst._now.getTime() >= inst._until.getTime());
+               if (expired && !inst._expiring) {
+                       inst._expiring = true;
+                       if (this._hasTarget(target) || this._get(inst, 'alwaysExpire')) {
+                               this._removeTarget(target);
+                               var onExpiry = this._get(inst, 'onExpiry');
+                               if (onExpiry) {
+                                       onExpiry.apply(target, []);
+                               }
+                               var expiryText = this._get(inst, 'expiryText');
+                               if (expiryText) {
+                                       var layout = this._get(inst, 'layout');
+                                       inst.options.layout = expiryText;
+                                       this._updateCountdown(target, inst);
+                                       inst.options.layout = layout;
+                               }
+                               var expiryUrl = this._get(inst, 'expiryUrl');
+                               if (expiryUrl) {
+                                       window.location = expiryUrl;
+                               }
+                       }
+                       inst._expiring = false;
+               }
+               else if (inst._hold == 'pause') {
+                       this._removeTarget(target);
+               }
+               $.data(target, PROP_NAME, inst);
+       },
+
+       /* Reconfigure the settings for a countdown div.
+          @param  target   (element) the containing division
+          @param  options  (object) the new settings for the countdown or
+                           (string) an individual property name
+          @param  value    (any) the individual property value
+                           (omit if options is an object) */
+       _changeCountdown: function(target, options, value) {
+               options = options || {};
+               if (typeof options == 'string') {
+                       var name = options;
+                       options = {};
+                       options[name] = value;
+               }
+               var inst = $.data(target, PROP_NAME);
+               if (inst) {
+                       this._resetExtraLabels(inst.options, options);
+                       extendRemove(inst.options, options);
+                       this._adjustSettings(target, inst);
+                       $.data(target, PROP_NAME, inst);
+                       var now = new Date();
+                       if ((inst._since && inst._since < now) ||
+                                       (inst._until && inst._until > now)) {
+                               this._addTarget(target);
+                       }
+                       this._updateCountdown(target, inst);
+               }
+       },
+
+       /* Reset any extra labelsn and compactLabelsn entries if changing labels.
+          @param  base     (object) the options to be updated
+          @param  options  (object) the new option values */
+       _resetExtraLabels: function(base, options) {
+               var changingLabels = false;
+               for (var n in options) {
+                       if (n.match(/[Ll]abels/)) {
+                               changingLabels = true;
+                               break;
+                       }
+               }
+               if (changingLabels) {
+                       for (var n in base) { // Remove custom numbered labels
+                               if (n.match(/[Ll]abels[0-9]/)) {
+                                       base[n] = null;
+                               }
+                       }
+               }
+       },
+       
+       /* Calculate interal settings for an instance.
+          @param  target  (element) the containing division
+          @param  inst    (object) the current settings for this instance */
+       _adjustSettings: function(target, inst) {
+               var now;
+               var serverSync = this._get(inst, 'serverSync');
+               var serverOffset = 0;
+               var serverEntry = null;
+               for (var i = 0; i < this._serverSyncs.length; i++) {
+                       if (this._serverSyncs[i][0] == serverSync) {
+                               serverEntry = this._serverSyncs[i][1];
+                               break;
+                       }
+               }
+               if (serverEntry != null) {
+                       serverOffset = (serverSync ? serverEntry : 0);
+                       now = new Date();
+               }
+               else {
+                       var serverResult = (serverSync ? serverSync.apply(target, []) : null);
+                       now = new Date();
+                       serverOffset = (serverResult ? now.getTime() - serverResult.getTime() : 0);
+                       this._serverSyncs.push([serverSync, serverOffset]);
+               }
+               var timezone = this._get(inst, 'timezone');
+               timezone = (timezone == null ? -now.getTimezoneOffset() : timezone);
+               inst._since = this._get(inst, 'since');
+               if (inst._since != null) {
+                       inst._since = this.UTCDate(timezone, this._determineTime(inst._since, null));
+                       if (inst._since && serverOffset) {
+                               inst._since.setMilliseconds(inst._since.getMilliseconds() + serverOffset);
+                       }
+               }
+               inst._until = this.UTCDate(timezone, this._determineTime(this._get(inst, 'until'), now));
+               if (serverOffset) {
+                       inst._until.setMilliseconds(inst._until.getMilliseconds() + serverOffset);
+               }
+               inst._show = this._determineShow(inst);
+       },
+
+       /* Remove the countdown widget from a div.
+          @param  target  (element) the containing division */
+       _destroyCountdown: function(target) {
+               var $target = $(target);
+               if (!$target.hasClass(this.markerClassName)) {
+                       return;
+               }
+               this._removeTarget(target);
+               $target.removeClass(this.markerClassName).empty();
+               $.removeData(target, PROP_NAME);
+       },
+
+       /* Pause a countdown widget at the current time.
+          Stop it running but remember and display the current time.
+          @param  target  (element) the containing division */
+       _pauseCountdown: function(target) {
+               this._hold(target, 'pause');
+       },
+
+       /* Pause a countdown widget at the current time.
+          Stop the display but keep the countdown running.
+          @param  target  (element) the containing division */
+       _lapCountdown: function(target) {
+               this._hold(target, 'lap');
+       },
+
+       /* Resume a paused countdown widget.
+          @param  target  (element) the containing division */
+       _resumeCountdown: function(target) {
+               this._hold(target, null);
+       },
+
+       /* Pause or resume a countdown widget.
+          @param  target  (element) the containing division
+          @param  hold    (string) the new hold setting */
+       _hold: function(target, hold) {
+               var inst = $.data(target, PROP_NAME);
+               if (inst) {
+                       if (inst._hold == 'pause' && !hold) {
+                               inst._periods = inst._savePeriods;
+                               var sign = (inst._since ? '-' : '+');
+                               inst[inst._since ? '_since' : '_until'] =
+                                       this._determineTime(sign + inst._periods[0] + 'y' +
+                                               sign + inst._periods[1] + 'o' + sign + inst._periods[2] + 'w' +
+                                               sign + inst._periods[3] + 'd' + sign + inst._periods[4] + 'h' + 
+                                               sign + inst._periods[5] + 'm' + sign + inst._periods[6] + 's');
+                               this._addTarget(target);
+                       }
+                       inst._hold = hold;
+                       inst._savePeriods = (hold == 'pause' ? inst._periods : null);
+                       $.data(target, PROP_NAME, inst);
+                       this._updateCountdown(target, inst);
+               }
+       },
+
+       /* Return the current time periods.
+          @param  target  (element) the containing division
+          @return  (number[7]) the current periods for the countdown */
+       _getTimesCountdown: function(target) {
+               var inst = $.data(target, PROP_NAME);
+               return (!inst ? null : (!inst._hold ? inst._periods :
+                       this._calculatePeriods(inst, inst._show, new Date())));
+       },
+
+       /* Get a setting value, defaulting if necessary.
+          @param  inst  (object) the current settings for this instance
+          @param  name  (string) the name of the required setting
+          @return  (any) the setting's value or a default if not overridden */
+       _get: function(inst, name) {
+               return (inst.options[name] != null ?
+                       inst.options[name] : $.countdown._defaults[name]);
+       },
+
+       /* A time may be specified as an exact value or a relative one.
+          @param  setting      (string or number or Date) - the date/time value
+                               as a relative or absolute value
+          @param  defaultTime  (Date) the date/time to use if no other is supplied
+          @return  (Date) the corresponding date/time */
+       _determineTime: function(setting, defaultTime) {
+               var offsetNumeric = function(offset) { // e.g. +300, -2
+                       var time = new Date();
+                       time.setTime(time.getTime() + offset * 1000);
+                       return time;
+               };
+               var offsetString = function(offset) { // e.g. '+2d', '-4w', '+3h +30m'
+                       offset = offset.toLowerCase();
+                       var time = new Date();
+                       var year = time.getFullYear();
+                       var month = time.getMonth();
+                       var day = time.getDate();
+                       var hour = time.getHours();
+                       var minute = time.getMinutes();
+                       var second = time.getSeconds();
+                       var pattern = /([+-]?[0-9]+)\s*(s|m|h|d|w|o|y)?/g;
+                       var matches = pattern.exec(offset);
+                       while (matches) {
+                               switch (matches[2] || 's') {
+                                       case 's': second += parseInt(matches[1], 10); break;
+                                       case 'm': minute += parseInt(matches[1], 10); break;
+                                       case 'h': hour += parseInt(matches[1], 10); break;
+                                       case 'd': day += parseInt(matches[1], 10); break;
+                                       case 'w': day += parseInt(matches[1], 10) * 7; break;
+                                       case 'o':
+                                               month += parseInt(matches[1], 10); 
+                                               day = Math.min(day, $.countdown._getDaysInMonth(year, month));
+                                               break;
+                                       case 'y':
+                                               year += parseInt(matches[1], 10);
+                                               day = Math.min(day, $.countdown._getDaysInMonth(year, month));
+                                               break;
+                               }
+                               matches = pattern.exec(offset);
+                       }
+                       return new Date(year, month, day, hour, minute, second, 0);
+               };
+               var time = (setting == null ? defaultTime :
+                       (typeof setting == 'string' ? offsetString(setting) :
+                       (typeof setting == 'number' ? offsetNumeric(setting) : setting)));
+               if (time) time.setMilliseconds(0);
+               return time;
+       },
+
+       /* Determine the number of days in a month.
+          @param  year   (number) the year
+          @param  month  (number) the month
+          @return  (number) the days in that month */
+       _getDaysInMonth: function(year, month) {
+               return 32 - new Date(year, month, 32).getDate();
+       },
+
+       /* Generate the HTML to display the countdown widget.
+          @param  inst  (object) the current settings for this instance
+          @return  (string) the new HTML for the countdown display */
+       _generateHTML: function(inst) {
+               // Determine what to show
+               inst._periods = periods = (inst._hold ? inst._periods :
+                       this._calculatePeriods(inst, inst._show, new Date()));
+               // Show all 'asNeeded' after first non-zero value
+               var shownNonZero = false;
+               var showCount = 0;
+               var show = $.extend({}, inst._show);
+               for (var period = 0; period < inst._show.length; period++) {
+                       shownNonZero |= (inst._show[period] == '?' && periods[period] > 0);
+                       show[period] = (inst._show[period] == '?' && !shownNonZero ? null : inst._show[period]);
+                       showCount += (show[period] ? 1 : 0);
+               }
+               var compact = this._get(inst, 'compact');
+               var layout = this._get(inst, 'layout');
+               var labels = (compact ? this._get(inst, 'compactLabels') : this._get(inst, 'labels'));
+               var timeSeparator = this._get(inst, 'timeSeparator');
+               var description = this._get(inst, 'description') || '';
+               var showCompact = function(period) {
+            var which = $.countdown._get(inst, 'which');
+            if (which) {
+                var labelsNum = $.countdown._get(inst, 'compactLabels' + which(periods[period]));
+            }
+                       return (show[period] ? periods[period] +
+                               (labelsNum ? labelsNum[period] : labels[period]) + ' ' : '');
+               };
+               var showFull = function(period) {
+                       var which = $.countdown._get(inst, 'which');
+                       if (which) {
+                               var labelsNum = $.countdown._get(inst, 'labels' + which(periods[period]));
+                       }
+                       return (show[period] ?
+                               '<span class="countdown_section"><span class="countdown_amount">' +
+                               periods[period] + '</span><br/>' +
+                               (labelsNum ? labelsNum[period] : labels[period]) + '</span>' : '');
+               };
+               return (layout ? this._buildLayout(inst, show, layout, compact) :
+                       ((compact ? // Compact version
+                       '<span class="countdown_row countdown_amount' +
+                       (inst._hold ? ' countdown_holding' : '') + '">' + 
+                       showCompact(Y) + showCompact(O) + showCompact(W) + showCompact(D) + 
+                       (show[H] ? this._minDigits(periods[H], 2) : '') +
+                       (show[M] ? (show[H] ? timeSeparator : '') +
+                       this._minDigits(periods[M], 2) : '') +
+                       (show[S] ? (show[H] || show[M] ? timeSeparator : '') +
+                       this._minDigits(periods[S], 2) : '') :
+                       // Full version
+                       '<span class="countdown_row countdown_show' + showCount +
+                       (inst._hold ? ' countdown_holding' : '') + '">' +
+                       showFull(Y) + showFull(O) + showFull(W) + showFull(D) +
+                       showFull(H) + showFull(M) + showFull(S)) + '</span>' +
+                       (description ? '<span class="countdown_row countdown_descr">' + description + '</span>' : '')));
+       },
+
+       /* Construct a custom layout.
+          @param  inst     (object) the current settings for this instance
+          @param  show     (string[7]) flags indicating which periods are requested
+          @param  layout   (string) the customised layout
+          @param  compact  (boolean) true if using compact labels
+          @return  (string) the custom HTML */
+       _buildLayout: function(inst, show, layout, compact) {
+               var labels = this._get(inst, (compact ? 'compactLabels' : 'labels'));
+               var labelFor = function(index) {
+                       return ($.countdown._get(inst,
+                               (compact ? 'compactLabels' : 'labels') + inst._periods[index]) ||
+                               labels)[index];
+               };
+               var digit = function(value, position) {
+                       return Math.floor(value / position) % 10;
+               };
+               var subs = {desc: this._get(inst, 'description'), sep: this._get(inst, 'timeSeparator'),
+                       yl: labelFor(Y), yn: inst._periods[Y], ynn: this._minDigits(inst._periods[Y], 2),
+                       ynnn: this._minDigits(inst._periods[Y], 3), y1: digit(inst._periods[Y], 1),
+                       y10: digit(inst._periods[Y], 10), y100: digit(inst._periods[Y], 100),
+                       y1000: digit(inst._periods[Y], 1000),
+                       ol: labelFor(O), on: inst._periods[O], onn: this._minDigits(inst._periods[O], 2),
+                       onnn: this._minDigits(inst._periods[O], 3), o1: digit(inst._periods[O], 1),
+                       o10: digit(inst._periods[O], 10), o100: digit(inst._periods[O], 100),
+                       o1000: digit(inst._periods[O], 1000),
+                       wl: labelFor(W), wn: inst._periods[W], wnn: this._minDigits(inst._periods[W], 2),
+                       wnnn: this._minDigits(inst._periods[W], 3), w1: digit(inst._periods[W], 1),
+                       w10: digit(inst._periods[W], 10), w100: digit(inst._periods[W], 100),
+                       w1000: digit(inst._periods[W], 1000),
+                       dl: labelFor(D), dn: inst._periods[D], dnn: this._minDigits(inst._periods[D], 2),
+                       dnnn: this._minDigits(inst._periods[D], 3), d1: digit(inst._periods[D], 1),
+                       d10: digit(inst._periods[D], 10), d100: digit(inst._periods[D], 100),
+                       d1000: digit(inst._periods[D], 1000),
+                       hl: labelFor(H), hn: inst._periods[H], hnn: this._minDigits(inst._periods[H], 2),
+                       hnnn: this._minDigits(inst._periods[H], 3), h1: digit(inst._periods[H], 1),
+                       h10: digit(inst._periods[H], 10), h100: digit(inst._periods[H], 100),
+                       h1000: digit(inst._periods[H], 1000),
+                       ml: labelFor(M), mn: inst._periods[M], mnn: this._minDigits(inst._periods[M], 2),
+                       mnnn: this._minDigits(inst._periods[M], 3), m1: digit(inst._periods[M], 1),
+                       m10: digit(inst._periods[M], 10), m100: digit(inst._periods[M], 100),
+                       m1000: digit(inst._periods[M], 1000),
+                       sl: labelFor(S), sn: inst._periods[S], snn: this._minDigits(inst._periods[S], 2),
+                       snnn: this._minDigits(inst._periods[S], 3), s1: digit(inst._periods[S], 1),
+                       s10: digit(inst._periods[S], 10), s100: digit(inst._periods[S], 100),
+                       s1000: digit(inst._periods[S], 1000)};
+               var html = layout;
+               // Replace period containers: {p<}...{p>}
+               for (var i = 0; i < 7; i++) {
+                       var period = 'yowdhms'.charAt(i);
+                       var re = new RegExp('\\{' + period + '<\\}(.*)\\{' + period + '>\\}', 'g');
+                       html = html.replace(re, (show[i] ? '$1' : ''));
+               }
+               // Replace period values: {pn}
+               $.each(subs, function(n, v) {
+                       var re = new RegExp('\\{' + n + '\\}', 'g');
+                       html = html.replace(re, v);
+               });
+               return html;
+       },
+
+       /* Ensure a numeric value has at least n digits for display.
+          @param  value  (number) the value to display
+          @param  len    (number) the minimum length
+          @return  (string) the display text */
+       _minDigits: function(value, len) {
+               value = '' + value;
+               if (value.length >= len) {
+                       return value;
+               }
+               value = '0000000000' + value;
+               return value.substr(value.length - len);
+       },
+
+       /* Translate the format into flags for each period.
+          @param  inst  (object) the current settings for this instance
+          @return  (string[7]) flags indicating which periods are requested (?) or
+                   required (!) by year, month, week, day, hour, minute, second */
+       _determineShow: function(inst) {
+               var format = this._get(inst, 'format');
+               var show = [];
+               show[Y] = (format.match('y') ? '?' : (format.match('Y') ? '!' : null));
+               show[O] = (format.match('o') ? '?' : (format.match('O') ? '!' : null));
+               show[W] = (format.match('w') ? '?' : (format.match('W') ? '!' : null));
+               show[D] = (format.match('d') ? '?' : (format.match('D') ? '!' : null));
+               show[H] = (format.match('h') ? '?' : (format.match('H') ? '!' : null));
+               show[M] = (format.match('m') ? '?' : (format.match('M') ? '!' : null));
+               show[S] = (format.match('s') ? '?' : (format.match('S') ? '!' : null));
+               return show;
+       },
+       
+       /* Calculate the requested periods between now and the target time.
+          @param  inst  (object) the current settings for this instance
+          @param  show  (string[7]) flags indicating which periods are requested/required
+          @param  now   (Date) the current date and time
+          @return  (number[7]) the current time periods (always positive)
+                   by year, month, week, day, hour, minute, second */
+       _calculatePeriods: function(inst, show, now) {
+               // Find endpoints
+               inst._now = now;
+               inst._now.setMilliseconds(0);
+               var until = new Date(inst._now.getTime());
+               if (inst._since) {
+                       if (now.getTime() < inst._since.getTime()) {
+                               inst._now = now = until;
+                       }
+                       else {
+                               now = inst._since;
+                       }
+               }
+               else {
+                       until.setTime(inst._until.getTime());
+                       if (now.getTime() > inst._until.getTime()) {
+                               inst._now = now = until;
+                       }
+               }
+               // Calculate differences by period
+               var periods = [0, 0, 0, 0, 0, 0, 0];
+               if (show[Y] || show[O]) {
+                       // Treat end of months as the same
+                       var lastNow = $.countdown._getDaysInMonth(now.getFullYear(), now.getMonth());
+                       var lastUntil = $.countdown._getDaysInMonth(until.getFullYear(), until.getMonth());
+                       var sameDay = (until.getDate() == now.getDate() ||
+                               (until.getDate() >= Math.min(lastNow, lastUntil) &&
+                               now.getDate() >= Math.min(lastNow, lastUntil)));
+                       var getSecs = function(date) {
+                               return (date.getHours() * 60 + date.getMinutes()) * 60 + date.getSeconds();
+                       };
+                       var months = Math.max(0,
+                               (until.getFullYear() - now.getFullYear()) * 12 + until.getMonth() - now.getMonth() +
+                               ((until.getDate() < now.getDate() && !sameDay) ||
+                               (sameDay && getSecs(until) < getSecs(now)) ? -1 : 0));
+                       periods[Y] = (show[Y] ? Math.floor(months / 12) : 0);
+                       periods[O] = (show[O] ? months - periods[Y] * 12 : 0);
+                       // Adjust for months difference and end of month if necessary
+                       var adjustDate = function(date, offset, last) {
+                               var wasLastDay = (date.getDate() == last);
+                               var lastDay = $.countdown._getDaysInMonth(date.getFullYear() + offset * periods[Y],
+                                       date.getMonth() + offset * periods[O]);
+                               if (date.getDate() > lastDay) {
+                                       date.setDate(lastDay);
+                               }
+                               date.setFullYear(date.getFullYear() + offset * periods[Y]);
+                               date.setMonth(date.getMonth() + offset * periods[O]);
+                               if (wasLastDay) {
+                                       date.setDate(lastDay);
+                               }
+                               return date;
+                       };
+                       if (inst._since) {
+                               until = adjustDate(until, -1, lastUntil);
+                       }
+                       else {
+                               now = adjustDate(new Date(now.getTime()), +1, lastNow);
+                       }
+               }
+               var diff = Math.floor((until.getTime() - now.getTime()) / 1000);
+               var extractPeriod = function(period, numSecs) {
+                       periods[period] = (show[period] ? Math.floor(diff / numSecs) : 0);
+                       diff -= periods[period] * numSecs;
+               };
+               extractPeriod(W, 604800);
+               extractPeriod(D, 86400);
+               extractPeriod(H, 3600);
+               extractPeriod(M, 60);
+               extractPeriod(S, 1);
+               if (diff > 0 && !inst._since) { // Round up if left overs
+                       var multiplier = [1, 12, 4.3482, 7, 24, 60, 60];
+                       var lastShown = S;
+                       var max = 1;
+                       for (var period = S; period >= Y; period--) {
+                               if (show[period]) {
+                                       if (periods[lastShown] >= max) {
+                                               periods[lastShown] = 0;
+                                               diff = 1;
+                                       }
+                                       if (diff > 0) {
+                                               periods[period]++;
+                                               diff = 0;
+                                               lastShown = period;
+                                               max = 1;
+                                       }
+                               }
+                               max *= multiplier[period];
+                       }
+               }
+               return periods;
+       }
+});
+
+/* jQuery extend now ignores nulls!
+   @param  target  (object) the object to update
+   @param  props   (object) the new settings
+   @return  (object) the updated object */
+function extendRemove(target, props) {
+       $.extend(target, props);
+       for (var name in props) {
+               if (props[name] == null) {
+                       target[name] = null;
+               }
+       }
+       return target;
+}
+
+/* Process the countdown functionality for a jQuery selection.
+   @param  command  (string) the command to run (optional, default 'attach')
+   @param  options  (object) the new settings to use for these countdown instances
+   @return  (jQuery) for chaining further calls */
+$.fn.countdown = function(options) {
+       var otherArgs = Array.prototype.slice.call(arguments, 1);
+       if (options == 'getTimes' || options == 'settings') {
+               return $.countdown['_' + options + 'Countdown'].
+                       apply($.countdown, [this[0]].concat(otherArgs));
+       }
+       return this.each(function() {
+               if (typeof options == 'string') {
+                       $.countdown['_' + options + 'Countdown'].apply($.countdown, [this].concat(otherArgs));
+               }
+               else {
+                       $.countdown._attachCountdown(this, options);
+               }
+       });
+};
+
+/* Initialise the countdown functionality. */
+$.countdown = new Countdown(); // singleton instance
+
+})(jQuery);
\ No newline at end of file
index 82b98e1..7c24308 100644 (file)
-/*
- * jQuery 1.2.6 - New Wave Javascript
+/*!
+ * jQuery JavaScript Library v1.4.2
+ * http://jquery.com/
  *
- * Copyright (c) 2008 John Resig (jquery.com)
- * Dual licensed under the MIT (MIT-LICENSE.txt)
- * and GPL (GPL-LICENSE.txt) licenses.
+ * Copyright 2010, John Resig
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
  *
- * $Date: 2008-05-24 14:22:17 -0400 (Sat, 24 May 2008) $
- * $Rev: 5685 $
+ * Includes Sizzle.js
+ * http://sizzlejs.com/
+ * Copyright 2010, The Dojo Foundation
+ * Released under the MIT, BSD, and GPL Licenses.
+ *
+ * Date: Sat Feb 13 22:33:48 2010 -0500
  */
-(function(){var _jQuery=window.jQuery,_$=window.$;var jQuery=window.jQuery=window.$=function(selector,context){return new jQuery.fn.init(selector,context);};var quickExpr=/^[^<]*(<(.|\s)+>)[^>]*$|^#(\w+)$/,isSimple=/^.[^:#\[\.]*$/,undefined;jQuery.fn=jQuery.prototype={init:function(selector,context){selector=selector||document;if(selector.nodeType){this[0]=selector;this.length=1;return this;}if(typeof selector=="string"){var match=quickExpr.exec(selector);if(match&&(match[1]||!context)){if(match[1])selector=jQuery.clean([match[1]],context);else{var elem=document.getElementById(match[3]);if(elem){if(elem.id!=match[3])return jQuery().find(selector);return jQuery(elem);}selector=[];}}else
-return jQuery(context).find(selector);}else if(jQuery.isFunction(selector))return jQuery(document)[jQuery.fn.ready?"ready":"load"](selector);return this.setArray(jQuery.makeArray(selector));},jquery:"1.2.6",size:function(){return this.length;},length:0,get:function(num){return num==undefined?jQuery.makeArray(this):this[num];},pushStack:function(elems){var ret=jQuery(elems);ret.prevObject=this;return ret;},setArray:function(elems){this.length=0;Array.prototype.push.apply(this,elems);return this;},each:function(callback,args){return jQuery.each(this,callback,args);},index:function(elem){var ret=-1;return jQuery.inArray(elem&&elem.jquery?elem[0]:elem,this);},attr:function(name,value,type){var options=name;if(name.constructor==String)if(value===undefined)return this[0]&&jQuery[type||"attr"](this[0],name);else{options={};options[name]=value;}return this.each(function(i){for(name in options)jQuery.attr(type?this.style:this,name,jQuery.prop(this,options[name],type,i,name));});},css:function(key,value){if((key=='width'||key=='height')&&parseFloat(value)<0)value=undefined;return this.attr(key,value,"curCSS");},text:function(text){if(typeof text!="object"&&text!=null)return this.empty().append((this[0]&&this[0].ownerDocument||document).createTextNode(text));var ret="";jQuery.each(text||this,function(){jQuery.each(this.childNodes,function(){if(this.nodeType!=8)ret+=this.nodeType!=1?this.nodeValue:jQuery.fn.text([this]);});});return ret;},wrapAll:function(html){if(this[0])jQuery(html,this[0].ownerDocument).clone().insertBefore(this[0]).map(function(){var elem=this;while(elem.firstChild)elem=elem.firstChild;return elem;}).append(this);return this;},wrapInner:function(html){return this.each(function(){jQuery(this).contents().wrapAll(html);});},wrap:function(html){return this.each(function(){jQuery(this).wrapAll(html);});},append:function(){return this.domManip(arguments,true,false,function(elem){if(this.nodeType==1)this.appendChild(elem);});},prepend:function(){return this.domManip(arguments,true,true,function(elem){if(this.nodeType==1)this.insertBefore(elem,this.firstChild);});},before:function(){return this.domManip(arguments,false,false,function(elem){this.parentNode.insertBefore(elem,this);});},after:function(){return this.domManip(arguments,false,true,function(elem){this.parentNode.insertBefore(elem,this.nextSibling);});},end:function(){return this.prevObject||jQuery([]);},find:function(selector){var elems=jQuery.map(this,function(elem){return jQuery.find(selector,elem);});return this.pushStack(/[^+>] [^+>]/.test(selector)||selector.indexOf("..")>-1?jQuery.unique(elems):elems);},clone:function(events){var ret=this.map(function(){if(jQuery.browser.msie&&!jQuery.isXMLDoc(this)){var clone=this.cloneNode(true),container=document.createElement("div");container.appendChild(clone);return jQuery.clean([container.innerHTML])[0];}else
-return this.cloneNode(true);});var clone=ret.find("*").andSelf().each(function(){if(this[expando]!=undefined)this[expando]=null;});if(events===true)this.find("*").andSelf().each(function(i){if(this.nodeType==3)return;var events=jQuery.data(this,"events");for(var type in events)for(var handler in events[type])jQuery.event.add(clone[i],type,events[type][handler],events[type][handler].data);});return ret;},filter:function(selector){return this.pushStack(jQuery.isFunction(selector)&&jQuery.grep(this,function(elem,i){return selector.call(elem,i);})||jQuery.multiFilter(selector,this));},not:function(selector){if(selector.constructor==String)if(isSimple.test(selector))return this.pushStack(jQuery.multiFilter(selector,this,true));else
-selector=jQuery.multiFilter(selector,this);var isArrayLike=selector.length&&selector[selector.length-1]!==undefined&&!selector.nodeType;return this.filter(function(){return isArrayLike?jQuery.inArray(this,selector)<0:this!=selector;});},add:function(selector){return this.pushStack(jQuery.unique(jQuery.merge(this.get(),typeof selector=='string'?jQuery(selector):jQuery.makeArray(selector))));},is:function(selector){return!!selector&&jQuery.multiFilter(selector,this).length>0;},hasClass:function(selector){return this.is("."+selector);},val:function(value){if(value==undefined){if(this.length){var elem=this[0];if(jQuery.nodeName(elem,"select")){var index=elem.selectedIndex,values=[],options=elem.options,one=elem.type=="select-one";if(index<0)return null;for(var i=one?index:0,max=one?index+1:options.length;i<max;i++){var option=options[i];if(option.selected){value=jQuery.browser.msie&&!option.attributes.value.specified?option.text:option.value;if(one)return value;values.push(value);}}return values;}else
-return(this[0].value||"").replace(/\r/g,"");}return undefined;}if(value.constructor==Number)value+='';return this.each(function(){if(this.nodeType!=1)return;if(value.constructor==Array&&/radio|checkbox/.test(this.type))this.checked=(jQuery.inArray(this.value,value)>=0||jQuery.inArray(this.name,value)>=0);else if(jQuery.nodeName(this,"select")){var values=jQuery.makeArray(value);jQuery("option",this).each(function(){this.selected=(jQuery.inArray(this.value,values)>=0||jQuery.inArray(this.text,values)>=0);});if(!values.length)this.selectedIndex=-1;}else
-this.value=value;});},html:function(value){return value==undefined?(this[0]?this[0].innerHTML:null):this.empty().append(value);},replaceWith:function(value){return this.after(value).remove();},eq:function(i){return this.slice(i,i+1);},slice:function(){return this.pushStack(Array.prototype.slice.apply(this,arguments));},map:function(callback){return this.pushStack(jQuery.map(this,function(elem,i){return callback.call(elem,i,elem);}));},andSelf:function(){return this.add(this.prevObject);},data:function(key,value){var parts=key.split(".");parts[1]=parts[1]?"."+parts[1]:"";if(value===undefined){var data=this.triggerHandler("getData"+parts[1]+"!",[parts[0]]);if(data===undefined&&this.length)data=jQuery.data(this[0],key);return data===undefined&&parts[1]?this.data(parts[0]):data;}else
-return this.trigger("setData"+parts[1]+"!",[parts[0],value]).each(function(){jQuery.data(this,key,value);});},removeData:function(key){return this.each(function(){jQuery.removeData(this,key);});},domManip:function(args,table,reverse,callback){var clone=this.length>1,elems;return this.each(function(){if(!elems){elems=jQuery.clean(args,this.ownerDocument);if(reverse)elems.reverse();}var obj=this;if(table&&jQuery.nodeName(this,"table")&&jQuery.nodeName(elems[0],"tr"))obj=this.getElementsByTagName("tbody")[0]||this.appendChild(this.ownerDocument.createElement("tbody"));var scripts=jQuery([]);jQuery.each(elems,function(){var elem=clone?jQuery(this).clone(true)[0]:this;if(jQuery.nodeName(elem,"script"))scripts=scripts.add(elem);else{if(elem.nodeType==1)scripts=scripts.add(jQuery("script",elem).remove());callback.call(obj,elem);}});scripts.each(evalScript);});}};jQuery.fn.init.prototype=jQuery.fn;function evalScript(i,elem){if(elem.src)jQuery.ajax({url:elem.src,async:false,dataType:"script"});else
-jQuery.globalEval(elem.text||elem.textContent||elem.innerHTML||"");if(elem.parentNode)elem.parentNode.removeChild(elem);}function now(){return+new Date;}jQuery.extend=jQuery.fn.extend=function(){var target=arguments[0]||{},i=1,length=arguments.length,deep=false,options;if(target.constructor==Boolean){deep=target;target=arguments[1]||{};i=2;}if(typeof target!="object"&&typeof target!="function")target={};if(length==i){target=this;--i;}for(;i<length;i++)if((options=arguments[i])!=null)for(var name in options){var src=target[name],copy=options[name];if(target===copy)continue;if(deep&&copy&&typeof copy=="object"&&!copy.nodeType)target[name]=jQuery.extend(deep,src||(copy.length!=null?[]:{}),copy);else if(copy!==undefined)target[name]=copy;}return target;};var expando="jQuery"+now(),uuid=0,windowData={},exclude=/z-?index|font-?weight|opacity|zoom|line-?height/i,defaultView=document.defaultView||{};jQuery.extend({noConflict:function(deep){window.$=_$;if(deep)window.jQuery=_jQuery;return jQuery;},isFunction:function(fn){return!!fn&&typeof fn!="string"&&!fn.nodeName&&fn.constructor!=Array&&/^[\s[]?function/.test(fn+"");},isXMLDoc:function(elem){return elem.documentElement&&!elem.body||elem.tagName&&elem.ownerDocument&&!elem.ownerDocument.body;},globalEval:function(data){data=jQuery.trim(data);if(data){var head=document.getElementsByTagName("head")[0]||document.documentElement,script=document.createElement("script");script.type="text/javascript";if(jQuery.browser.msie)script.text=data;else
-script.appendChild(document.createTextNode(data));head.insertBefore(script,head.firstChild);head.removeChild(script);}},nodeName:function(elem,name){return elem.nodeName&&elem.nodeName.toUpperCase()==name.toUpperCase();},cache:{},data:function(elem,name,data){elem=elem==window?windowData:elem;var id=elem[expando];if(!id)id=elem[expando]=++uuid;if(name&&!jQuery.cache[id])jQuery.cache[id]={};if(data!==undefined)jQuery.cache[id][name]=data;return name?jQuery.cache[id][name]:id;},removeData:function(elem,name){elem=elem==window?windowData:elem;var id=elem[expando];if(name){if(jQuery.cache[id]){delete jQuery.cache[id][name];name="";for(name in jQuery.cache[id])break;if(!name)jQuery.removeData(elem);}}else{try{delete elem[expando];}catch(e){if(elem.removeAttribute)elem.removeAttribute(expando);}delete jQuery.cache[id];}},each:function(object,callback,args){var name,i=0,length=object.length;if(args){if(length==undefined){for(name in object)if(callback.apply(object[name],args)===false)break;}else
-for(;i<length;)if(callback.apply(object[i++],args)===false)break;}else{if(length==undefined){for(name in object)if(callback.call(object[name],name,object[name])===false)break;}else
-for(var value=object[0];i<length&&callback.call(value,i,value)!==false;value=object[++i]){}}return object;},prop:function(elem,value,type,i,name){if(jQuery.isFunction(value))value=value.call(elem,i);return value&&value.constructor==Number&&type=="curCSS"&&!exclude.test(name)?value+"px":value;},className:{add:function(elem,classNames){jQuery.each((classNames||"").split(/\s+/),function(i,className){if(elem.nodeType==1&&!jQuery.className.has(elem.className,className))elem.className+=(elem.className?" ":"")+className;});},remove:function(elem,classNames){if(elem.nodeType==1)elem.className=classNames!=undefined?jQuery.grep(elem.className.split(/\s+/),function(className){return!jQuery.className.has(classNames,className);}).join(" "):"";},has:function(elem,className){return jQuery.inArray(className,(elem.className||elem).toString().split(/\s+/))>-1;}},swap:function(elem,options,callback){var old={};for(var name in options){old[name]=elem.style[name];elem.style[name]=options[name];}callback.call(elem);for(var name in options)elem.style[name]=old[name];},css:function(elem,name,force){if(name=="width"||name=="height"){var val,props={position:"absolute",visibility:"hidden",display:"block"},which=name=="width"?["Left","Right"]:["Top","Bottom"];function getWH(){val=name=="width"?elem.offsetWidth:elem.offsetHeight;var padding=0,border=0;jQuery.each(which,function(){padding+=parseFloat(jQuery.curCSS(elem,"padding"+this,true))||0;border+=parseFloat(jQuery.curCSS(elem,"border"+this+"Width",true))||0;});val-=Math.round(padding+border);}if(jQuery(elem).is(":visible"))getWH();else
-jQuery.swap(elem,props,getWH);return Math.max(0,val);}return jQuery.curCSS(elem,name,force);},curCSS:function(elem,name,force){var ret,style=elem.style;function color(elem){if(!jQuery.browser.safari)return false;var ret=defaultView.getComputedStyle(elem,null);return!ret||ret.getPropertyValue("color")=="";}if(name=="opacity"&&jQuery.browser.msie){ret=jQuery.attr(style,"opacity");return ret==""?"1":ret;}if(jQuery.browser.opera&&name=="display"){var save=style.outline;style.outline="0 solid black";style.outline=save;}if(name.match(/float/i))name=styleFloat;if(!force&&style&&style[name])ret=style[name];else if(defaultView.getComputedStyle){if(name.match(/float/i))name="float";name=name.replace(/([A-Z])/g,"-$1").toLowerCase();var computedStyle=defaultView.getComputedStyle(elem,null);if(computedStyle&&!color(elem))ret=computedStyle.getPropertyValue(name);else{var swap=[],stack=[],a=elem,i=0;for(;a&&color(a);a=a.parentNode)stack.unshift(a);for(;i<stack.length;i++)if(color(stack[i])){swap[i]=stack[i].style.display;stack[i].style.display="block";}ret=name=="display"&&swap[stack.length-1]!=null?"none":(computedStyle&&computedStyle.getPropertyValue(name))||"";for(i=0;i<swap.length;i++)if(swap[i]!=null)stack[i].style.display=swap[i];}if(name=="opacity"&&ret=="")ret="1";}else if(elem.currentStyle){var camelCase=name.replace(/\-(\w)/g,function(all,letter){return letter.toUpperCase();});ret=elem.currentStyle[name]||elem.currentStyle[camelCase];if(!/^\d+(px)?$/i.test(ret)&&/^\d/.test(ret)){var left=style.left,rsLeft=elem.runtimeStyle.left;elem.runtimeStyle.left=elem.currentStyle.left;style.left=ret||0;ret=style.pixelLeft+"px";style.left=left;elem.runtimeStyle.left=rsLeft;}}return ret;},clean:function(elems,context){var ret=[];context=context||document;if(typeof context.createElement=='undefined')context=context.ownerDocument||context[0]&&context[0].ownerDocument||document;jQuery.each(elems,function(i,elem){if(!elem)return;if(elem.constructor==Number)elem+='';if(typeof elem=="string"){elem=elem.replace(/(<(\w+)[^>]*?)\/>/g,function(all,front,tag){return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i)?all:front+"></"+tag+">";});var tags=jQuery.trim(elem).toLowerCase(),div=context.createElement("div");var wrap=!tags.indexOf("<opt")&&[1,"<select multiple='multiple'>","</select>"]||!tags.indexOf("<leg")&&[1,"<fieldset>","</fieldset>"]||tags.match(/^<(thead|tbody|tfoot|colg|cap)/)&&[1,"<table>","</table>"]||!tags.indexOf("<tr")&&[2,"<table><tbody>","</tbody></table>"]||(!tags.indexOf("<td")||!tags.indexOf("<th"))&&[3,"<table><tbody><tr>","</tr></tbody></table>"]||!tags.indexOf("<col")&&[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"]||jQuery.browser.msie&&[1,"div<div>","</div>"]||[0,"",""];div.innerHTML=wrap[1]+elem+wrap[2];while(wrap[0]--)div=div.lastChild;if(jQuery.browser.msie){var tbody=!tags.indexOf("<table")&&tags.indexOf("<tbody")<0?div.firstChild&&div.firstChild.childNodes:wrap[1]=="<table>"&&tags.indexOf("<tbody")<0?div.childNodes:[];for(var j=tbody.length-1;j>=0;--j)if(jQuery.nodeName(tbody[j],"tbody")&&!tbody[j].childNodes.length)tbody[j].parentNode.removeChild(tbody[j]);if(/^\s/.test(elem))div.insertBefore(context.createTextNode(elem.match(/^\s*/)[0]),div.firstChild);}elem=jQuery.makeArray(div.childNodes);}if(elem.length===0&&(!jQuery.nodeName(elem,"form")&&!jQuery.nodeName(elem,"select")))return;if(elem[0]==undefined||jQuery.nodeName(elem,"form")||elem.options)ret.push(elem);else
-ret=jQuery.merge(ret,elem);});return ret;},attr:function(elem,name,value){if(!elem||elem.nodeType==3||elem.nodeType==8)return undefined;var notxml=!jQuery.isXMLDoc(elem),set=value!==undefined,msie=jQuery.browser.msie;name=notxml&&jQuery.props[name]||name;if(elem.tagName){var special=/href|src|style/.test(name);if(name=="selected"&&jQuery.browser.safari)elem.parentNode.selectedIndex;if(name in elem&&notxml&&!special){if(set){if(name=="type"&&jQuery.nodeName(elem,"input")&&elem.parentNode)throw"type property can't be changed";elem[name]=value;}if(jQuery.nodeName(elem,"form")&&elem.getAttributeNode(name))return elem.getAttributeNode(name).nodeValue;return elem[name];}if(msie&&notxml&&name=="style")return jQuery.attr(elem.style,"cssText",value);if(set)elem.setAttribute(name,""+value);var attr=msie&&notxml&&special?elem.getAttribute(name,2):elem.getAttribute(name);return attr===null?undefined:attr;}if(msie&&name=="opacity"){if(set){elem.zoom=1;elem.filter=(elem.filter||"").replace(/alpha\([^)]*\)/,"")+(parseInt(value)+''=="NaN"?"":"alpha(opacity="+value*100+")");}return elem.filter&&elem.filter.indexOf("opacity=")>=0?(parseFloat(elem.filter.match(/opacity=([^)]*)/)[1])/100)+'':"";}name=name.replace(/-([a-z])/ig,function(all,letter){return letter.toUpperCase();});if(set)elem[name]=value;return elem[name];},trim:function(text){return(text||"").replace(/^\s+|\s+$/g,"");},makeArray:function(array){var ret=[];if(array!=null){var i=array.length;if(i==null||array.split||array.setInterval||array.call)ret[0]=array;else
-while(i)ret[--i]=array[i];}return ret;},inArray:function(elem,array){for(var i=0,length=array.length;i<length;i++)if(array[i]===elem)return i;return-1;},merge:function(first,second){var i=0,elem,pos=first.length;if(jQuery.browser.msie){while(elem=second[i++])if(elem.nodeType!=8)first[pos++]=elem;}else
-while(elem=second[i++])first[pos++]=elem;return first;},unique:function(array){var ret=[],done={};try{for(var i=0,length=array.length;i<length;i++){var id=jQuery.data(array[i]);if(!done[id]){done[id]=true;ret.push(array[i]);}}}catch(e){ret=array;}return ret;},grep:function(elems,callback,inv){var ret=[];for(var i=0,length=elems.length;i<length;i++)if(!inv!=!callback(elems[i],i))ret.push(elems[i]);return ret;},map:function(elems,callback){var ret=[];for(var i=0,length=elems.length;i<length;i++){var value=callback(elems[i],i);if(value!=null)ret[ret.length]=value;}return ret.concat.apply([],ret);}});var userAgent=navigator.userAgent.toLowerCase();jQuery.browser={version:(userAgent.match(/.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/)||[])[1],safari:/webkit/.test(userAgent),opera:/opera/.test(userAgent),msie:/msie/.test(userAgent)&&!/opera/.test(userAgent),mozilla:/mozilla/.test(userAgent)&&!/(compatible|webkit)/.test(userAgent)};var styleFloat=jQuery.browser.msie?"styleFloat":"cssFloat";jQuery.extend({boxModel:!jQuery.browser.msie||document.compatMode=="CSS1Compat",props:{"for":"htmlFor","class":"className","float":styleFloat,cssFloat:styleFloat,styleFloat:styleFloat,readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing"}});jQuery.each({parent:function(elem){return elem.parentNode;},parents:function(elem){return jQuery.dir(elem,"parentNode");},next:function(elem){return jQuery.nth(elem,2,"nextSibling");},prev:function(elem){return jQuery.nth(elem,2,"previousSibling");},nextAll:function(elem){return jQuery.dir(elem,"nextSibling");},prevAll:function(elem){return jQuery.dir(elem,"previousSibling");},siblings:function(elem){return jQuery.sibling(elem.parentNode.firstChild,elem);},children:function(elem){return jQuery.sibling(elem.firstChild);},contents:function(elem){return jQuery.nodeName(elem,"iframe")?elem.contentDocument||elem.contentWindow.document:jQuery.makeArray(elem.childNodes);}},function(name,fn){jQuery.fn[name]=function(selector){var ret=jQuery.map(this,fn);if(selector&&typeof selector=="string")ret=jQuery.multiFilter(selector,ret);return this.pushStack(jQuery.unique(ret));};});jQuery.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(name,original){jQuery.fn[name]=function(){var args=arguments;return this.each(function(){for(var i=0,length=args.length;i<length;i++)jQuery(args[i])[original](this);});};});jQuery.each({removeAttr:function(name){jQuery.attr(this,name,"");if(this.nodeType==1)this.removeAttribute(name);},addClass:function(classNames){jQuery.className.add(this,classNames);},removeClass:function(classNames){jQuery.className.remove(this,classNames);},toggleClass:function(classNames){jQuery.className[jQuery.className.has(this,classNames)?"remove":"add"](this,classNames);},remove:function(selector){if(!selector||jQuery.filter(selector,[this]).r.length){jQuery("*",this).add(this).each(function(){jQuery.event.remove(this);jQuery.removeData(this);});if(this.parentNode)this.parentNode.removeChild(this);}},empty:function(){jQuery(">*",this).remove();while(this.firstChild)this.removeChild(this.firstChild);}},function(name,fn){jQuery.fn[name]=function(){return this.each(fn,arguments);};});jQuery.each(["Height","Width"],function(i,name){var type=name.toLowerCase();jQuery.fn[type]=function(size){return this[0]==window?jQuery.browser.opera&&document.body["client"+name]||jQuery.browser.safari&&window["inner"+name]||document.compatMode=="CSS1Compat"&&document.documentElement["client"+name]||document.body["client"+name]:this[0]==document?Math.max(Math.max(document.body["scroll"+name],document.documentElement["scroll"+name]),Math.max(document.body["offset"+name],document.documentElement["offset"+name])):size==undefined?(this.length?jQuery.css(this[0],type):null):this.css(type,size.constructor==String?size:size+"px");};});function num(elem,prop){return elem[0]&&parseInt(jQuery.curCSS(elem[0],prop,true),10)||0;}var chars=jQuery.browser.safari&&parseInt(jQuery.browser.version)<417?"(?:[\\w*_-]|\\\\.)":"(?:[\\w\u0128-\uFFFF*_-]|\\\\.)",quickChild=new RegExp("^>\\s*("+chars+"+)"),quickID=new RegExp("^("+chars+"+)(#)("+chars+"+)"),quickClass=new RegExp("^([#.]?)("+chars+"*)");jQuery.extend({expr:{"":function(a,i,m){return m[2]=="*"||jQuery.nodeName(a,m[2]);},"#":function(a,i,m){return a.getAttribute("id")==m[2];},":":{lt:function(a,i,m){return i<m[3]-0;},gt:function(a,i,m){return i>m[3]-0;},nth:function(a,i,m){return m[3]-0==i;},eq:function(a,i,m){return m[3]-0==i;},first:function(a,i){return i==0;},last:function(a,i,m,r){return i==r.length-1;},even:function(a,i){return i%2==0;},odd:function(a,i){return i%2;},"first-child":function(a){return a.parentNode.getElementsByTagName("*")[0]==a;},"last-child":function(a){return jQuery.nth(a.parentNode.lastChild,1,"previousSibling")==a;},"only-child":function(a){return!jQuery.nth(a.parentNode.lastChild,2,"previousSibling");},parent:function(a){return a.firstChild;},empty:function(a){return!a.firstChild;},contains:function(a,i,m){return(a.textContent||a.innerText||jQuery(a).text()||"").indexOf(m[3])>=0;},visible:function(a){return"hidden"!=a.type&&jQuery.css(a,"display")!="none"&&jQuery.css(a,"visibility")!="hidden";},hidden:function(a){return"hidden"==a.type||jQuery.css(a,"display")=="none"||jQuery.css(a,"visibility")=="hidden";},enabled:function(a){return!a.disabled;},disabled:function(a){return a.disabled;},checked:function(a){return a.checked;},selected:function(a){return a.selected||jQuery.attr(a,"selected");},text:function(a){return"text"==a.type;},radio:function(a){return"radio"==a.type;},checkbox:function(a){return"checkbox"==a.type;},file:function(a){return"file"==a.type;},password:function(a){return"password"==a.type;},submit:function(a){return"submit"==a.type;},image:function(a){return"image"==a.type;},reset:function(a){return"reset"==a.type;},button:function(a){return"button"==a.type||jQuery.nodeName(a,"button");},input:function(a){return/input|select|textarea|button/i.test(a.nodeName);},has:function(a,i,m){return jQuery.find(m[3],a).length;},header:function(a){return/h\d/i.test(a.nodeName);},animated:function(a){return jQuery.grep(jQuery.timers,function(fn){return a==fn.elem;}).length;}}},parse:[/^(\[) *@?([\w-]+) *([!*$^~=]*) *('?"?)(.*?)\4 *\]/,/^(:)([\w-]+)\("?'?(.*?(\(.*?\))?[^(]*?)"?'?\)/,new RegExp("^([:.#]*)("+chars+"+)")],multiFilter:function(expr,elems,not){var old,cur=[];while(expr&&expr!=old){old=expr;var f=jQuery.filter(expr,elems,not);expr=f.t.replace(/^\s*,\s*/,"");cur=not?elems=f.r:jQuery.merge(cur,f.r);}return cur;},find:function(t,context){if(typeof t!="string")return[t];if(context&&context.nodeType!=1&&context.nodeType!=9)return[];context=context||document;var ret=[context],done=[],last,nodeName;while(t&&last!=t){var r=[];last=t;t=jQuery.trim(t);var foundToken=false,re=quickChild,m=re.exec(t);if(m){nodeName=m[1].toUpperCase();for(var i=0;ret[i];i++)for(var c=ret[i].firstChild;c;c=c.nextSibling)if(c.nodeType==1&&(nodeName=="*"||c.nodeName.toUpperCase()==nodeName))r.push(c);ret=r;t=t.replace(re,"");if(t.indexOf(" ")==0)continue;foundToken=true;}else{re=/^([>+~])\s*(\w*)/i;if((m=re.exec(t))!=null){r=[];var merge={};nodeName=m[2].toUpperCase();m=m[1];for(var j=0,rl=ret.length;j<rl;j++){var n=m=="~"||m=="+"?ret[j].nextSibling:ret[j].firstChild;for(;n;n=n.nextSibling)if(n.nodeType==1){var id=jQuery.data(n);if(m=="~"&&merge[id])break;if(!nodeName||n.nodeName.toUpperCase()==nodeName){if(m=="~")merge[id]=true;r.push(n);}if(m=="+")break;}}ret=r;t=jQuery.trim(t.replace(re,""));foundToken=true;}}if(t&&!foundToken){if(!t.indexOf(",")){if(context==ret[0])ret.shift();done=jQuery.merge(done,ret);r=ret=[context];t=" "+t.substr(1,t.length);}else{var re2=quickID;var m=re2.exec(t);if(m){m=[0,m[2],m[3],m[1]];}else{re2=quickClass;m=re2.exec(t);}m[2]=m[2].replace(/\\/g,"");var elem=ret[ret.length-1];if(m[1]=="#"&&elem&&elem.getElementById&&!jQuery.isXMLDoc(elem)){var oid=elem.getElementById(m[2]);if((jQuery.browser.msie||jQuery.browser.opera)&&oid&&typeof oid.id=="string"&&oid.id!=m[2])oid=jQuery('[@id="'+m[2]+'"]',elem)[0];ret=r=oid&&(!m[3]||jQuery.nodeName(oid,m[3]))?[oid]:[];}else{for(var i=0;ret[i];i++){var tag=m[1]=="#"&&m[3]?m[3]:m[1]!=""||m[0]==""?"*":m[2];if(tag=="*"&&ret[i].nodeName.toLowerCase()=="object")tag="param";r=jQuery.merge(r,ret[i].getElementsByTagName(tag));}if(m[1]==".")r=jQuery.classFilter(r,m[2]);if(m[1]=="#"){var tmp=[];for(var i=0;r[i];i++)if(r[i].getAttribute("id")==m[2]){tmp=[r[i]];break;}r=tmp;}ret=r;}t=t.replace(re2,"");}}if(t){var val=jQuery.filter(t,r);ret=r=val.r;t=jQuery.trim(val.t);}}if(t)ret=[];if(ret&&context==ret[0])ret.shift();done=jQuery.merge(done,ret);return done;},classFilter:function(r,m,not){m=" "+m+" ";var tmp=[];for(var i=0;r[i];i++){var pass=(" "+r[i].className+" ").indexOf(m)>=0;if(!not&&pass||not&&!pass)tmp.push(r[i]);}return tmp;},filter:function(t,r,not){var last;while(t&&t!=last){last=t;var p=jQuery.parse,m;for(var i=0;p[i];i++){m=p[i].exec(t);if(m){t=t.substring(m[0].length);m[2]=m[2].replace(/\\/g,"");break;}}if(!m)break;if(m[1]==":"&&m[2]=="not")r=isSimple.test(m[3])?jQuery.filter(m[3],r,true).r:jQuery(r).not(m[3]);else if(m[1]==".")r=jQuery.classFilter(r,m[2],not);else if(m[1]=="["){var tmp=[],type=m[3];for(var i=0,rl=r.length;i<rl;i++){var a=r[i],z=a[jQuery.props[m[2]]||m[2]];if(z==null||/href|src|selected/.test(m[2]))z=jQuery.attr(a,m[2])||'';if((type==""&&!!z||type=="="&&z==m[5]||type=="!="&&z!=m[5]||type=="^="&&z&&!z.indexOf(m[5])||type=="$="&&z.substr(z.length-m[5].length)==m[5]||(type=="*="||type=="~=")&&z.indexOf(m[5])>=0)^not)tmp.push(a);}r=tmp;}else if(m[1]==":"&&m[2]=="nth-child"){var merge={},tmp=[],test=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(m[3]=="even"&&"2n"||m[3]=="odd"&&"2n+1"||!/\D/.test(m[3])&&"0n+"+m[3]||m[3]),first=(test[1]+(test[2]||1))-0,last=test[3]-0;for(var i=0,rl=r.length;i<rl;i++){var node=r[i],parentNode=node.parentNode,id=jQuery.data(parentNode);if(!merge[id]){var c=1;for(var n=parentNode.firstChild;n;n=n.nextSibling)if(n.nodeType==1)n.nodeIndex=c++;merge[id]=true;}var add=false;if(first==0){if(node.nodeIndex==last)add=true;}else if((node.nodeIndex-last)%first==0&&(node.nodeIndex-last)/first>=0)add=true;if(add^not)tmp.push(node);}r=tmp;}else{var fn=jQuery.expr[m[1]];if(typeof fn=="object")fn=fn[m[2]];if(typeof fn=="string")fn=eval("false||function(a,i){return "+fn+";}");r=jQuery.grep(r,function(elem,i){return fn(elem,i,m,r);},not);}}return{r:r,t:t};},dir:function(elem,dir){var matched=[],cur=elem[dir];while(cur&&cur!=document){if(cur.nodeType==1)matched.push(cur);cur=cur[dir];}return matched;},nth:function(cur,result,dir,elem){result=result||1;var num=0;for(;cur;cur=cur[dir])if(cur.nodeType==1&&++num==result)break;return cur;},sibling:function(n,elem){var r=[];for(;n;n=n.nextSibling){if(n.nodeType==1&&n!=elem)r.push(n);}return r;}});jQuery.event={add:function(elem,types,handler,data){if(elem.nodeType==3||elem.nodeType==8)return;if(jQuery.browser.msie&&elem.setInterval)elem=window;if(!handler.guid)handler.guid=this.guid++;if(data!=undefined){var fn=handler;handler=this.proxy(fn,function(){return fn.apply(this,arguments);});handler.data=data;}var events=jQuery.data(elem,"events")||jQuery.data(elem,"events",{}),handle=jQuery.data(elem,"handle")||jQuery.data(elem,"handle",function(){if(typeof jQuery!="undefined"&&!jQuery.event.triggered)return jQuery.event.handle.apply(arguments.callee.elem,arguments);});handle.elem=elem;jQuery.each(types.split(/\s+/),function(index,type){var parts=type.split(".");type=parts[0];handler.type=parts[1];var handlers=events[type];if(!handlers){handlers=events[type]={};if(!jQuery.event.special[type]||jQuery.event.special[type].setup.call(elem)===false){if(elem.addEventListener)elem.addEventListener(type,handle,false);else if(elem.attachEvent)elem.attachEvent("on"+type,handle);}}handlers[handler.guid]=handler;jQuery.event.global[type]=true;});elem=null;},guid:1,global:{},remove:function(elem,types,handler){if(elem.nodeType==3||elem.nodeType==8)return;var events=jQuery.data(elem,"events"),ret,index;if(events){if(types==undefined||(typeof types=="string"&&types.charAt(0)=="."))for(var type in events)this.remove(elem,type+(types||""));else{if(types.type){handler=types.handler;types=types.type;}jQuery.each(types.split(/\s+/),function(index,type){var parts=type.split(".");type=parts[0];if(events[type]){if(handler)delete events[type][handler.guid];else
-for(handler in events[type])if(!parts[1]||events[type][handler].type==parts[1])delete events[type][handler];for(ret in events[type])break;if(!ret){if(!jQuery.event.special[type]||jQuery.event.special[type].teardown.call(elem)===false){if(elem.removeEventListener)elem.removeEventListener(type,jQuery.data(elem,"handle"),false);else if(elem.detachEvent)elem.detachEvent("on"+type,jQuery.data(elem,"handle"));}ret=null;delete events[type];}}});}for(ret in events)break;if(!ret){var handle=jQuery.data(elem,"handle");if(handle)handle.elem=null;jQuery.removeData(elem,"events");jQuery.removeData(elem,"handle");}}},trigger:function(type,data,elem,donative,extra){data=jQuery.makeArray(data);if(type.indexOf("!")>=0){type=type.slice(0,-1);var exclusive=true;}if(!elem){if(this.global[type])jQuery("*").add([window,document]).trigger(type,data);}else{if(elem.nodeType==3||elem.nodeType==8)return undefined;var val,ret,fn=jQuery.isFunction(elem[type]||null),event=!data[0]||!data[0].preventDefault;if(event){data.unshift({type:type,target:elem,preventDefault:function(){},stopPropagation:function(){},timeStamp:now()});data[0][expando]=true;}data[0].type=type;if(exclusive)data[0].exclusive=true;var handle=jQuery.data(elem,"handle");if(handle)val=handle.apply(elem,data);if((!fn||(jQuery.nodeName(elem,'a')&&type=="click"))&&elem["on"+type]&&elem["on"+type].apply(elem,data)===false)val=false;if(event)data.shift();if(extra&&jQuery.isFunction(extra)){ret=extra.apply(elem,val==null?data:data.concat(val));if(ret!==undefined)val=ret;}if(fn&&donative!==false&&val!==false&&!(jQuery.nodeName(elem,'a')&&type=="click")){this.triggered=true;try{elem[type]();}catch(e){}}this.triggered=false;}return val;},handle:function(event){var val,ret,namespace,all,handlers;event=arguments[0]=jQuery.event.fix(event||window.event);namespace=event.type.split(".");event.type=namespace[0];namespace=namespace[1];all=!namespace&&!event.exclusive;handlers=(jQuery.data(this,"events")||{})[event.type];for(var j in handlers){var handler=handlers[j];if(all||handler.type==namespace){event.handler=handler;event.data=handler.data;ret=handler.apply(this,arguments);if(val!==false)val=ret;if(ret===false){event.preventDefault();event.stopPropagation();}}}return val;},fix:function(event){if(event[expando]==true)return event;var originalEvent=event;event={originalEvent:originalEvent};var props="altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey newValue originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target timeStamp toElement type view wheelDelta which".split(" ");for(var i=props.length;i;i--)event[props[i]]=originalEvent[props[i]];event[expando]=true;event.preventDefault=function(){if(originalEvent.preventDefault)originalEvent.preventDefault();originalEvent.returnValue=false;};event.stopPropagation=function(){if(originalEvent.stopPropagation)originalEvent.stopPropagation();originalEvent.cancelBubble=true;};event.timeStamp=event.timeStamp||now();if(!event.target)event.target=event.srcElement||document;if(event.target.nodeType==3)event.target=event.target.parentNode;if(!event.relatedTarget&&event.fromElement)event.relatedTarget=event.fromElement==event.target?event.toElement:event.fromElement;if(event.pageX==null&&event.clientX!=null){var doc=document.documentElement,body=document.body;event.pageX=event.clientX+(doc&&doc.scrollLeft||body&&body.scrollLeft||0)-(doc.clientLeft||0);event.pageY=event.clientY+(doc&&doc.scrollTop||body&&body.scrollTop||0)-(doc.clientTop||0);}if(!event.which&&((event.charCode||event.charCode===0)?event.charCode:event.keyCode))event.which=event.charCode||event.keyCode;if(!event.metaKey&&event.ctrlKey)event.metaKey=event.ctrlKey;if(!event.which&&event.button)event.which=(event.button&1?1:(event.button&2?3:(event.button&4?2:0)));return event;},proxy:function(fn,proxy){proxy.guid=fn.guid=fn.guid||proxy.guid||this.guid++;return proxy;},special:{ready:{setup:function(){bindReady();return;},teardown:function(){return;}},mouseenter:{setup:function(){if(jQuery.browser.msie)return false;jQuery(this).bind("mouseover",jQuery.event.special.mouseenter.handler);return true;},teardown:function(){if(jQuery.browser.msie)return false;jQuery(this).unbind("mouseover",jQuery.event.special.mouseenter.handler);return true;},handler:function(event){if(withinElement(event,this))return true;event.type="mouseenter";return jQuery.event.handle.apply(this,arguments);}},mouseleave:{setup:function(){if(jQuery.browser.msie)return false;jQuery(this).bind("mouseout",jQuery.event.special.mouseleave.handler);return true;},teardown:function(){if(jQuery.browser.msie)return false;jQuery(this).unbind("mouseout",jQuery.event.special.mouseleave.handler);return true;},handler:function(event){if(withinElement(event,this))return true;event.type="mouseleave";return jQuery.event.handle.apply(this,arguments);}}}};jQuery.fn.extend({bind:function(type,data,fn){return type=="unload"?this.one(type,data,fn):this.each(function(){jQuery.event.add(this,type,fn||data,fn&&data);});},one:function(type,data,fn){var one=jQuery.event.proxy(fn||data,function(event){jQuery(this).unbind(event,one);return(fn||data).apply(this,arguments);});return this.each(function(){jQuery.event.add(this,type,one,fn&&data);});},unbind:function(type,fn){return this.each(function(){jQuery.event.remove(this,type,fn);});},trigger:function(type,data,fn){return this.each(function(){jQuery.event.trigger(type,data,this,true,fn);});},triggerHandler:function(type,data,fn){return this[0]&&jQuery.event.trigger(type,data,this[0],false,fn);},toggle:function(fn){var args=arguments,i=1;while(i<args.length)jQuery.event.proxy(fn,args[i++]);return this.click(jQuery.event.proxy(fn,function(event){this.lastToggle=(this.lastToggle||0)%i;event.preventDefault();return args[this.lastToggle++].apply(this,arguments)||false;}));},hover:function(fnOver,fnOut){return this.bind('mouseenter',fnOver).bind('mouseleave',fnOut);},ready:function(fn){bindReady();if(jQuery.isReady)fn.call(document,jQuery);else
-jQuery.readyList.push(function(){return fn.call(this,jQuery);});return this;}});jQuery.extend({isReady:false,readyList:[],ready:function(){if(!jQuery.isReady){jQuery.isReady=true;if(jQuery.readyList){jQuery.each(jQuery.readyList,function(){this.call(document);});jQuery.readyList=null;}jQuery(document).triggerHandler("ready");}}});var readyBound=false;function bindReady(){if(readyBound)return;readyBound=true;if(document.addEventListener&&!jQuery.browser.opera)document.addEventListener("DOMContentLoaded",jQuery.ready,false);if(jQuery.browser.msie&&window==top)(function(){if(jQuery.isReady)return;try{document.documentElement.doScroll("left");}catch(error){setTimeout(arguments.callee,0);return;}jQuery.ready();})();if(jQuery.browser.opera)document.addEventListener("DOMContentLoaded",function(){if(jQuery.isReady)return;for(var i=0;i<document.styleSheets.length;i++)if(document.styleSheets[i].disabled){setTimeout(arguments.callee,0);return;}jQuery.ready();},false);if(jQuery.browser.safari){var numStyles;(function(){if(jQuery.isReady)return;if(document.readyState!="loaded"&&document.readyState!="complete"){setTimeout(arguments.callee,0);return;}if(numStyles===undefined)numStyles=jQuery("style, link[rel=stylesheet]").length;if(document.styleSheets.length!=numStyles){setTimeout(arguments.callee,0);return;}jQuery.ready();})();}jQuery.event.add(window,"load",jQuery.ready);}jQuery.each(("blur,focus,load,resize,scroll,unload,click,dblclick,"+"mousedown,mouseup,mousemove,mouseover,mouseout,change,select,"+"submit,keydown,keypress,keyup,error").split(","),function(i,name){jQuery.fn[name]=function(fn){return fn?this.bind(name,fn):this.trigger(name);};});var withinElement=function(event,elem){var parent=event.relatedTarget;while(parent&&parent!=elem)try{parent=parent.parentNode;}catch(error){parent=elem;}return parent==elem;};jQuery(window).bind("unload",function(){jQuery("*").add(document).unbind();});jQuery.fn.extend({_load:jQuery.fn.load,load:function(url,params,callback){if(typeof url!='string')return this._load(url);var off=url.indexOf(" ");if(off>=0){var selector=url.slice(off,url.length);url=url.slice(0,off);}callback=callback||function(){};var type="GET";if(params)if(jQuery.isFunction(params)){callback=params;params=null;}else{params=jQuery.param(params);type="POST";}var self=this;jQuery.ajax({url:url,type:type,dataType:"html",data:params,complete:function(res,status){if(status=="success"||status=="notmodified")self.html(selector?jQuery("<div/>").append(res.responseText.replace(/<script(.|\s)*?\/script>/g,"")).find(selector):res.responseText);self.each(callback,[res.responseText,status,res]);}});return this;},serialize:function(){return jQuery.param(this.serializeArray());},serializeArray:function(){return this.map(function(){return jQuery.nodeName(this,"form")?jQuery.makeArray(this.elements):this;}).filter(function(){return this.name&&!this.disabled&&(this.checked||/select|textarea/i.test(this.nodeName)||/text|hidden|password/i.test(this.type));}).map(function(i,elem){var val=jQuery(this).val();return val==null?null:val.constructor==Array?jQuery.map(val,function(val,i){return{name:elem.name,value:val};}):{name:elem.name,value:val};}).get();}});jQuery.each("ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","),function(i,o){jQuery.fn[o]=function(f){return this.bind(o,f);};});var jsc=now();jQuery.extend({get:function(url,data,callback,type){if(jQuery.isFunction(data)){callback=data;data=null;}return jQuery.ajax({type:"GET",url:url,data:data,success:callback,dataType:type});},getScript:function(url,callback){return jQuery.get(url,null,callback,"script");},getJSON:function(url,data,callback){return jQuery.get(url,data,callback,"json");},post:function(url,data,callback,type){if(jQuery.isFunction(data)){callback=data;data={};}return jQuery.ajax({type:"POST",url:url,data:data,success:callback,dataType:type});},ajaxSetup:function(settings){jQuery.extend(jQuery.ajaxSettings,settings);},ajaxSettings:{url:location.href,global:true,type:"GET",timeout:0,contentType:"application/x-www-form-urlencoded",processData:true,async:true,data:null,username:null,password:null,accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},ajax:function(s){s=jQuery.extend(true,s,jQuery.extend(true,{},jQuery.ajaxSettings,s));var jsonp,jsre=/=\?(&|$)/g,status,data,type=s.type.toUpperCase();if(s.data&&s.processData&&typeof s.data!="string")s.data=jQuery.param(s.data);if(s.dataType=="jsonp"){if(type=="GET"){if(!s.url.match(jsre))s.url+=(s.url.match(/\?/)?"&":"?")+(s.jsonp||"callback")+"=?";}else if(!s.data||!s.data.match(jsre))s.data=(s.data?s.data+"&":"")+(s.jsonp||"callback")+"=?";s.dataType="json";}if(s.dataType=="json"&&(s.data&&s.data.match(jsre)||s.url.match(jsre))){jsonp="jsonp"+jsc++;if(s.data)s.data=(s.data+"").replace(jsre,"="+jsonp+"$1");s.url=s.url.replace(jsre,"="+jsonp+"$1");s.dataType="script";window[jsonp]=function(tmp){data=tmp;success();complete();window[jsonp]=undefined;try{delete window[jsonp];}catch(e){}if(head)head.removeChild(script);};}if(s.dataType=="script"&&s.cache==null)s.cache=false;if(s.cache===false&&type=="GET"){var ts=now();var ret=s.url.replace(/(\?|&)_=.*?(&|$)/,"$1_="+ts+"$2");s.url=ret+((ret==s.url)?(s.url.match(/\?/)?"&":"?")+"_="+ts:"");}if(s.data&&type=="GET"){s.url+=(s.url.match(/\?/)?"&":"?")+s.data;s.data=null;}if(s.global&&!jQuery.active++)jQuery.event.trigger("ajaxStart");var remote=/^(?:\w+:)?\/\/([^\/?#]+)/;if(s.dataType=="script"&&type=="GET"&&remote.test(s.url)&&remote.exec(s.url)[1]!=location.host){var head=document.getElementsByTagName("head")[0];var script=document.createElement("script");script.src=s.url;if(s.scriptCharset)script.charset=s.scriptCharset;if(!jsonp){var done=false;script.onload=script.onreadystatechange=function(){if(!done&&(!this.readyState||this.readyState=="loaded"||this.readyState=="complete")){done=true;success();complete();head.removeChild(script);}};}head.appendChild(script);return undefined;}var requestDone=false;var xhr=window.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):new XMLHttpRequest();if(s.username)xhr.open(type,s.url,s.async,s.username,s.password);else
-xhr.open(type,s.url,s.async);try{if(s.data)xhr.setRequestHeader("Content-Type",s.contentType);if(s.ifModified)xhr.setRequestHeader("If-Modified-Since",jQuery.lastModified[s.url]||"Thu, 01 Jan 1970 00:00:00 GMT");xhr.setRequestHeader("X-Requested-With","XMLHttpRequest");xhr.setRequestHeader("Accept",s.dataType&&s.accepts[s.dataType]?s.accepts[s.dataType]+", */*":s.accepts._default);}catch(e){}if(s.beforeSend&&s.beforeSend(xhr,s)===false){s.global&&jQuery.active--;xhr.abort();return false;}if(s.global)jQuery.event.trigger("ajaxSend",[xhr,s]);var onreadystatechange=function(isTimeout){if(!requestDone&&xhr&&(xhr.readyState==4||isTimeout=="timeout")){requestDone=true;if(ival){clearInterval(ival);ival=null;}status=isTimeout=="timeout"&&"timeout"||!jQuery.httpSuccess(xhr)&&"error"||s.ifModified&&jQuery.httpNotModified(xhr,s.url)&&"notmodified"||"success";if(status=="success"){try{data=jQuery.httpData(xhr,s.dataType,s.dataFilter);}catch(e){status="parsererror";}}if(status=="success"){var modRes;try{modRes=xhr.getResponseHeader("Last-Modified");}catch(e){}if(s.ifModified&&modRes)jQuery.lastModified[s.url]=modRes;if(!jsonp)success();}else
-jQuery.handleError(s,xhr,status);complete();if(s.async)xhr=null;}};if(s.async){var ival=setInterval(onreadystatechange,13);if(s.timeout>0)setTimeout(function(){if(xhr){xhr.abort();if(!requestDone)onreadystatechange("timeout");}},s.timeout);}try{xhr.send(s.data);}catch(e){jQuery.handleError(s,xhr,null,e);}if(!s.async)onreadystatechange();function success(){if(s.success)s.success(data,status);if(s.global)jQuery.event.trigger("ajaxSuccess",[xhr,s]);}function complete(){if(s.complete)s.complete(xhr,status);if(s.global)jQuery.event.trigger("ajaxComplete",[xhr,s]);if(s.global&&!--jQuery.active)jQuery.event.trigger("ajaxStop");}return xhr;},handleError:function(s,xhr,status,e){if(s.error)s.error(xhr,status,e);if(s.global)jQuery.event.trigger("ajaxError",[xhr,s,e]);},active:0,httpSuccess:function(xhr){try{return!xhr.status&&location.protocol=="file:"||(xhr.status>=200&&xhr.status<300)||xhr.status==304||xhr.status==1223||jQuery.browser.safari&&xhr.status==undefined;}catch(e){}return false;},httpNotModified:function(xhr,url){try{var xhrRes=xhr.getResponseHeader("Last-Modified");return xhr.status==304||xhrRes==jQuery.lastModified[url]||jQuery.browser.safari&&xhr.status==undefined;}catch(e){}return false;},httpData:function(xhr,type,filter){var ct=xhr.getResponseHeader("content-type"),xml=type=="xml"||!type&&ct&&ct.indexOf("xml")>=0,data=xml?xhr.responseXML:xhr.responseText;if(xml&&data.documentElement.tagName=="parsererror")throw"parsererror";if(filter)data=filter(data,type);if(type=="script")jQuery.globalEval(data);if(type=="json")data=eval("("+data+")");return data;},param:function(a){var s=[];if(a.constructor==Array||a.jquery)jQuery.each(a,function(){s.push(encodeURIComponent(this.name)+"="+encodeURIComponent(this.value));});else
-for(var j in a)if(a[j]&&a[j].constructor==Array)jQuery.each(a[j],function(){s.push(encodeURIComponent(j)+"="+encodeURIComponent(this));});else
-s.push(encodeURIComponent(j)+"="+encodeURIComponent(jQuery.isFunction(a[j])?a[j]():a[j]));return s.join("&").replace(/%20/g,"+");}});jQuery.fn.extend({show:function(speed,callback){return speed?this.animate({height:"show",width:"show",opacity:"show"},speed,callback):this.filter(":hidden").each(function(){this.style.display=this.oldblock||"";if(jQuery.css(this,"display")=="none"){var elem=jQuery("<"+this.tagName+" />").appendTo("body");this.style.display=elem.css("display");if(this.style.display=="none")this.style.display="block";elem.remove();}}).end();},hide:function(speed,callback){return speed?this.animate({height:"hide",width:"hide",opacity:"hide"},speed,callback):this.filter(":visible").each(function(){this.oldblock=this.oldblock||jQuery.css(this,"display");this.style.display="none";}).end();},_toggle:jQuery.fn.toggle,toggle:function(fn,fn2){return jQuery.isFunction(fn)&&jQuery.isFunction(fn2)?this._toggle.apply(this,arguments):fn?this.animate({height:"toggle",width:"toggle",opacity:"toggle"},fn,fn2):this.each(function(){jQuery(this)[jQuery(this).is(":hidden")?"show":"hide"]();});},slideDown:function(speed,callback){return this.animate({height:"show"},speed,callback);},slideUp:function(speed,callback){return this.animate({height:"hide"},speed,callback);},slideToggle:function(speed,callback){return this.animate({height:"toggle"},speed,callback);},fadeIn:function(speed,callback){return this.animate({opacity:"show"},speed,callback);},fadeOut:function(speed,callback){return this.animate({opacity:"hide"},speed,callback);},fadeTo:function(speed,to,callback){return this.animate({opacity:to},speed,callback);},animate:function(prop,speed,easing,callback){var optall=jQuery.speed(speed,easing,callback);return this[optall.queue===false?"each":"queue"](function(){if(this.nodeType!=1)return false;var opt=jQuery.extend({},optall),p,hidden=jQuery(this).is(":hidden"),self=this;for(p in prop){if(prop[p]=="hide"&&hidden||prop[p]=="show"&&!hidden)return opt.complete.call(this);if(p=="height"||p=="width"){opt.display=jQuery.css(this,"display");opt.overflow=this.style.overflow;}}if(opt.overflow!=null)this.style.overflow="hidden";opt.curAnim=jQuery.extend({},prop);jQuery.each(prop,function(name,val){var e=new jQuery.fx(self,opt,name);if(/toggle|show|hide/.test(val))e[val=="toggle"?hidden?"show":"hide":val](prop);else{var parts=val.toString().match(/^([+-]=)?([\d+-.]+)(.*)$/),start=e.cur(true)||0;if(parts){var end=parseFloat(parts[2]),unit=parts[3]||"px";if(unit!="px"){self.style[name]=(end||1)+unit;start=((end||1)/e.cur(true))*start;self.style[name]=start+unit;}if(parts[1])end=((parts[1]=="-="?-1:1)*end)+start;e.custom(start,end,unit);}else
-e.custom(start,val,"");}});return true;});},queue:function(type,fn){if(jQuery.isFunction(type)||(type&&type.constructor==Array)){fn=type;type="fx";}if(!type||(typeof type=="string"&&!fn))return queue(this[0],type);return this.each(function(){if(fn.constructor==Array)queue(this,type,fn);else{queue(this,type).push(fn);if(queue(this,type).length==1)fn.call(this);}});},stop:function(clearQueue,gotoEnd){var timers=jQuery.timers;if(clearQueue)this.queue([]);this.each(function(){for(var i=timers.length-1;i>=0;i--)if(timers[i].elem==this){if(gotoEnd)timers[i](true);timers.splice(i,1);}});if(!gotoEnd)this.dequeue();return this;}});var queue=function(elem,type,array){if(elem){type=type||"fx";var q=jQuery.data(elem,type+"queue");if(!q||array)q=jQuery.data(elem,type+"queue",jQuery.makeArray(array));}return q;};jQuery.fn.dequeue=function(type){type=type||"fx";return this.each(function(){var q=queue(this,type);q.shift();if(q.length)q[0].call(this);});};jQuery.extend({speed:function(speed,easing,fn){var opt=speed&&speed.constructor==Object?speed:{complete:fn||!fn&&easing||jQuery.isFunction(speed)&&speed,duration:speed,easing:fn&&easing||easing&&easing.constructor!=Function&&easing};opt.duration=(opt.duration&&opt.duration.constructor==Number?opt.duration:jQuery.fx.speeds[opt.duration])||jQuery.fx.speeds.def;opt.old=opt.complete;opt.complete=function(){if(opt.queue!==false)jQuery(this).dequeue();if(jQuery.isFunction(opt.old))opt.old.call(this);};return opt;},easing:{linear:function(p,n,firstNum,diff){return firstNum+diff*p;},swing:function(p,n,firstNum,diff){return((-Math.cos(p*Math.PI)/2)+0.5)*diff+firstNum;}},timers:[],timerId:null,fx:function(elem,options,prop){this.options=options;this.elem=elem;this.prop=prop;if(!options.orig)options.orig={};}});jQuery.fx.prototype={update:function(){if(this.options.step)this.options.step.call(this.elem,this.now,this);(jQuery.fx.step[this.prop]||jQuery.fx.step._default)(this);if(this.prop=="height"||this.prop=="width")this.elem.style.display="block";},cur:function(force){if(this.elem[this.prop]!=null&&this.elem.style[this.prop]==null)return this.elem[this.prop];var r=parseFloat(jQuery.css(this.elem,this.prop,force));return r&&r>-10000?r:parseFloat(jQuery.curCSS(this.elem,this.prop))||0;},custom:function(from,to,unit){this.startTime=now();this.start=from;this.end=to;this.unit=unit||this.unit||"px";this.now=this.start;this.pos=this.state=0;this.update();var self=this;function t(gotoEnd){return self.step(gotoEnd);}t.elem=this.elem;jQuery.timers.push(t);if(jQuery.timerId==null){jQuery.timerId=setInterval(function(){var timers=jQuery.timers;for(var i=0;i<timers.length;i++)if(!timers[i]())timers.splice(i--,1);if(!timers.length){clearInterval(jQuery.timerId);jQuery.timerId=null;}},13);}},show:function(){this.options.orig[this.prop]=jQuery.attr(this.elem.style,this.prop);this.options.show=true;this.custom(0,this.cur());if(this.prop=="width"||this.prop=="height")this.elem.style[this.prop]="1px";jQuery(this.elem).show();},hide:function(){this.options.orig[this.prop]=jQuery.attr(this.elem.style,this.prop);this.options.hide=true;this.custom(this.cur(),0);},step:function(gotoEnd){var t=now();if(gotoEnd||t>this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;var done=true;for(var i in this.options.curAnim)if(this.options.curAnim[i]!==true)done=false;if(done){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;this.elem.style.display=this.options.display;if(jQuery.css(this.elem,"display")=="none")this.elem.style.display="block";}if(this.options.hide)this.elem.style.display="none";if(this.options.hide||this.options.show)for(var p in this.options.curAnim)jQuery.attr(this.elem.style,p,this.options.orig[p]);}if(done)this.options.complete.call(this.elem);return false;}else{var n=t-this.startTime;this.state=n/this.options.duration;this.pos=jQuery.easing[this.options.easing||(jQuery.easing.swing?"swing":"linear")](this.state,n,0,1,this.options.duration);this.now=this.start+((this.end-this.start)*this.pos);this.update();}return true;}};jQuery.extend(jQuery.fx,{speeds:{slow:600,fast:200,def:400},step:{scrollLeft:function(fx){fx.elem.scrollLeft=fx.now;},scrollTop:function(fx){fx.elem.scrollTop=fx.now;},opacity:function(fx){jQuery.attr(fx.elem.style,"opacity",fx.now);},_default:function(fx){fx.elem.style[fx.prop]=fx.now+fx.unit;}}});jQuery.fn.offset=function(){var left=0,top=0,elem=this[0],results;if(elem)with(jQuery.browser){var parent=elem.parentNode,offsetChild=elem,offsetParent=elem.offsetParent,doc=elem.ownerDocument,safari2=safari&&parseInt(version)<522&&!/adobeair/i.test(userAgent),css=jQuery.curCSS,fixed=css(elem,"position")=="fixed";if(elem.getBoundingClientRect){var box=elem.getBoundingClientRect();add(box.left+Math.max(doc.documentElement.scrollLeft,doc.body.scrollLeft),box.top+Math.max(doc.documentElement.scrollTop,doc.body.scrollTop));add(-doc.documentElement.clientLeft,-doc.documentElement.clientTop);}else{add(elem.offsetLeft,elem.offsetTop);while(offsetParent){add(offsetParent.offsetLeft,offsetParent.offsetTop);if(mozilla&&!/^t(able|d|h)$/i.test(offsetParent.tagName)||safari&&!safari2)border(offsetParent);if(!fixed&&css(offsetParent,"position")=="fixed")fixed=true;offsetChild=/^body$/i.test(offsetParent.tagName)?offsetChild:offsetParent;offsetParent=offsetParent.offsetParent;}while(parent&&parent.tagName&&!/^body|html$/i.test(parent.tagName)){if(!/^inline|table.*$/i.test(css(parent,"display")))add(-parent.scrollLeft,-parent.scrollTop);if(mozilla&&css(parent,"overflow")!="visible")border(parent);parent=parent.parentNode;}if((safari2&&(fixed||css(offsetChild,"position")=="absolute"))||(mozilla&&css(offsetChild,"position")!="absolute"))add(-doc.body.offsetLeft,-doc.body.offsetTop);if(fixed)add(Math.max(doc.documentElement.scrollLeft,doc.body.scrollLeft),Math.max(doc.documentElement.scrollTop,doc.body.scrollTop));}results={top:top,left:left};}function border(elem){add(jQuery.curCSS(elem,"borderLeftWidth",true),jQuery.curCSS(elem,"borderTopWidth",true));}function add(l,t){left+=parseInt(l,10)||0;top+=parseInt(t,10)||0;}return results;};jQuery.fn.extend({position:function(){var left=0,top=0,results;if(this[0]){var offsetParent=this.offsetParent(),offset=this.offset(),parentOffset=/^body|html$/i.test(offsetParent[0].tagName)?{top:0,left:0}:offsetParent.offset();offset.top-=num(this,'marginTop');offset.left-=num(this,'marginLeft');parentOffset.top+=num(offsetParent,'borderTopWidth');parentOffset.left+=num(offsetParent,'borderLeftWidth');results={top:offset.top-parentOffset.top,left:offset.left-parentOffset.left};}return results;},offsetParent:function(){var offsetParent=this[0].offsetParent;while(offsetParent&&(!/^body|html$/i.test(offsetParent.tagName)&&jQuery.css(offsetParent,'position')=='static'))offsetParent=offsetParent.offsetParent;return jQuery(offsetParent);}});jQuery.each(['Left','Top'],function(i,name){var method='scroll'+name;jQuery.fn[method]=function(val){if(!this[0])return;return val!=undefined?this.each(function(){this==window||this==document?window.scrollTo(!i?val:jQuery(window).scrollLeft(),i?val:jQuery(window).scrollTop()):this[method]=val;}):this[0]==window||this[0]==document?self[i?'pageYOffset':'pageXOffset']||jQuery.boxModel&&document.documentElement[method]||document.body[method]:this[0][method];};});jQuery.each(["Height","Width"],function(i,name){var tl=i?"Left":"Top",br=i?"Right":"Bottom";jQuery.fn["inner"+name]=function(){return this[name.toLowerCase()]()+num(this,"padding"+tl)+num(this,"padding"+br);};jQuery.fn["outer"+name]=function(margin){return this["inner"+name]()+num(this,"border"+tl+"Width")+num(this,"border"+br+"Width")+(margin?num(this,"margin"+tl)+num(this,"margin"+br):0);};});})();
\ No newline at end of file
+(function(A,w){function ma(){if(!c.isReady){try{s.documentElement.doScroll("left")}catch(a){setTimeout(ma,1);return}c.ready()}}function Qa(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function X(a,b,d,f,e,j){var i=a.length;if(typeof b==="object"){for(var o in b)X(a,o,b[o],f,e,d);return a}if(d!==w){f=!j&&f&&c.isFunction(d);for(o=0;o<i;o++)e(a[o],b,f?d.call(a[o],o,e(a[o],b)):d,j);return a}return i?
+e(a[0],b):w}function J(){return(new Date).getTime()}function Y(){return false}function Z(){return true}function na(a,b,d){d[0].type=a;return c.event.handle.apply(b,d)}function oa(a){var b,d=[],f=[],e=arguments,j,i,o,k,n,r;i=c.data(this,"events");if(!(a.liveFired===this||!i||!i.live||a.button&&a.type==="click")){a.liveFired=this;var u=i.live.slice(0);for(k=0;k<u.length;k++){i=u[k];i.origType.replace(O,"")===a.type?f.push(i.selector):u.splice(k--,1)}j=c(a.target).closest(f,a.currentTarget);n=0;for(r=
+j.length;n<r;n++)for(k=0;k<u.length;k++){i=u[k];if(j[n].selector===i.selector){o=j[n].elem;f=null;if(i.preType==="mouseenter"||i.preType==="mouseleave")f=c(a.relatedTarget).closest(i.selector)[0];if(!f||f!==o)d.push({elem:o,handleObj:i})}}n=0;for(r=d.length;n<r;n++){j=d[n];a.currentTarget=j.elem;a.data=j.handleObj.data;a.handleObj=j.handleObj;if(j.handleObj.origHandler.apply(j.elem,e)===false){b=false;break}}return b}}function pa(a,b){return"live."+(a&&a!=="*"?a+".":"")+b.replace(/\./g,"`").replace(/ /g,
+"&")}function qa(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function ra(a,b){var d=0;b.each(function(){if(this.nodeName===(a[d]&&a[d].nodeName)){var f=c.data(a[d++]),e=c.data(this,f);if(f=f&&f.events){delete e.handle;e.events={};for(var j in f)for(var i in f[j])c.event.add(this,j,f[j][i],f[j][i].data)}}})}function sa(a,b,d){var f,e,j;b=b&&b[0]?b[0].ownerDocument||b[0]:s;if(a.length===1&&typeof a[0]==="string"&&a[0].length<512&&b===s&&!ta.test(a[0])&&(c.support.checkClone||!ua.test(a[0]))){e=
+true;if(j=c.fragments[a[0]])if(j!==1)f=j}if(!f){f=b.createDocumentFragment();c.clean(a,b,f,d)}if(e)c.fragments[a[0]]=j?f:1;return{fragment:f,cacheable:e}}function K(a,b){var d={};c.each(va.concat.apply([],va.slice(0,b)),function(){d[this]=a});return d}function wa(a){return"scrollTo"in a&&a.document?a:a.nodeType===9?a.defaultView||a.parentWindow:false}var c=function(a,b){return new c.fn.init(a,b)},Ra=A.jQuery,Sa=A.$,s=A.document,T,Ta=/^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/,Ua=/^.[^:#\[\.,]*$/,Va=/\S/,
+Wa=/^(\s|\u00A0)+|(\s|\u00A0)+$/g,Xa=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,P=navigator.userAgent,xa=false,Q=[],L,$=Object.prototype.toString,aa=Object.prototype.hasOwnProperty,ba=Array.prototype.push,R=Array.prototype.slice,ya=Array.prototype.indexOf;c.fn=c.prototype={init:function(a,b){var d,f;if(!a)return this;if(a.nodeType){this.context=this[0]=a;this.length=1;return this}if(a==="body"&&!b){this.context=s;this[0]=s.body;this.selector="body";this.length=1;return this}if(typeof a==="string")if((d=Ta.exec(a))&&
+(d[1]||!b))if(d[1]){f=b?b.ownerDocument||b:s;if(a=Xa.exec(a))if(c.isPlainObject(b)){a=[s.createElement(a[1])];c.fn.attr.call(a,b,true)}else a=[f.createElement(a[1])];else{a=sa([d[1]],[f]);a=(a.cacheable?a.fragment.cloneNode(true):a.fragment).childNodes}return c.merge(this,a)}else{if(b=s.getElementById(d[2])){if(b.id!==d[2])return T.find(a);this.length=1;this[0]=b}this.context=s;this.selector=a;return this}else if(!b&&/^\w+$/.test(a)){this.selector=a;this.context=s;a=s.getElementsByTagName(a);return c.merge(this,
+a)}else return!b||b.jquery?(b||T).find(a):c(b).find(a);else if(c.isFunction(a))return T.ready(a);if(a.selector!==w){this.selector=a.selector;this.context=a.context}return c.makeArray(a,this)},selector:"",jquery:"1.4.2",length:0,size:function(){return this.length},toArray:function(){return R.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this.slice(a)[0]:this[a]},pushStack:function(a,b,d){var f=c();c.isArray(a)?ba.apply(f,a):c.merge(f,a);f.prevObject=this;f.context=this.context;if(b===
+"find")f.selector=this.selector+(this.selector?" ":"")+d;else if(b)f.selector=this.selector+"."+b+"("+d+")";return f},each:function(a,b){return c.each(this,a,b)},ready:function(a){c.bindReady();if(c.isReady)a.call(s,c);else Q&&Q.push(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(R.apply(this,arguments),"slice",R.call(arguments).join(","))},map:function(a){return this.pushStack(c.map(this,
+function(b,d){return a.call(b,d,b)}))},end:function(){return this.prevObject||c(null)},push:ba,sort:[].sort,splice:[].splice};c.fn.init.prototype=c.fn;c.extend=c.fn.extend=function(){var a=arguments[0]||{},b=1,d=arguments.length,f=false,e,j,i,o;if(typeof a==="boolean"){f=a;a=arguments[1]||{};b=2}if(typeof a!=="object"&&!c.isFunction(a))a={};if(d===b){a=this;--b}for(;b<d;b++)if((e=arguments[b])!=null)for(j in e){i=a[j];o=e[j];if(a!==o)if(f&&o&&(c.isPlainObject(o)||c.isArray(o))){i=i&&(c.isPlainObject(i)||
+c.isArray(i))?i:c.isArray(o)?[]:{};a[j]=c.extend(f,i,o)}else if(o!==w)a[j]=o}return a};c.extend({noConflict:function(a){A.$=Sa;if(a)A.jQuery=Ra;return c},isReady:false,ready:function(){if(!c.isReady){if(!s.body)return setTimeout(c.ready,13);c.isReady=true;if(Q){for(var a,b=0;a=Q[b++];)a.call(s,c);Q=null}c.fn.triggerHandler&&c(s).triggerHandler("ready")}},bindReady:function(){if(!xa){xa=true;if(s.readyState==="complete")return c.ready();if(s.addEventListener){s.addEventListener("DOMContentLoaded",
+L,false);A.addEventListener("load",c.ready,false)}else if(s.attachEvent){s.attachEvent("onreadystatechange",L);A.attachEvent("onload",c.ready);var a=false;try{a=A.frameElement==null}catch(b){}s.documentElement.doScroll&&a&&ma()}}},isFunction:function(a){return $.call(a)==="[object Function]"},isArray:function(a){return $.call(a)==="[object Array]"},isPlainObject:function(a){if(!a||$.call(a)!=="[object Object]"||a.nodeType||a.setInterval)return false;if(a.constructor&&!aa.call(a,"constructor")&&!aa.call(a.constructor.prototype,
+"isPrototypeOf"))return false;var b;for(b in a);return b===w||aa.call(a,b)},isEmptyObject:function(a){for(var b in a)return false;return true},error:function(a){throw a;},parseJSON:function(a){if(typeof a!=="string"||!a)return null;a=c.trim(a);if(/^[\],:{}\s]*$/.test(a.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,"")))return A.JSON&&A.JSON.parse?A.JSON.parse(a):(new Function("return "+
+a))();else c.error("Invalid JSON: "+a)},noop:function(){},globalEval:function(a){if(a&&Va.test(a)){var b=s.getElementsByTagName("head")[0]||s.documentElement,d=s.createElement("script");d.type="text/javascript";if(c.support.scriptEval)d.appendChild(s.createTextNode(a));else d.text=a;b.insertBefore(d,b.firstChild);b.removeChild(d)}},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,b,d){var f,e=0,j=a.length,i=j===w||c.isFunction(a);if(d)if(i)for(f in a){if(b.apply(a[f],
+d)===false)break}else for(;e<j;){if(b.apply(a[e++],d)===false)break}else if(i)for(f in a){if(b.call(a[f],f,a[f])===false)break}else for(d=a[0];e<j&&b.call(d,e,d)!==false;d=a[++e]);return a},trim:function(a){return(a||"").replace(Wa,"")},makeArray:function(a,b){b=b||[];if(a!=null)a.length==null||typeof a==="string"||c.isFunction(a)||typeof a!=="function"&&a.setInterval?ba.call(b,a):c.merge(b,a);return b},inArray:function(a,b){if(b.indexOf)return b.indexOf(a);for(var d=0,f=b.length;d<f;d++)if(b[d]===
+a)return d;return-1},merge:function(a,b){var d=a.length,f=0;if(typeof b.length==="number")for(var e=b.length;f<e;f++)a[d++]=b[f];else for(;b[f]!==w;)a[d++]=b[f++];a.length=d;return a},grep:function(a,b,d){for(var f=[],e=0,j=a.length;e<j;e++)!d!==!b(a[e],e)&&f.push(a[e]);return f},map:function(a,b,d){for(var f=[],e,j=0,i=a.length;j<i;j++){e=b(a[j],j,d);if(e!=null)f[f.length]=e}return f.concat.apply([],f)},guid:1,proxy:function(a,b,d){if(arguments.length===2)if(typeof b==="string"){d=a;a=d[b];b=w}else if(b&&
+!c.isFunction(b)){d=b;b=w}if(!b&&a)b=function(){return a.apply(d||this,arguments)};if(a)b.guid=a.guid=a.guid||b.guid||c.guid++;return b},uaMatch:function(a){a=a.toLowerCase();a=/(webkit)[ \/]([\w.]+)/.exec(a)||/(opera)(?:.*version)?[ \/]([\w.]+)/.exec(a)||/(msie) ([\w.]+)/.exec(a)||!/compatible/.test(a)&&/(mozilla)(?:.*? rv:([\w.]+))?/.exec(a)||[];return{browser:a[1]||"",version:a[2]||"0"}},browser:{}});P=c.uaMatch(P);if(P.browser){c.browser[P.browser]=true;c.browser.version=P.version}if(c.browser.webkit)c.browser.safari=
+true;if(ya)c.inArray=function(a,b){return ya.call(b,a)};T=c(s);if(s.addEventListener)L=function(){s.removeEventListener("DOMContentLoaded",L,false);c.ready()};else if(s.attachEvent)L=function(){if(s.readyState==="complete"){s.detachEvent("onreadystatechange",L);c.ready()}};(function(){c.support={};var a=s.documentElement,b=s.createElement("script"),d=s.createElement("div"),f="script"+J();d.style.display="none";d.innerHTML="   <link/><table></table><a href='/a' style='color:red;float:left;opacity:.55;'>a</a><input type='checkbox'/>";
+var e=d.getElementsByTagName("*"),j=d.getElementsByTagName("a")[0];if(!(!e||!e.length||!j)){c.support={leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(j.getAttribute("style")),hrefNormalized:j.getAttribute("href")==="/a",opacity:/^0.55$/.test(j.style.opacity),cssFloat:!!j.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:s.createElement("select").appendChild(s.createElement("option")).selected,
+parentNode:d.removeChild(d.appendChild(s.createElement("div"))).parentNode===null,deleteExpando:true,checkClone:false,scriptEval:false,noCloneEvent:true,boxModel:null};b.type="text/javascript";try{b.appendChild(s.createTextNode("window."+f+"=1;"))}catch(i){}a.insertBefore(b,a.firstChild);if(A[f]){c.support.scriptEval=true;delete A[f]}try{delete b.test}catch(o){c.support.deleteExpando=false}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function k(){c.support.noCloneEvent=
+false;d.detachEvent("onclick",k)});d.cloneNode(true).fireEvent("onclick")}d=s.createElement("div");d.innerHTML="<input type='radio' name='radiotest' checked='checked'/>";a=s.createDocumentFragment();a.appendChild(d.firstChild);c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var k=s.createElement("div");k.style.width=k.style.paddingLeft="1px";s.body.appendChild(k);c.boxModel=c.support.boxModel=k.offsetWidth===2;s.body.removeChild(k).style.display="none"});a=function(k){var n=
+s.createElement("div");k="on"+k;var r=k in n;if(!r){n.setAttribute(k,"return;");r=typeof n[k]==="function"}return r};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=e=j=null}})();c.props={"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",frameborder:"frameBorder"};var G="jQuery"+J(),Ya=0,za={};c.extend({cache:{},expando:G,noData:{embed:true,object:true,
+applet:true},data:function(a,b,d){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var f=a[G],e=c.cache;if(!f&&typeof b==="string"&&d===w)return null;f||(f=++Ya);if(typeof b==="object"){a[G]=f;e[f]=c.extend(true,{},b)}else if(!e[f]){a[G]=f;e[f]={}}a=e[f];if(d!==w)a[b]=d;return typeof b==="string"?a[b]:a}},removeData:function(a,b){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var d=a[G],f=c.cache,e=f[d];if(b){if(e){delete e[b];c.isEmptyObject(e)&&c.removeData(a)}}else{if(c.support.deleteExpando)delete a[c.expando];
+else a.removeAttribute&&a.removeAttribute(c.expando);delete f[d]}}}});c.fn.extend({data:function(a,b){if(typeof a==="undefined"&&this.length)return c.data(this[0]);else if(typeof a==="object")return this.each(function(){c.data(this,a)});var d=a.split(".");d[1]=d[1]?"."+d[1]:"";if(b===w){var f=this.triggerHandler("getData"+d[1]+"!",[d[0]]);if(f===w&&this.length)f=c.data(this[0],a);return f===w&&d[1]?this.data(d[0]):f}else return this.trigger("setData"+d[1]+"!",[d[0],b]).each(function(){c.data(this,
+a,b)})},removeData:function(a){return this.each(function(){c.removeData(this,a)})}});c.extend({queue:function(a,b,d){if(a){b=(b||"fx")+"queue";var f=c.data(a,b);if(!d)return f||[];if(!f||c.isArray(d))f=c.data(a,b,c.makeArray(d));else f.push(d);return f}},dequeue:function(a,b){b=b||"fx";var d=c.queue(a,b),f=d.shift();if(f==="inprogress")f=d.shift();if(f){b==="fx"&&d.unshift("inprogress");f.call(a,function(){c.dequeue(a,b)})}}});c.fn.extend({queue:function(a,b){if(typeof a!=="string"){b=a;a="fx"}if(b===
+w)return c.queue(this[0],a);return this.each(function(){var d=c.queue(this,a,b);a==="fx"&&d[0]!=="inprogress"&&c.dequeue(this,a)})},dequeue:function(a){return this.each(function(){c.dequeue(this,a)})},delay:function(a,b){a=c.fx?c.fx.speeds[a]||a:a;b=b||"fx";return this.queue(b,function(){var d=this;setTimeout(function(){c.dequeue(d,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])}});var Aa=/[\n\t]/g,ca=/\s+/,Za=/\r/g,$a=/href|src|style/,ab=/(button|input)/i,bb=/(button|input|object|select|textarea)/i,
+cb=/^(a|area)$/i,Ba=/radio|checkbox/;c.fn.extend({attr:function(a,b){return X(this,a,b,true,c.attr)},removeAttr:function(a){return this.each(function(){c.attr(this,a,"");this.nodeType===1&&this.removeAttribute(a)})},addClass:function(a){if(c.isFunction(a))return this.each(function(n){var r=c(this);r.addClass(a.call(this,n,r.attr("class")))});if(a&&typeof a==="string")for(var b=(a||"").split(ca),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1)if(e.className){for(var j=" "+e.className+" ",
+i=e.className,o=0,k=b.length;o<k;o++)if(j.indexOf(" "+b[o]+" ")<0)i+=" "+b[o];e.className=c.trim(i)}else e.className=a}return this},removeClass:function(a){if(c.isFunction(a))return this.each(function(k){var n=c(this);n.removeClass(a.call(this,k,n.attr("class")))});if(a&&typeof a==="string"||a===w)for(var b=(a||"").split(ca),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1&&e.className)if(a){for(var j=(" "+e.className+" ").replace(Aa," "),i=0,o=b.length;i<o;i++)j=j.replace(" "+b[i]+" ",
+" ");e.className=c.trim(j)}else e.className=""}return this},toggleClass:function(a,b){var d=typeof a,f=typeof b==="boolean";if(c.isFunction(a))return this.each(function(e){var j=c(this);j.toggleClass(a.call(this,e,j.attr("class"),b),b)});return this.each(function(){if(d==="string")for(var e,j=0,i=c(this),o=b,k=a.split(ca);e=k[j++];){o=f?o:!i.hasClass(e);i[o?"addClass":"removeClass"](e)}else if(d==="undefined"||d==="boolean"){this.className&&c.data(this,"__className__",this.className);this.className=
+this.className||a===false?"":c.data(this,"__className__")||""}})},hasClass:function(a){a=" "+a+" ";for(var b=0,d=this.length;b<d;b++)if((" "+this[b].className+" ").replace(Aa," ").indexOf(a)>-1)return true;return false},val:function(a){if(a===w){var b=this[0];if(b){if(c.nodeName(b,"option"))return(b.attributes.value||{}).specified?b.value:b.text;if(c.nodeName(b,"select")){var d=b.selectedIndex,f=[],e=b.options;b=b.type==="select-one";if(d<0)return null;var j=b?d:0;for(d=b?d+1:e.length;j<d;j++){var i=
+e[j];if(i.selected){a=c(i).val();if(b)return a;f.push(a)}}return f}if(Ba.test(b.type)&&!c.support.checkOn)return b.getAttribute("value")===null?"on":b.value;return(b.value||"").replace(Za,"")}return w}var o=c.isFunction(a);return this.each(function(k){var n=c(this),r=a;if(this.nodeType===1){if(o)r=a.call(this,k,n.val());if(typeof r==="number")r+="";if(c.isArray(r)&&Ba.test(this.type))this.checked=c.inArray(n.val(),r)>=0;else if(c.nodeName(this,"select")){var u=c.makeArray(r);c("option",this).each(function(){this.selected=
+c.inArray(c(this).val(),u)>=0});if(!u.length)this.selectedIndex=-1}else this.value=r}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},attr:function(a,b,d,f){if(!a||a.nodeType===3||a.nodeType===8)return w;if(f&&b in c.attrFn)return c(a)[b](d);f=a.nodeType!==1||!c.isXMLDoc(a);var e=d!==w;b=f&&c.props[b]||b;if(a.nodeType===1){var j=$a.test(b);if(b in a&&f&&!j){if(e){b==="type"&&ab.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed");
+a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&&b.specified?b.value:bb.test(a.nodeName)||cb.test(a.nodeName)&&a.href?0:w;return a[b]}if(!c.support.style&&f&&b==="style"){if(e)a.style.cssText=""+d;return a.style.cssText}e&&a.setAttribute(b,""+d);a=!c.support.hrefNormalized&&f&&j?a.getAttribute(b,2):a.getAttribute(b);return a===null?w:a}return c.style(a,b,d)}});var O=/\.(.*)$/,db=function(a){return a.replace(/[^\w\s\.\|`]/g,
+function(b){return"\\"+b})};c.event={add:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){if(a.setInterval&&a!==A&&!a.frameElement)a=A;var e,j;if(d.handler){e=d;d=e.handler}if(!d.guid)d.guid=c.guid++;if(j=c.data(a)){var i=j.events=j.events||{},o=j.handle;if(!o)j.handle=o=function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(o.elem,arguments):w};o.elem=a;b=b.split(" ");for(var k,n=0,r;k=b[n++];){j=e?c.extend({},e):{handler:d,data:f};if(k.indexOf(".")>-1){r=k.split(".");
+k=r.shift();j.namespace=r.slice(0).sort().join(".")}else{r=[];j.namespace=""}j.type=k;j.guid=d.guid;var u=i[k],z=c.event.special[k]||{};if(!u){u=i[k]=[];if(!z.setup||z.setup.call(a,f,r,o)===false)if(a.addEventListener)a.addEventListener(k,o,false);else a.attachEvent&&a.attachEvent("on"+k,o)}if(z.add){z.add.call(a,j);if(!j.handler.guid)j.handler.guid=d.guid}u.push(j);c.event.global[k]=true}a=null}}},global:{},remove:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){var e,j=0,i,o,k,n,r,u,z=c.data(a),
+C=z&&z.events;if(z&&C){if(b&&b.type){d=b.handler;b=b.type}if(!b||typeof b==="string"&&b.charAt(0)==="."){b=b||"";for(e in C)c.event.remove(a,e+b)}else{for(b=b.split(" ");e=b[j++];){n=e;i=e.indexOf(".")<0;o=[];if(!i){o=e.split(".");e=o.shift();k=new RegExp("(^|\\.)"+c.map(o.slice(0).sort(),db).join("\\.(?:.*\\.)?")+"(\\.|$)")}if(r=C[e])if(d){n=c.event.special[e]||{};for(B=f||0;B<r.length;B++){u=r[B];if(d.guid===u.guid){if(i||k.test(u.namespace)){f==null&&r.splice(B--,1);n.remove&&n.remove.call(a,u)}if(f!=
+null)break}}if(r.length===0||f!=null&&r.length===1){if(!n.teardown||n.teardown.call(a,o)===false)Ca(a,e,z.handle);delete C[e]}}else for(var B=0;B<r.length;B++){u=r[B];if(i||k.test(u.namespace)){c.event.remove(a,n,u.handler,B);r.splice(B--,1)}}}if(c.isEmptyObject(C)){if(b=z.handle)b.elem=null;delete z.events;delete z.handle;c.isEmptyObject(z)&&c.removeData(a)}}}}},trigger:function(a,b,d,f){var e=a.type||a;if(!f){a=typeof a==="object"?a[G]?a:c.extend(c.Event(e),a):c.Event(e);if(e.indexOf("!")>=0){a.type=
+e=e.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();c.event.global[e]&&c.each(c.cache,function(){this.events&&this.events[e]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType===8)return w;a.result=w;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(f=c.data(d,"handle"))&&f.apply(d,b);f=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+e]&&d["on"+e].apply(d,b)===false)a.result=false}catch(j){}if(!a.isPropagationStopped()&&
+f)c.event.trigger(a,b,f,true);else if(!a.isDefaultPrevented()){f=a.target;var i,o=c.nodeName(f,"a")&&e==="click",k=c.event.special[e]||{};if((!k._default||k._default.call(d,a)===false)&&!o&&!(f&&f.nodeName&&c.noData[f.nodeName.toLowerCase()])){try{if(f[e]){if(i=f["on"+e])f["on"+e]=null;c.event.triggered=true;f[e]()}}catch(n){}if(i)f["on"+e]=i;c.event.triggered=false}}},handle:function(a){var b,d,f,e;a=arguments[0]=c.event.fix(a||A.event);a.currentTarget=this;b=a.type.indexOf(".")<0&&!a.exclusive;
+if(!b){d=a.type.split(".");a.type=d.shift();f=new RegExp("(^|\\.)"+d.slice(0).sort().join("\\.(?:.*\\.)?")+"(\\.|$)")}e=c.data(this,"events");d=e[a.type];if(e&&d){d=d.slice(0);e=0;for(var j=d.length;e<j;e++){var i=d[e];if(b||f.test(i.namespace)){a.handler=i.handler;a.data=i.data;a.handleObj=i;i=i.handler.apply(this,arguments);if(i!==w){a.result=i;if(i===false){a.preventDefault();a.stopPropagation()}}if(a.isImmediatePropagationStopped())break}}}return a.result},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),
+fix:function(a){if(a[G])return a;var b=a;a=c.Event(b);for(var d=this.props.length,f;d;){f=this.props[--d];a[f]=b[f]}if(!a.target)a.target=a.srcElement||s;if(a.target.nodeType===3)a.target=a.target.parentNode;if(!a.relatedTarget&&a.fromElement)a.relatedTarget=a.fromElement===a.target?a.toElement:a.fromElement;if(a.pageX==null&&a.clientX!=null){b=s.documentElement;d=s.body;a.pageX=a.clientX+(b&&b.scrollLeft||d&&d.scrollLeft||0)-(b&&b.clientLeft||d&&d.clientLeft||0);a.pageY=a.clientY+(b&&b.scrollTop||
+d&&d.scrollTop||0)-(b&&b.clientTop||d&&d.clientTop||0)}if(!a.which&&(a.charCode||a.charCode===0?a.charCode:a.keyCode))a.which=a.charCode||a.keyCode;if(!a.metaKey&&a.ctrlKey)a.metaKey=a.ctrlKey;if(!a.which&&a.button!==w)a.which=a.button&1?1:a.button&2?3:a.button&4?2:0;return a},guid:1E8,proxy:c.proxy,special:{ready:{setup:c.bindReady,teardown:c.noop},live:{add:function(a){c.event.add(this,a.origType,c.extend({},a,{handler:oa}))},remove:function(a){var b=true,d=a.origType.replace(O,"");c.each(c.data(this,
+"events").live||[],function(){if(d===this.origType.replace(O,""))return b=false});b&&c.event.remove(this,a.origType,oa)}},beforeunload:{setup:function(a,b,d){if(this.setInterval)this.onbeforeunload=d;return false},teardown:function(a,b){if(this.onbeforeunload===b)this.onbeforeunload=null}}}};var Ca=s.removeEventListener?function(a,b,d){a.removeEventListener(b,d,false)}:function(a,b,d){a.detachEvent("on"+b,d)};c.Event=function(a){if(!this.preventDefault)return new c.Event(a);if(a&&a.type){this.originalEvent=
+a;this.type=a.type}else this.type=a;this.timeStamp=J();this[G]=true};c.Event.prototype={preventDefault:function(){this.isDefaultPrevented=Z;var a=this.originalEvent;if(a){a.preventDefault&&a.preventDefault();a.returnValue=false}},stopPropagation:function(){this.isPropagationStopped=Z;var a=this.originalEvent;if(a){a.stopPropagation&&a.stopPropagation();a.cancelBubble=true}},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=Z;this.stopPropagation()},isDefaultPrevented:Y,isPropagationStopped:Y,
+isImmediatePropagationStopped:Y};var Da=function(a){var b=a.relatedTarget;try{for(;b&&b!==this;)b=b.parentNode;if(b!==this){a.type=a.data;c.event.handle.apply(this,arguments)}}catch(d){}},Ea=function(a){a.type=a.data;c.event.handle.apply(this,arguments)};c.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){c.event.special[a]={setup:function(d){c.event.add(this,b,d&&d.selector?Ea:Da,a)},teardown:function(d){c.event.remove(this,b,d&&d.selector?Ea:Da)}}});if(!c.support.submitBubbles)c.event.special.submit=
+{setup:function(){if(this.nodeName.toLowerCase()!=="form"){c.event.add(this,"click.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="submit"||d==="image")&&c(b).closest("form").length)return na("submit",this,arguments)});c.event.add(this,"keypress.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="text"||d==="password")&&c(b).closest("form").length&&a.keyCode===13)return na("submit",this,arguments)})}else return false},teardown:function(){c.event.remove(this,".specialSubmit")}};
+if(!c.support.changeBubbles){var da=/textarea|input|select/i,ea,Fa=function(a){var b=a.type,d=a.value;if(b==="radio"||b==="checkbox")d=a.checked;else if(b==="select-multiple")d=a.selectedIndex>-1?c.map(a.options,function(f){return f.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d},fa=function(a,b){var d=a.target,f,e;if(!(!da.test(d.nodeName)||d.readOnly)){f=c.data(d,"_change_data");e=Fa(d);if(a.type!=="focusout"||d.type!=="radio")c.data(d,"_change_data",
+e);if(!(f===w||e===f))if(f!=null||e){a.type="change";return c.event.trigger(a,b,d)}}};c.event.special.change={filters:{focusout:fa,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return fa.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return fa.call(this,a)},beforeactivate:function(a){a=a.target;c.data(a,
+"_change_data",Fa(a))}},setup:function(){if(this.type==="file")return false;for(var a in ea)c.event.add(this,a+".specialChange",ea[a]);return da.test(this.nodeName)},teardown:function(){c.event.remove(this,".specialChange");return da.test(this.nodeName)}};ea=c.event.special.change.filters}s.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(f){f=c.event.fix(f);f.type=b;return c.event.handle.call(this,f)}c.event.special[b]={setup:function(){this.addEventListener(a,
+d,true)},teardown:function(){this.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,f,e){if(typeof d==="object"){for(var j in d)this[b](j,f,d[j],e);return this}if(c.isFunction(f)){e=f;f=w}var i=b==="one"?c.proxy(e,function(k){c(this).unbind(k,i);return e.apply(this,arguments)}):e;if(d==="unload"&&b!=="one")this.one(d,f,e);else{j=0;for(var o=this.length;j<o;j++)c.event.add(this[j],d,i,f)}return this}});c.fn.extend({unbind:function(a,b){if(typeof a==="object"&&
+!a.preventDefault)for(var d in a)this.unbind(d,a[d]);else{d=0;for(var f=this.length;d<f;d++)c.event.remove(this[d],a,b)}return this},delegate:function(a,b,d,f){return this.live(b,d,f,a)},undelegate:function(a,b,d){return arguments.length===0?this.unbind("live"):this.die(b,null,d,a)},trigger:function(a,b){return this.each(function(){c.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0]){a=c.Event(a);a.preventDefault();a.stopPropagation();c.event.trigger(a,b,this[0]);return a.result}},
+toggle:function(a){for(var b=arguments,d=1;d<b.length;)c.proxy(a,b[d++]);return this.click(c.proxy(a,function(f){var e=(c.data(this,"lastToggle"+a.guid)||0)%d;c.data(this,"lastToggle"+a.guid,e+1);f.preventDefault();return b[e].apply(this,arguments)||false}))},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}});var Ga={focus:"focusin",blur:"focusout",mouseenter:"mouseover",mouseleave:"mouseout"};c.each(["live","die"],function(a,b){c.fn[b]=function(d,f,e,j){var i,o=0,k,n,r=j||this.selector,
+u=j?this:c(this.context);if(c.isFunction(f)){e=f;f=w}for(d=(d||"").split(" ");(i=d[o++])!=null;){j=O.exec(i);k="";if(j){k=j[0];i=i.replace(O,"")}if(i==="hover")d.push("mouseenter"+k,"mouseleave"+k);else{n=i;if(i==="focus"||i==="blur"){d.push(Ga[i]+k);i+=k}else i=(Ga[i]||i)+k;b==="live"?u.each(function(){c.event.add(this,pa(i,r),{data:f,selector:r,handler:e,origType:i,origHandler:e,preType:n})}):u.unbind(pa(i,r),e)}}return this}});c.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error".split(" "),
+function(a,b){c.fn[b]=function(d){return d?this.bind(b,d):this.trigger(b)};if(c.attrFn)c.attrFn[b]=true});A.attachEvent&&!A.addEventListener&&A.attachEvent("onunload",function(){for(var a in c.cache)if(c.cache[a].handle)try{c.event.remove(c.cache[a].handle.elem)}catch(b){}});(function(){function a(g){for(var h="",l,m=0;g[m];m++){l=g[m];if(l.nodeType===3||l.nodeType===4)h+=l.nodeValue;else if(l.nodeType!==8)h+=a(l.childNodes)}return h}function b(g,h,l,m,q,p){q=0;for(var v=m.length;q<v;q++){var t=m[q];
+if(t){t=t[g];for(var y=false;t;){if(t.sizcache===l){y=m[t.sizset];break}if(t.nodeType===1&&!p){t.sizcache=l;t.sizset=q}if(t.nodeName.toLowerCase()===h){y=t;break}t=t[g]}m[q]=y}}}function d(g,h,l,m,q,p){q=0;for(var v=m.length;q<v;q++){var t=m[q];if(t){t=t[g];for(var y=false;t;){if(t.sizcache===l){y=m[t.sizset];break}if(t.nodeType===1){if(!p){t.sizcache=l;t.sizset=q}if(typeof h!=="string"){if(t===h){y=true;break}}else if(k.filter(h,[t]).length>0){y=t;break}}t=t[g]}m[q]=y}}}var f=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
+e=0,j=Object.prototype.toString,i=false,o=true;[0,0].sort(function(){o=false;return 0});var k=function(g,h,l,m){l=l||[];var q=h=h||s;if(h.nodeType!==1&&h.nodeType!==9)return[];if(!g||typeof g!=="string")return l;for(var p=[],v,t,y,S,H=true,M=x(h),I=g;(f.exec(""),v=f.exec(I))!==null;){I=v[3];p.push(v[1]);if(v[2]){S=v[3];break}}if(p.length>1&&r.exec(g))if(p.length===2&&n.relative[p[0]])t=ga(p[0]+p[1],h);else for(t=n.relative[p[0]]?[h]:k(p.shift(),h);p.length;){g=p.shift();if(n.relative[g])g+=p.shift();
+t=ga(g,t)}else{if(!m&&p.length>1&&h.nodeType===9&&!M&&n.match.ID.test(p[0])&&!n.match.ID.test(p[p.length-1])){v=k.find(p.shift(),h,M);h=v.expr?k.filter(v.expr,v.set)[0]:v.set[0]}if(h){v=m?{expr:p.pop(),set:z(m)}:k.find(p.pop(),p.length===1&&(p[0]==="~"||p[0]==="+")&&h.parentNode?h.parentNode:h,M);t=v.expr?k.filter(v.expr,v.set):v.set;if(p.length>0)y=z(t);else H=false;for(;p.length;){var D=p.pop();v=D;if(n.relative[D])v=p.pop();else D="";if(v==null)v=h;n.relative[D](y,v,M)}}else y=[]}y||(y=t);y||k.error(D||
+g);if(j.call(y)==="[object Array]")if(H)if(h&&h.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&E(h,y[g])))l.push(t[g])}else for(g=0;y[g]!=null;g++)y[g]&&y[g].nodeType===1&&l.push(t[g]);else l.push.apply(l,y);else z(y,l);if(S){k(S,q,l,m);k.uniqueSort(l)}return l};k.uniqueSort=function(g){if(B){i=o;g.sort(B);if(i)for(var h=1;h<g.length;h++)g[h]===g[h-1]&&g.splice(h--,1)}return g};k.matches=function(g,h){return k(g,null,null,h)};k.find=function(g,h,l){var m,q;if(!g)return[];
+for(var p=0,v=n.order.length;p<v;p++){var t=n.order[p];if(q=n.leftMatch[t].exec(g)){var y=q[1];q.splice(1,1);if(y.substr(y.length-1)!=="\\"){q[1]=(q[1]||"").replace(/\\/g,"");m=n.find[t](q,h,l);if(m!=null){g=g.replace(n.match[t],"");break}}}}m||(m=h.getElementsByTagName("*"));return{set:m,expr:g}};k.filter=function(g,h,l,m){for(var q=g,p=[],v=h,t,y,S=h&&h[0]&&x(h[0]);g&&h.length;){for(var H in n.filter)if((t=n.leftMatch[H].exec(g))!=null&&t[2]){var M=n.filter[H],I,D;D=t[1];y=false;t.splice(1,1);if(D.substr(D.length-
+1)!=="\\"){if(v===p)p=[];if(n.preFilter[H])if(t=n.preFilter[H](t,v,l,p,m,S)){if(t===true)continue}else y=I=true;if(t)for(var U=0;(D=v[U])!=null;U++)if(D){I=M(D,t,U,v);var Ha=m^!!I;if(l&&I!=null)if(Ha)y=true;else v[U]=false;else if(Ha){p.push(D);y=true}}if(I!==w){l||(v=p);g=g.replace(n.match[H],"");if(!y)return[];break}}}if(g===q)if(y==null)k.error(g);else break;q=g}return v};k.error=function(g){throw"Syntax error, unrecognized expression: "+g;};var n=k.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF-]|\\.)+)/,
+CLASS:/\.((?:[\w\u00c0-\uFFFF-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(g){return g.getAttribute("href")}},
+relative:{"+":function(g,h){var l=typeof h==="string",m=l&&!/\W/.test(h);l=l&&!m;if(m)h=h.toLowerCase();m=0;for(var q=g.length,p;m<q;m++)if(p=g[m]){for(;(p=p.previousSibling)&&p.nodeType!==1;);g[m]=l||p&&p.nodeName.toLowerCase()===h?p||false:p===h}l&&k.filter(h,g,true)},">":function(g,h){var l=typeof h==="string";if(l&&!/\W/.test(h)){h=h.toLowerCase();for(var m=0,q=g.length;m<q;m++){var p=g[m];if(p){l=p.parentNode;g[m]=l.nodeName.toLowerCase()===h?l:false}}}else{m=0;for(q=g.length;m<q;m++)if(p=g[m])g[m]=
+l?p.parentNode:p.parentNode===h;l&&k.filter(h,g,true)}},"":function(g,h,l){var m=e++,q=d;if(typeof h==="string"&&!/\W/.test(h)){var p=h=h.toLowerCase();q=b}q("parentNode",h,m,g,p,l)},"~":function(g,h,l){var m=e++,q=d;if(typeof h==="string"&&!/\W/.test(h)){var p=h=h.toLowerCase();q=b}q("previousSibling",h,m,g,p,l)}},find:{ID:function(g,h,l){if(typeof h.getElementById!=="undefined"&&!l)return(g=h.getElementById(g[1]))?[g]:[]},NAME:function(g,h){if(typeof h.getElementsByName!=="undefined"){var l=[];
+h=h.getElementsByName(g[1]);for(var m=0,q=h.length;m<q;m++)h[m].getAttribute("name")===g[1]&&l.push(h[m]);return l.length===0?null:l}},TAG:function(g,h){return h.getElementsByTagName(g[1])}},preFilter:{CLASS:function(g,h,l,m,q,p){g=" "+g[1].replace(/\\/g,"")+" ";if(p)return g;p=0;for(var v;(v=h[p])!=null;p++)if(v)if(q^(v.className&&(" "+v.className+" ").replace(/[\t\n]/g," ").indexOf(g)>=0))l||m.push(v);else if(l)h[p]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()},
+CHILD:function(g){if(g[1]==="nth"){var h=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=h[1]+(h[2]||1)-0;g[3]=h[3]-0}g[0]=e++;return g},ATTR:function(g,h,l,m,q,p){h=g[1].replace(/\\/g,"");if(!p&&n.attrMap[h])g[1]=n.attrMap[h];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,h,l,m,q){if(g[1]==="not")if((f.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=k(g[3],null,null,h);else{g=k.filter(g[3],h,l,true^q);l||m.push.apply(m,
+g);return false}else if(n.match.POS.test(g[0])||n.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled===true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,h,l){return!!k(l[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)},
+text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"===g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}},
+setFilters:{first:function(g,h){return h===0},last:function(g,h,l,m){return h===m.length-1},even:function(g,h){return h%2===0},odd:function(g,h){return h%2===1},lt:function(g,h,l){return h<l[3]-0},gt:function(g,h,l){return h>l[3]-0},nth:function(g,h,l){return l[3]-0===h},eq:function(g,h,l){return l[3]-0===h}},filter:{PSEUDO:function(g,h,l,m){var q=h[1],p=n.filters[q];if(p)return p(g,l,h,m);else if(q==="contains")return(g.textContent||g.innerText||a([g])||"").indexOf(h[3])>=0;else if(q==="not"){h=
+h[3];l=0;for(m=h.length;l<m;l++)if(h[l]===g)return false;return true}else k.error("Syntax error, unrecognized expression: "+q)},CHILD:function(g,h){var l=h[1],m=g;switch(l){case "only":case "first":for(;m=m.previousSibling;)if(m.nodeType===1)return false;if(l==="first")return true;m=g;case "last":for(;m=m.nextSibling;)if(m.nodeType===1)return false;return true;case "nth":l=h[2];var q=h[3];if(l===1&&q===0)return true;h=h[0];var p=g.parentNode;if(p&&(p.sizcache!==h||!g.nodeIndex)){var v=0;for(m=p.firstChild;m;m=
+m.nextSibling)if(m.nodeType===1)m.nodeIndex=++v;p.sizcache=h}g=g.nodeIndex-q;return l===0?g===0:g%l===0&&g/l>=0}},ID:function(g,h){return g.nodeType===1&&g.getAttribute("id")===h},TAG:function(g,h){return h==="*"&&g.nodeType===1||g.nodeName.toLowerCase()===h},CLASS:function(g,h){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(h)>-1},ATTR:function(g,h){var l=h[1];g=n.attrHandle[l]?n.attrHandle[l](g):g[l]!=null?g[l]:g.getAttribute(l);l=g+"";var m=h[2];h=h[4];return g==null?m==="!=":m===
+"="?l===h:m==="*="?l.indexOf(h)>=0:m==="~="?(" "+l+" ").indexOf(h)>=0:!h?l&&g!==false:m==="!="?l!==h:m==="^="?l.indexOf(h)===0:m==="$="?l.substr(l.length-h.length)===h:m==="|="?l===h||l.substr(0,h.length+1)===h+"-":false},POS:function(g,h,l,m){var q=n.setFilters[h[2]];if(q)return q(g,l,h,m)}}},r=n.match.POS;for(var u in n.match){n.match[u]=new RegExp(n.match[u].source+/(?![^\[]*\])(?![^\(]*\))/.source);n.leftMatch[u]=new RegExp(/(^(?:.|\r|\n)*?)/.source+n.match[u].source.replace(/\\(\d+)/g,function(g,
+h){return"\\"+(h-0+1)}))}var z=function(g,h){g=Array.prototype.slice.call(g,0);if(h){h.push.apply(h,g);return h}return g};try{Array.prototype.slice.call(s.documentElement.childNodes,0)}catch(C){z=function(g,h){h=h||[];if(j.call(g)==="[object Array]")Array.prototype.push.apply(h,g);else if(typeof g.length==="number")for(var l=0,m=g.length;l<m;l++)h.push(g[l]);else for(l=0;g[l];l++)h.push(g[l]);return h}}var B;if(s.documentElement.compareDocumentPosition)B=function(g,h){if(!g.compareDocumentPosition||
+!h.compareDocumentPosition){if(g==h)i=true;return g.compareDocumentPosition?-1:1}g=g.compareDocumentPosition(h)&4?-1:g===h?0:1;if(g===0)i=true;return g};else if("sourceIndex"in s.documentElement)B=function(g,h){if(!g.sourceIndex||!h.sourceIndex){if(g==h)i=true;return g.sourceIndex?-1:1}g=g.sourceIndex-h.sourceIndex;if(g===0)i=true;return g};else if(s.createRange)B=function(g,h){if(!g.ownerDocument||!h.ownerDocument){if(g==h)i=true;return g.ownerDocument?-1:1}var l=g.ownerDocument.createRange(),m=
+h.ownerDocument.createRange();l.setStart(g,0);l.setEnd(g,0);m.setStart(h,0);m.setEnd(h,0);g=l.compareBoundaryPoints(Range.START_TO_END,m);if(g===0)i=true;return g};(function(){var g=s.createElement("div"),h="script"+(new Date).getTime();g.innerHTML="<a name='"+h+"'/>";var l=s.documentElement;l.insertBefore(g,l.firstChild);if(s.getElementById(h)){n.find.ID=function(m,q,p){if(typeof q.getElementById!=="undefined"&&!p)return(q=q.getElementById(m[1]))?q.id===m[1]||typeof q.getAttributeNode!=="undefined"&&
+q.getAttributeNode("id").nodeValue===m[1]?[q]:w:[]};n.filter.ID=function(m,q){var p=typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id");return m.nodeType===1&&p&&p.nodeValue===q}}l.removeChild(g);l=g=null})();(function(){var g=s.createElement("div");g.appendChild(s.createComment(""));if(g.getElementsByTagName("*").length>0)n.find.TAG=function(h,l){l=l.getElementsByTagName(h[1]);if(h[1]==="*"){h=[];for(var m=0;l[m];m++)l[m].nodeType===1&&h.push(l[m]);l=h}return l};g.innerHTML="<a href='#'></a>";
+if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")n.attrHandle.href=function(h){return h.getAttribute("href",2)};g=null})();s.querySelectorAll&&function(){var g=k,h=s.createElement("div");h.innerHTML="<p class='TEST'></p>";if(!(h.querySelectorAll&&h.querySelectorAll(".TEST").length===0)){k=function(m,q,p,v){q=q||s;if(!v&&q.nodeType===9&&!x(q))try{return z(q.querySelectorAll(m),p)}catch(t){}return g(m,q,p,v)};for(var l in g)k[l]=g[l];h=null}}();
+(function(){var g=s.createElement("div");g.innerHTML="<div class='test e'></div><div class='test'></div>";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length===0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){n.order.splice(1,0,"CLASS");n.find.CLASS=function(h,l,m){if(typeof l.getElementsByClassName!=="undefined"&&!m)return l.getElementsByClassName(h[1])};g=null}}})();var E=s.compareDocumentPosition?function(g,h){return!!(g.compareDocumentPosition(h)&16)}:
+function(g,h){return g!==h&&(g.contains?g.contains(h):true)},x=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false},ga=function(g,h){var l=[],m="",q;for(h=h.nodeType?[h]:h;q=n.match.PSEUDO.exec(g);){m+=q[0];g=g.replace(n.match.PSEUDO,"")}g=n.relative[g]?g+"*":g;q=0;for(var p=h.length;q<p;q++)k(g,h[q],l);return k.filter(m,l)};c.find=k;c.expr=k.selectors;c.expr[":"]=c.expr.filters;c.unique=k.uniqueSort;c.text=a;c.isXMLDoc=x;c.contains=E})();var eb=/Until$/,fb=/^(?:parents|prevUntil|prevAll)/,
+gb=/,/;R=Array.prototype.slice;var Ia=function(a,b,d){if(c.isFunction(b))return c.grep(a,function(e,j){return!!b.call(e,j,e)===d});else if(b.nodeType)return c.grep(a,function(e){return e===b===d});else if(typeof b==="string"){var f=c.grep(a,function(e){return e.nodeType===1});if(Ua.test(b))return c.filter(b,f,!d);else b=c.filter(b,f)}return c.grep(a,function(e){return c.inArray(e,b)>=0===d})};c.fn.extend({find:function(a){for(var b=this.pushStack("","find",a),d=0,f=0,e=this.length;f<e;f++){d=b.length;
+c.find(a,this[f],b);if(f>0)for(var j=d;j<b.length;j++)for(var i=0;i<d;i++)if(b[i]===b[j]){b.splice(j--,1);break}}return b},has:function(a){var b=c(a);return this.filter(function(){for(var d=0,f=b.length;d<f;d++)if(c.contains(this,b[d]))return true})},not:function(a){return this.pushStack(Ia(this,a,false),"not",a)},filter:function(a){return this.pushStack(Ia(this,a,true),"filter",a)},is:function(a){return!!a&&c.filter(a,this).length>0},closest:function(a,b){if(c.isArray(a)){var d=[],f=this[0],e,j=
+{},i;if(f&&a.length){e=0;for(var o=a.length;e<o;e++){i=a[e];j[i]||(j[i]=c.expr.match.POS.test(i)?c(i,b||this.context):i)}for(;f&&f.ownerDocument&&f!==b;){for(i in j){e=j[i];if(e.jquery?e.index(f)>-1:c(f).is(e)){d.push({selector:i,elem:f});delete j[i]}}f=f.parentNode}}return d}var k=c.expr.match.POS.test(a)?c(a,b||this.context):null;return this.map(function(n,r){for(;r&&r.ownerDocument&&r!==b;){if(k?k.index(r)>-1:c(r).is(a))return r;r=r.parentNode}return null})},index:function(a){if(!a||typeof a===
+"string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){a=typeof a==="string"?c(a,b||this.context):c.makeArray(a);b=c.merge(this.get(),a);return this.pushStack(qa(a[0])||qa(b[0])?b:c.unique(b))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode",
+d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a,2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")},nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")?
+a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a,b){c.fn[a]=function(d,f){var e=c.map(this,b,d);eb.test(a)||(f=d);if(f&&typeof f==="string")e=c.filter(f,e);e=this.length>1?c.unique(e):e;if((this.length>1||gb.test(f))&&fb.test(a))e=e.reverse();return this.pushStack(e,a,R.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return c.find.matches(a,b)},dir:function(a,b,d){var f=[];for(a=a[b];a&&a.nodeType!==9&&(d===w||a.nodeType!==1||!c(a).is(d));){a.nodeType===
+1&&f.push(a);a=a[b]}return f},nth:function(a,b,d){b=b||1;for(var f=0;a;a=a[d])if(a.nodeType===1&&++f===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==b&&d.push(a);return d}});var Ja=/ jQuery\d+="(?:\d+|null)"/g,V=/^\s+/,Ka=/(<([\w:]+)[^>]*?)\/>/g,hb=/^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,La=/<([\w:]+)/,ib=/<tbody/i,jb=/<|&#?\w+;/,ta=/<script|<object|<embed|<option|<style/i,ua=/checked\s*(?:[^=]|=\s*.checked.)/i,Ma=function(a,b,d){return hb.test(d)?
+a:b+"></"+d+">"},F={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]};F.optgroup=F.option;F.tbody=F.tfoot=F.colgroup=F.caption=F.thead;F.th=F.td;if(!c.support.htmlSerialize)F._default=[1,"div<div>","</div>"];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d=
+c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==w)return this.empty().append((this[0]&&this[0].ownerDocument||s).createTextNode(a));return c.text(this)},wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this},
+wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length?d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})},
+prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,
+this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},remove:function(a,b){for(var d=0,f;(f=this[d])!=null;d++)if(!a||c.filter(a,[f]).length){if(!b&&f.nodeType===1){c.cleanData(f.getElementsByTagName("*"));c.cleanData([f])}f.parentNode&&f.parentNode.removeChild(f)}return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++)for(b.nodeType===1&&c.cleanData(b.getElementsByTagName("*"));b.firstChild;)b.removeChild(b.firstChild);
+return this},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&!c.isXMLDoc(this)){var d=this.outerHTML,f=this.ownerDocument;if(!d){d=f.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(Ja,"").replace(/=([^="'>\s]+\/)>/g,'="$1">').replace(V,"")],f)[0]}else return this.cloneNode(true)});if(a===true){ra(this,b);ra(this.find("*"),b.find("*"))}return b},html:function(a){if(a===w)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(Ja,
+""):null;else if(typeof a==="string"&&!ta.test(a)&&(c.support.leadingWhitespace||!V.test(a))&&!F[(La.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Ka,Ma);try{for(var b=0,d=this.length;b<d;b++)if(this[b].nodeType===1){c.cleanData(this[b].getElementsByTagName("*"));this[b].innerHTML=a}}catch(f){this.empty().append(a)}}else c.isFunction(a)?this.each(function(e){var j=c(this),i=j.html();j.empty().append(function(){return a.call(this,e,i)})}):this.empty().append(a);return this},replaceWith:function(a){if(this[0]&&
+this[0].parentNode){if(c.isFunction(a))return this.each(function(b){var d=c(this),f=d.html();d.replaceWith(a.call(this,b,f))});if(typeof a!=="string")a=c(a).detach();return this.each(function(){var b=this.nextSibling,d=this.parentNode;c(this).remove();b?c(b).before(a):c(d).append(a)})}else return this.pushStack(c(c.isFunction(a)?a():a),"replaceWith",a)},detach:function(a){return this.remove(a,true)},domManip:function(a,b,d){function f(u){return c.nodeName(u,"table")?u.getElementsByTagName("tbody")[0]||
+u.appendChild(u.ownerDocument.createElement("tbody")):u}var e,j,i=a[0],o=[],k;if(!c.support.checkClone&&arguments.length===3&&typeof i==="string"&&ua.test(i))return this.each(function(){c(this).domManip(a,b,d,true)});if(c.isFunction(i))return this.each(function(u){var z=c(this);a[0]=i.call(this,u,b?z.html():w);z.domManip(a,b,d)});if(this[0]){e=i&&i.parentNode;e=c.support.parentNode&&e&&e.nodeType===11&&e.childNodes.length===this.length?{fragment:e}:sa(a,this,o);k=e.fragment;if(j=k.childNodes.length===
+1?(k=k.firstChild):k.firstChild){b=b&&c.nodeName(j,"tr");for(var n=0,r=this.length;n<r;n++)d.call(b?f(this[n],j):this[n],n>0||e.cacheable||this.length>1?k.cloneNode(true):k)}o.length&&c.each(o,Qa)}return this}});c.fragments={};c.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=function(d){var f=[];d=c(d);var e=this.length===1&&this[0].parentNode;if(e&&e.nodeType===11&&e.childNodes.length===1&&d.length===1){d[b](this[0]);
+return this}else{e=0;for(var j=d.length;e<j;e++){var i=(e>0?this.clone(true):this).get();c.fn[b].apply(c(d[e]),i);f=f.concat(i)}return this.pushStack(f,a,d.selector)}}});c.extend({clean:function(a,b,d,f){b=b||s;if(typeof b.createElement==="undefined")b=b.ownerDocument||b[0]&&b[0].ownerDocument||s;for(var e=[],j=0,i;(i=a[j])!=null;j++){if(typeof i==="number")i+="";if(i){if(typeof i==="string"&&!jb.test(i))i=b.createTextNode(i);else if(typeof i==="string"){i=i.replace(Ka,Ma);var o=(La.exec(i)||["",
+""])[1].toLowerCase(),k=F[o]||F._default,n=k[0],r=b.createElement("div");for(r.innerHTML=k[1]+i+k[2];n--;)r=r.lastChild;if(!c.support.tbody){n=ib.test(i);o=o==="table"&&!n?r.firstChild&&r.firstChild.childNodes:k[1]==="<table>"&&!n?r.childNodes:[];for(k=o.length-1;k>=0;--k)c.nodeName(o[k],"tbody")&&!o[k].childNodes.length&&o[k].parentNode.removeChild(o[k])}!c.support.leadingWhitespace&&V.test(i)&&r.insertBefore(b.createTextNode(V.exec(i)[0]),r.firstChild);i=r.childNodes}if(i.nodeType)e.push(i);else e=
+c.merge(e,i)}}if(d)for(j=0;e[j];j++)if(f&&c.nodeName(e[j],"script")&&(!e[j].type||e[j].type.toLowerCase()==="text/javascript"))f.push(e[j].parentNode?e[j].parentNode.removeChild(e[j]):e[j]);else{e[j].nodeType===1&&e.splice.apply(e,[j+1,0].concat(c.makeArray(e[j].getElementsByTagName("script"))));d.appendChild(e[j])}return e},cleanData:function(a){for(var b,d,f=c.cache,e=c.event.special,j=c.support.deleteExpando,i=0,o;(o=a[i])!=null;i++)if(d=o[c.expando]){b=f[d];if(b.events)for(var k in b.events)e[k]?
+c.event.remove(o,k):Ca(o,k,b.handle);if(j)delete o[c.expando];else o.removeAttribute&&o.removeAttribute(c.expando);delete f[d]}}});var kb=/z-?index|font-?weight|opacity|zoom|line-?height/i,Na=/alpha\([^)]*\)/,Oa=/opacity=([^)]*)/,ha=/float/i,ia=/-([a-z])/ig,lb=/([A-Z])/g,mb=/^-?\d+(?:px)?$/i,nb=/^-?\d/,ob={position:"absolute",visibility:"hidden",display:"block"},pb=["Left","Right"],qb=["Top","Bottom"],rb=s.defaultView&&s.defaultView.getComputedStyle,Pa=c.support.cssFloat?"cssFloat":"styleFloat",ja=
+function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){return X(this,a,b,true,function(d,f,e){if(e===w)return c.curCSS(d,f);if(typeof e==="number"&&!kb.test(f))e+="px";c.style(d,f,e)})};c.extend({style:function(a,b,d){if(!a||a.nodeType===3||a.nodeType===8)return w;if((b==="width"||b==="height")&&parseFloat(d)<0)d=w;var f=a.style||a,e=d!==w;if(!c.support.opacity&&b==="opacity"){if(e){f.zoom=1;b=parseInt(d,10)+""==="NaN"?"":"alpha(opacity="+d*100+")";a=f.filter||c.curCSS(a,"filter")||"";f.filter=
+Na.test(a)?a.replace(Na,b):b}return f.filter&&f.filter.indexOf("opacity=")>=0?parseFloat(Oa.exec(f.filter)[1])/100+"":""}if(ha.test(b))b=Pa;b=b.replace(ia,ja);if(e)f[b]=d;return f[b]},css:function(a,b,d,f){if(b==="width"||b==="height"){var e,j=b==="width"?pb:qb;function i(){e=b==="width"?a.offsetWidth:a.offsetHeight;f!=="border"&&c.each(j,function(){f||(e-=parseFloat(c.curCSS(a,"padding"+this,true))||0);if(f==="margin")e+=parseFloat(c.curCSS(a,"margin"+this,true))||0;else e-=parseFloat(c.curCSS(a,
+"border"+this+"Width",true))||0})}a.offsetWidth!==0?i():c.swap(a,ob,i);return Math.max(0,Math.round(e))}return c.curCSS(a,b,d)},curCSS:function(a,b,d){var f,e=a.style;if(!c.support.opacity&&b==="opacity"&&a.currentStyle){f=Oa.test(a.currentStyle.filter||"")?parseFloat(RegExp.$1)/100+"":"";return f===""?"1":f}if(ha.test(b))b=Pa;if(!d&&e&&e[b])f=e[b];else if(rb){if(ha.test(b))b="float";b=b.replace(lb,"-$1").toLowerCase();e=a.ownerDocument.defaultView;if(!e)return null;if(a=e.getComputedStyle(a,null))f=
+a.getPropertyValue(b);if(b==="opacity"&&f==="")f="1"}else if(a.currentStyle){d=b.replace(ia,ja);f=a.currentStyle[b]||a.currentStyle[d];if(!mb.test(f)&&nb.test(f)){b=e.left;var j=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;e.left=d==="fontSize"?"1em":f||0;f=e.pixelLeft+"px";e.left=b;a.runtimeStyle.left=j}}return f},swap:function(a,b,d){var f={};for(var e in b){f[e]=a.style[e];a.style[e]=b[e]}d.call(a);for(e in b)a.style[e]=f[e]}});if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b=
+a.offsetWidth,d=a.offsetHeight,f=a.nodeName.toLowerCase()==="tr";return b===0&&d===0&&!f?true:b>0&&d>0&&!f?false:c.curCSS(a,"display")==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var sb=J(),tb=/<script(.|\s)*?\/script>/gi,ub=/select|textarea/i,vb=/color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i,N=/=\?(&|$)/,ka=/\?/,wb=/(\?|&)_=.*?(&|$)/,xb=/^(\w+:)?\/\/([^\/?#]+)/,yb=/%20/g,zb=c.fn.load;c.fn.extend({load:function(a,b,d){if(typeof a!==
+"string")return zb.call(this,a);else if(!this.length)return this;var f=a.indexOf(" ");if(f>=0){var e=a.slice(f,a.length);a=a.slice(0,f)}f="GET";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b==="object"){b=c.param(b,c.ajaxSettings.traditional);f="POST"}var j=this;c.ajax({url:a,type:f,dataType:"html",data:b,complete:function(i,o){if(o==="success"||o==="notmodified")j.html(e?c("<div />").append(i.responseText.replace(tb,"")).find(e):i.responseText);d&&j.each(d,[i.responseText,o,i])}});return this},
+serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||ub.test(this.nodeName)||vb.test(this.type))}).map(function(a,b){a=c(this).val();return a==null?null:c.isArray(a)?c.map(a,function(d){return{name:b.name,value:d}}):{name:b.name,value:a}}).get()}});c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),
+function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:f})},getScript:function(a,b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:f})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href,
+global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:A.XMLHttpRequest&&(A.location.protocol!=="file:"||!A.ActiveXObject)?function(){return new A.XMLHttpRequest}:function(){try{return new A.ActiveXObject("Microsoft.XMLHTTP")}catch(a){}},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},etag:{},ajax:function(a){function b(){e.success&&
+e.success.call(k,o,i,x);e.global&&f("ajaxSuccess",[x,e])}function d(){e.complete&&e.complete.call(k,x,i);e.global&&f("ajaxComplete",[x,e]);e.global&&!--c.active&&c.event.trigger("ajaxStop")}function f(q,p){(e.context?c(e.context):c.event).trigger(q,p)}var e=c.extend(true,{},c.ajaxSettings,a),j,i,o,k=a&&a.context||e,n=e.type.toUpperCase();if(e.data&&e.processData&&typeof e.data!=="string")e.data=c.param(e.data,e.traditional);if(e.dataType==="jsonp"){if(n==="GET")N.test(e.url)||(e.url+=(ka.test(e.url)?
+"&":"?")+(e.jsonp||"callback")+"=?");else if(!e.data||!N.test(e.data))e.data=(e.data?e.data+"&":"")+(e.jsonp||"callback")+"=?";e.dataType="json"}if(e.dataType==="json"&&(e.data&&N.test(e.data)||N.test(e.url))){j=e.jsonpCallback||"jsonp"+sb++;if(e.data)e.data=(e.data+"").replace(N,"="+j+"$1");e.url=e.url.replace(N,"="+j+"$1");e.dataType="script";A[j]=A[j]||function(q){o=q;b();d();A[j]=w;try{delete A[j]}catch(p){}z&&z.removeChild(C)}}if(e.dataType==="script"&&e.cache===null)e.cache=false;if(e.cache===
+false&&n==="GET"){var r=J(),u=e.url.replace(wb,"$1_="+r+"$2");e.url=u+(u===e.url?(ka.test(e.url)?"&":"?")+"_="+r:"")}if(e.data&&n==="GET")e.url+=(ka.test(e.url)?"&":"?")+e.data;e.global&&!c.active++&&c.event.trigger("ajaxStart");r=(r=xb.exec(e.url))&&(r[1]&&r[1]!==location.protocol||r[2]!==location.host);if(e.dataType==="script"&&n==="GET"&&r){var z=s.getElementsByTagName("head")[0]||s.documentElement,C=s.createElement("script");C.src=e.url;if(e.scriptCharset)C.charset=e.scriptCharset;if(!j){var B=
+false;C.onload=C.onreadystatechange=function(){if(!B&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){B=true;b();d();C.onload=C.onreadystatechange=null;z&&C.parentNode&&z.removeChild(C)}}}z.insertBefore(C,z.firstChild);return w}var E=false,x=e.xhr();if(x){e.username?x.open(n,e.url,e.async,e.username,e.password):x.open(n,e.url,e.async);try{if(e.data||a&&a.contentType)x.setRequestHeader("Content-Type",e.contentType);if(e.ifModified){c.lastModified[e.url]&&x.setRequestHeader("If-Modified-Since",
+c.lastModified[e.url]);c.etag[e.url]&&x.setRequestHeader("If-None-Match",c.etag[e.url])}r||x.setRequestHeader("X-Requested-With","XMLHttpRequest");x.setRequestHeader("Accept",e.dataType&&e.accepts[e.dataType]?e.accepts[e.dataType]+", */*":e.accepts._default)}catch(ga){}if(e.beforeSend&&e.beforeSend.call(k,x,e)===false){e.global&&!--c.active&&c.event.trigger("ajaxStop");x.abort();return false}e.global&&f("ajaxSend",[x,e]);var g=x.onreadystatechange=function(q){if(!x||x.readyState===0||q==="abort"){E||
+d();E=true;if(x)x.onreadystatechange=c.noop}else if(!E&&x&&(x.readyState===4||q==="timeout")){E=true;x.onreadystatechange=c.noop;i=q==="timeout"?"timeout":!c.httpSuccess(x)?"error":e.ifModified&&c.httpNotModified(x,e.url)?"notmodified":"success";var p;if(i==="success")try{o=c.httpData(x,e.dataType,e)}catch(v){i="parsererror";p=v}if(i==="success"||i==="notmodified")j||b();else c.handleError(e,x,i,p);d();q==="timeout"&&x.abort();if(e.async)x=null}};try{var h=x.abort;x.abort=function(){x&&h.call(x);
+g("abort")}}catch(l){}e.async&&e.timeout>0&&setTimeout(function(){x&&!E&&g("timeout")},e.timeout);try{x.send(n==="POST"||n==="PUT"||n==="DELETE"?e.data:null)}catch(m){c.handleError(e,x,null,m);d()}e.async||g();return x}},handleError:function(a,b,d,f){if(a.error)a.error.call(a.context||a,b,d,f);if(a.global)(a.context?c(a.context):c.event).trigger("ajaxError",[b,a,f])},active:0,httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status===
+1223||a.status===0}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"),f=a.getResponseHeader("Etag");if(d)c.lastModified[b]=d;if(f)c.etag[b]=f;return a.status===304||a.status===0},httpData:function(a,b,d){var f=a.getResponseHeader("content-type")||"",e=b==="xml"||!b&&f.indexOf("xml")>=0;a=e?a.responseXML:a.responseText;e&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b===
+"json"||!b&&f.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&f.indexOf("javascript")>=0)c.globalEval(a);return a},param:function(a,b){function d(i,o){if(c.isArray(o))c.each(o,function(k,n){b||/\[\]$/.test(i)?f(i,n):d(i+"["+(typeof n==="object"||c.isArray(n)?k:"")+"]",n)});else!b&&o!=null&&typeof o==="object"?c.each(o,function(k,n){d(i+"["+k+"]",n)}):f(i,o)}function f(i,o){o=c.isFunction(o)?o():o;e[e.length]=encodeURIComponent(i)+"="+encodeURIComponent(o)}var e=[];if(b===w)b=c.ajaxSettings.traditional;
+if(c.isArray(a)||a.jquery)c.each(a,function(){f(this.name,this.value)});else for(var j in a)d(j,a[j]);return e.join("&").replace(yb,"+")}});var la={},Ab=/toggle|show|hide/,Bb=/^([+-]=)?([\d+-.]+)(.*)$/,W,va=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];c.fn.extend({show:function(a,b){if(a||a===0)return this.animate(K("show",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");
+this[a].style.display=d||"";if(c.css(this[a],"display")==="none"){d=this[a].nodeName;var f;if(la[d])f=la[d];else{var e=c("<"+d+" />").appendTo("body");f=e.css("display");if(f==="none")f="block";e.remove();la[d]=f}c.data(this[a],"olddisplay",f)}}a=0;for(b=this.length;a<b;a++)this[a].style.display=c.data(this[a],"olddisplay")||"";return this}},hide:function(a,b){if(a||a===0)return this.animate(K("hide",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");!d&&d!=="none"&&c.data(this[a],
+"olddisplay",c.css(this[a],"display"))}a=0;for(b=this.length;a<b;a++)this[a].style.display="none";return this}},_toggle:c.fn.toggle,toggle:function(a,b){var d=typeof a==="boolean";if(c.isFunction(a)&&c.isFunction(b))this._toggle.apply(this,arguments);else a==null||d?this.each(function(){var f=d?a:c(this).is(":hidden");c(this)[f?"show":"hide"]()}):this.animate(K("toggle",3),a,b);return this},fadeTo:function(a,b,d){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,d)},
+animate:function(a,b,d,f){var e=c.speed(b,d,f);if(c.isEmptyObject(a))return this.each(e.complete);return this[e.queue===false?"each":"queue"](function(){var j=c.extend({},e),i,o=this.nodeType===1&&c(this).is(":hidden"),k=this;for(i in a){var n=i.replace(ia,ja);if(i!==n){a[n]=a[i];delete a[i];i=n}if(a[i]==="hide"&&o||a[i]==="show"&&!o)return j.complete.call(this);if((i==="height"||i==="width")&&this.style){j.display=c.css(this,"display");j.overflow=this.style.overflow}if(c.isArray(a[i])){(j.specialEasing=
+j.specialEasing||{})[i]=a[i][1];a[i]=a[i][0]}}if(j.overflow!=null)this.style.overflow="hidden";j.curAnim=c.extend({},a);c.each(a,function(r,u){var z=new c.fx(k,j,r);if(Ab.test(u))z[u==="toggle"?o?"show":"hide":u](a);else{var C=Bb.exec(u),B=z.cur(true)||0;if(C){u=parseFloat(C[2]);var E=C[3]||"px";if(E!=="px"){k.style[r]=(u||1)+E;B=(u||1)/z.cur(true)*B;k.style[r]=B+E}if(C[1])u=(C[1]==="-="?-1:1)*u+B;z.custom(B,u,E)}else z.custom(B,u,"")}});return true})},stop:function(a,b){var d=c.timers;a&&this.queue([]);
+this.each(function(){for(var f=d.length-1;f>=0;f--)if(d[f].elem===this){b&&d[f](true);d.splice(f,1)}});b||this.dequeue();return this}});c.each({slideDown:K("show",1),slideUp:K("hide",1),slideToggle:K("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(a,b){c.fn[a]=function(d,f){return this.animate(b,d,f)}});c.extend({speed:function(a,b,d){var f=a&&typeof a==="object"?a:{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};f.duration=c.fx.off?0:typeof f.duration===
+"number"?f.duration:c.fx.speeds[f.duration]||c.fx.speeds._default;f.old=f.complete;f.complete=function(){f.queue!==false&&c(this).dequeue();c.isFunction(f.old)&&f.old.call(this)};return f},easing:{linear:function(a,b,d,f){return d+f*a},swing:function(a,b,d,f){return(-Math.cos(a*Math.PI)/2+0.5)*f+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]||
+c.fx.step._default)(this);if((this.prop==="height"||this.prop==="width")&&this.elem.style)this.elem.style.display="block"},cur:function(a){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];return(a=parseFloat(c.css(this.elem,this.prop,a)))&&a>-10000?a:parseFloat(c.curCSS(this.elem,this.prop))||0},custom:function(a,b,d){function f(j){return e.step(j)}this.startTime=J();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start;
+this.pos=this.state=0;var e=this;f.elem=this.elem;if(f()&&c.timers.push(f)&&!W)W=setInterval(c.fx.tick,13)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(a){var b=J(),d=true;if(a||b>=this.options.duration+this.startTime){this.now=
+this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var f in this.options.curAnim)if(this.options.curAnim[f]!==true)d=false;if(d){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;a=c.data(this.elem,"olddisplay");this.elem.style.display=a?a:this.options.display;if(c.css(this.elem,"display")==="none")this.elem.style.display="block"}this.options.hide&&c(this.elem).hide();if(this.options.hide||this.options.show)for(var e in this.options.curAnim)c.style(this.elem,
+e,this.options.orig[e]);this.options.complete.call(this.elem)}return false}else{e=b-this.startTime;this.state=e/this.options.duration;a=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||a](this.state,e,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a=c.timers,b=0;b<a.length;b++)a[b]()||a.splice(b--,1);a.length||
+c.fx.stop()},stop:function(){clearInterval(W);W=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){c.style(a.elem,"opacity",a.now)},_default:function(a){if(a.elem.style&&a.elem.style[a.prop]!=null)a.elem.style[a.prop]=(a.prop==="width"||a.prop==="height"?Math.max(0,a.now):a.now)+a.unit;else a.elem[a.prop]=a.now}}});if(c.expr&&c.expr.filters)c.expr.filters.animated=function(a){return c.grep(c.timers,function(b){return a===b.elem}).length};c.fn.offset="getBoundingClientRect"in s.documentElement?
+function(a){var b=this[0];if(a)return this.each(function(e){c.offset.setOffset(this,a,e)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);var d=b.getBoundingClientRect(),f=b.ownerDocument;b=f.body;f=f.documentElement;return{top:d.top+(self.pageYOffset||c.support.boxModel&&f.scrollTop||b.scrollTop)-(f.clientTop||b.clientTop||0),left:d.left+(self.pageXOffset||c.support.boxModel&&f.scrollLeft||b.scrollLeft)-(f.clientLeft||b.clientLeft||0)}}:function(a){var b=
+this[0];if(a)return this.each(function(r){c.offset.setOffset(this,a,r)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);c.offset.initialize();var d=b.offsetParent,f=b,e=b.ownerDocument,j,i=e.documentElement,o=e.body;f=(e=e.defaultView)?e.getComputedStyle(b,null):b.currentStyle;for(var k=b.offsetTop,n=b.offsetLeft;(b=b.parentNode)&&b!==o&&b!==i;){if(c.offset.supportsFixedPosition&&f.position==="fixed")break;j=e?e.getComputedStyle(b,null):b.currentStyle;
+k-=b.scrollTop;n-=b.scrollLeft;if(b===d){k+=b.offsetTop;n+=b.offsetLeft;if(c.offset.doesNotAddBorder&&!(c.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(b.nodeName))){k+=parseFloat(j.borderTopWidth)||0;n+=parseFloat(j.borderLeftWidth)||0}f=d;d=b.offsetParent}if(c.offset.subtractsBorderForOverflowNotVisible&&j.overflow!=="visible"){k+=parseFloat(j.borderTopWidth)||0;n+=parseFloat(j.borderLeftWidth)||0}f=j}if(f.position==="relative"||f.position==="static"){k+=o.offsetTop;n+=o.offsetLeft}if(c.offset.supportsFixedPosition&&
+f.position==="fixed"){k+=Math.max(i.scrollTop,o.scrollTop);n+=Math.max(i.scrollLeft,o.scrollLeft)}return{top:k,left:n}};c.offset={initialize:function(){var a=s.body,b=s.createElement("div"),d,f,e,j=parseFloat(c.curCSS(a,"marginTop",true))||0;c.extend(b.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"});b.innerHTML="<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>";
+a.insertBefore(b,a.firstChild);d=b.firstChild;f=d.firstChild;e=d.nextSibling.firstChild.firstChild;this.doesNotAddBorder=f.offsetTop!==5;this.doesAddBorderForTableAndCells=e.offsetTop===5;f.style.position="fixed";f.style.top="20px";this.supportsFixedPosition=f.offsetTop===20||f.offsetTop===15;f.style.position=f.style.top="";d.style.overflow="hidden";d.style.position="relative";this.subtractsBorderForOverflowNotVisible=f.offsetTop===-5;this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==j;a.removeChild(b);
+c.offset.initialize=c.noop},bodyOffset:function(a){var b=a.offsetTop,d=a.offsetLeft;c.offset.initialize();if(c.offset.doesNotIncludeMarginInBodyOffset){b+=parseFloat(c.curCSS(a,"marginTop",true))||0;d+=parseFloat(c.curCSS(a,"marginLeft",true))||0}return{top:b,left:d}},setOffset:function(a,b,d){if(/static/.test(c.curCSS(a,"position")))a.style.position="relative";var f=c(a),e=f.offset(),j=parseInt(c.curCSS(a,"top",true),10)||0,i=parseInt(c.curCSS(a,"left",true),10)||0;if(c.isFunction(b))b=b.call(a,
+d,e);d={top:b.top-e.top+j,left:b.left-e.left+i};"using"in b?b.using.call(a,d):f.css(d)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),f=/^body|html$/i.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.curCSS(a,"marginTop",true))||0;d.left-=parseFloat(c.curCSS(a,"marginLeft",true))||0;f.top+=parseFloat(c.curCSS(b[0],"borderTopWidth",true))||0;f.left+=parseFloat(c.curCSS(b[0],"borderLeftWidth",true))||0;return{top:d.top-
+f.top,left:d.left-f.left}},offsetParent:function(){return this.map(function(){for(var a=this.offsetParent||s.body;a&&!/^body|html$/i.test(a.nodeName)&&c.css(a,"position")==="static";)a=a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(f){var e=this[0],j;if(!e)return null;if(f!==w)return this.each(function(){if(j=wa(this))j.scrollTo(!a?f:c(j).scrollLeft(),a?f:c(j).scrollTop());else this[d]=f});else return(j=wa(e))?"pageXOffset"in j?j[a?"pageYOffset":
+"pageXOffset"]:c.support.boxModel&&j.document.documentElement[d]||j.document.body[d]:e[d]}});c.each(["Height","Width"],function(a,b){var d=b.toLowerCase();c.fn["inner"+b]=function(){return this[0]?c.css(this[0],d,false,"padding"):null};c.fn["outer"+b]=function(f){return this[0]?c.css(this[0],d,false,f?"margin":"border"):null};c.fn[d]=function(f){var e=this[0];if(!e)return f==null?null:this;if(c.isFunction(f))return this.each(function(j){var i=c(this);i[d](f.call(this,j,i[d]()))});return"scrollTo"in
+e&&e.document?e.document.compatMode==="CSS1Compat"&&e.document.documentElement["client"+b]||e.document.body["client"+b]:e.nodeType===9?Math.max(e.documentElement["client"+b],e.body["scroll"+b],e.documentElement["scroll"+b],e.body["offset"+b],e.documentElement["offset"+b]):f===w?c.css(e,d):this.css(d,typeof f==="string"?f:f+"px")}});A.jQuery=A.$=c})(window);
index 7ecb7a2..d7bbdc0 100644 (file)
@@ -1,12 +1,13 @@
 {% extends 'base.html' %}
+{% load i18n %}
 
 {% block title %}1% podatku na WolneLektury.pl{% endblock title %}
 
 {% block extrahead %}
-    <link rel="stylesheet" href="/static/css/reset.css" type="text/css" charset="utf-8" />
-    <link rel="stylesheet" href="/static/css/960.css" type="text/css" charset="utf-8" />
-    <link rel="stylesheet" href="/static/css/text.css" type="text/css" charset="utf-8" />
-    <link rel="stylesheet" href="/static/css/1percent.css" type="text/css" charset="utf-8" />
+    <link rel="stylesheet" href="{{ STATIC_URL }}css/reset.css" type="text/css" charset="utf-8" />
+    <link rel="stylesheet" href="{{ STATIC_URL }}css/960.css" type="text/css" charset="utf-8" />
+    <link rel="stylesheet" href="{{ STATIC_URL }}css/text.css" type="text/css" charset="utf-8" />
+    <link rel="stylesheet" href="{{ STATIC_URL }}css/1percent.css" type="text/css" charset="utf-8" />
 {% endblock extrahead %}
 
 {% block bodycontent %}
         
         <div id="header" class="grid_12">
             <div id="logos" class="alpha grid_5 suffix_1">
-                <img src="/static/img/logo-big.png" />
-                <img src="/static/img/1percent-big.png" />
+                <img src="{{ STATIC_URL }}img/logo-big.png" />
+                <img src="{{ STATIC_URL }}img/1percent-big.png" />
             </div>
             <div id="title" class="omega grid_6">
-                <img src="/static/img/tagline.png" />
+                <img src="{{ STATIC_URL }}img/tagline.png" />
             </div>
         </div>
         
@@ -40,7 +41,7 @@
                 <p>Przy wypełnianiu formularza PIT w polu <em>wniosek o przekazanie 1% podatku na rzecz OPP</em> wpisz:</p>
 <pre>Fundacja Nowoczesna Polska
 KRS 0000070056</pre>
-                <img src="/static/img/pit37.png" />
+                <img src="{{ STATIC_URL }}img/pit37.png" />
                 <a href="https://www.pitroczny.pl/pit?krs=0000070056"><img src="http://www.pitroczny.pl/pl/resources/images/1oppmini.png" alt="1% dla OPP" style="float: left; margin: 8px 8px 0 0" ></a>
                 <p>Skorzystaj z programu ułatwiającego przygotowanie deklaracji podatkowej on-line.</p>
             </div>
index e51ec82..cc8ccc9 100644 (file)
@@ -1,19 +1,20 @@
+{% load i18n %}
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
         "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
 <html xmlns="http://www.w3.org/1999/xhtml" lang="pl" xml:lang="pl">
 <head>
-<title>404 - Zagubiona strona w WolneLektury.pl</title>
+<title>404 - {% trans "Page does not exist" %} - WolneLektury.pl</title>
 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
 <link rel="stylesheet" type="text/css" href="http://yui.yahooapis.com/2.5.2/build/reset/reset-min.css"> 
-<link rel="stylesheet" href="/static/css/error.css" type="text/css" />
+<link rel="stylesheet" href="{{ STATIC_URL }}css/error.css" type="text/css" />
 </head>
 
 <body>
 
-<a href="/"><img src="/static/img/logo.png" /></a>
-<p class="haj" style="font-weight: bold">Podana strona nie istnieje</p>
+<a href="/"><img src="{{ STATIC_URL }}img/logo.png" /></a>
+<p class="haj" style="font-weight: bold">{% trans "Page does not exist" %}</p>
 <p>
-Przepraszamy, ale ta strona nie istnieje. Sprawdź czy podałeś dobry adres, lub przejdź do <a href="/">strony głównej</a>.
+{% trans "We are sorry, but this page does not exist. Please check if you entered correct address or go to "%} <a href="/">{% trans "main page" %}</a>.
 </p>
 
 <script src="http://www.google-analytics.com/urchin.js" type="text/javascript">
index 8346afe..4ecf3ad 100644 (file)
@@ -1,20 +1,58 @@
+{% load i18n %}
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
         "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
 <html xmlns="http://www.w3.org/1999/xhtml" lang="pl" xml:lang="pl">
 <head>
-<title>500 - Błąd serwera w WolneLektury.pl</title>
+<title>500 - {% trans "Server error" %} WolneLektury.pl</title>
 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
 <link rel="stylesheet" type="text/css" href="http://yui.yahooapis.com/2.5.2/build/reset/reset-min.css"> 
-<link rel="stylesheet" href="/static/css/error.css" type="text/css" />
+<style type="text/css">
+
+body {
+font-family: verdana, arial, sans-serif; 
+margin: 2em;
+font-size: 100%;
+}
+
+p {
+width: 450px;
+margin-top: 0.5em;
+}
+
+img {
+    border: none;
+}
+
+.haj {
+    background-color: #ffc;
+    font-weight: bold;
+}
+
+a:link {
+    color: #037;
+    text-decoration: underline; 
+}
+
+a:visited {
+    color: #636;
+}
+
+a:active {
+    color: #900;
+    text-decoration: none;
+}
+a:hover {
+    color: #d46400;
+}
+</style>
+
 </head>
 
 <body>
 
-<a href="/"><img src="/static/img/logo.png" /></a>
-<p class="haj" style="font-weight: bold">Błąd serwera</p>
-<p>
-Przepraszamy, ale wystąpił błąd serwera. Na pewno już się nim zajmujemy i postaramy się naprawić tak szybko jak to tylko możliwe. Do tego czasu proponujemy żebyś przeszedł do <a href="/">strony głównej</a>.
-</p>
+<a href="/"><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAABKCAYAAAD+IBtNAAAACXBIWXMAAAMTAAADEwE9ZoPHAAAgAElEQVR4nO2dd5iU1fX4P+e+78zs7MIqIIigsLusikFBWERRUazYNSr4jVKjASMqIGCJbdAkVLGiwZ+NYgnExGiMGkSwIrJUgwpIF1B63d0p7z2/P2Zm2Q4LKyXO53n22SnnnnvOfee8t7y3CDXAY5/mnOClmc+MZal15DOJ6We73ILPQ23XbqwJ/XuLKrJzWq1f+aweF7hg1wci6IHMP8X/HlITSobl5zRx1axU5QfQo0QkLfHVd9YyTZQPNvy09aMRV2/cURP5JVFFdn3oP9lVOd8xpiPQAaEegEvhsXIBa2oyvxS/PGokQABGzmq2wqj8Y0dG4J7MbdFTY0bbi6Nng14gSB0gqsoMa3nf8XuvDWqzfCXAqFm53WJil9/bdtlne5OPKhKb4p4jrr+bWr0SoQGqEYWZKuZjUXuuiJzsnl9YL1WDpNhf3JpSpPCxwrmhFgsjwFeJvydDismccUJb6/cuRuUix2GIRk3/0MtZTUO9VhSB9nOsWQtUGSDbPwycEDBOj8hU29U40sSqXYSVN4xrP3AKwtPlSgoAIlMDX6PyWSo4UtQENRcgnpmuju06dEGTOve1XLUl+XlIsLA4GTB/HDH7hA4G+0ntk92LgbctLBToUJne8BROEkkbKyIdsLpZRF61nnnJf/GueeVsmEhmTOVXGMbXlF8pftmYmlJkC51pghh/xH9hVXIFbRZ/rqo/euh1AGrtLBHNejz/pGMqTOCkPYxQX6x3vbu58Bj3gsI7KwoOgHDDtHaImKgX+3S/HUqRghoMkPvO+26FKsvV04urkgsJVlX+IXDl2Pw8X6ETfkORaNSLdSsrqxPJNMpVWEa4F0XelC5EqtIt1pyDakGwTnT2/vqTIgXUYIAktL2H0GlPYmLM3wWps0W2nR9qu3ajKu8YY7uWlYs2Srsexfq2FE3em+zF6CUY/UTaEt0X81OkKEuNBoiV2AREjhv9xQntqpJrunzxNLDrfJbOAMboOyCnDMvPaVJSTixdrZo3pQs795S3fkg9QfM85YP98yJFit3UaIDck7fiSxVdYl29uSq5Ll3wgAlWuSYU6ujuNJEpAG6MK5Iy+kHwOISO1rOv7E3eERu8GBEjsfD7++NDihQlqdkmFiAwHvT/QvmN0quSi4h9WYR6mZetuDDUZvVaVBdax7k8+X3YZ6+3sCqtU9H0vcrYlU4WXRnoxHf750GKFLup8QCJoeMRrZ0RC1xbldwf2q74DsuXFuf3AIJMEbXnj83P8wEYlevxzPg9Pc/QSfiLpmR0MlYvdaymmlcpapQaD5B72y5bpapTxXUGDc9vep5q5U/rrcOLKlwxdFrzLM9Gp4hI2k7deLL+M72RKGdorGBcRen0ZdJiU/zXhaemvRqrG1zvGPs+4BShz9S0Pyl+2dR4gADYSPQ+taijvo8em52zbPjM3NCjM0/MLitXEAyMF9FVbnr4tvXBoo9R9Tzx5YVredcqfJ52GUtLykc+Ss+LTQmOiTUJrlXj/A2hgwjjY55e6H5W2LDWRZGvfw5/UvxyqbG5WBXxp1nNTg6g3VTNjSLaGJhurX2lwAn/LdR2bQHAiJnZ3R1jnosaudbx9C0xZkC/rWuuVWPf8J9f9JI+RSDcPP0WY7S3CC2BHxQmx2xsUvDC6MzUlJIUPyc/a4AkCSkmMKdZR8dqF1G5GiRDlMkxx76cs2zZjFVNc9/CcAWqhSYWbnnzpg2SuTG8Otw4vYfj6AOgR1vh7yYae9a9OPZpMih0Ko1j1t8LkTPFSHM8/cC9KPz7A+FTil8GByRAShJSjP/LZu0dV6911FzjmegthXkrPw7Mzr6QHYHFiSfyEpmW9rqxXGPVDPMHCsbKOawDCH9Ac3UDFzrWXILoJQhWkQXGyvyota8GOxV9dKB9SvG/ywEPEIBpWVlpzWOxlkE1tQs0Nr/R2tILq/RdmsbSgiuwDN4Z079lOF4HFd+FRvQChMaoWoWZ1upfA2H/q3LljgO6MCvFL4cDEiAKZn3j3NN9fr0cy6UoLTGJmcQqmwuNd2bj5csXFcuHMNGz074VkRNKKNlkRaeK5/3LF057LxUUKQ4EP1uAKJjNTZpcIPi6IVyWWOkXQfnCWpkS1aIP/a5rRM0XotKrzqqlpYZ0dTwZXuPAOQjiRM3X0qlw9c9la4oUlVGjAbIlO7upetoHIyermjwRGmH5XsR7Rz1nSuSHnZ805Kddmpfn27Jp0/Wo8yiAGys6PXPNmk15vXunazjtJN/6TUt21orYQFqjfIx8LMibtVce8dH06aFYTdqbIsWeqNEA2dgku6uIjBXkO1HmYL2JdX5Y8XHy+zWNGh3ndwK/cx3prUgdPJ6P+Hk0LeZmXJP9m9XbmmydIUJbsN/FdhRc5Dsi0MB6/l6C/Y0VEdT+Uzwz+Yg1R05JBUuKA8HP3gdZddRxjWqn+6+3jnYRy5kI21HzWlhiI45ZsWLF6nonNE6vFVvy23bt265Or79Q4DaFQWr5fO6EJ7oDtOgc8gfStl6hoj0EuUxhsxheR+2EOeOeSq39SPGz8bMEyJbs7KZW5VqxXItwFqAiZqoSe3k+/OO8FSuKkrKbs3LuVZVzLz73ij4qsjJG7Gq/9cc8Y9/xbFGrBRP+8t+Supt1G9SgthO7UTx6iqGVqn5rxYTmj3t80s/hS4pfNjUaIGsbNUoPumn/VCMXoqqKzLTGvhkJh//aeO3aCjvZW7JyFlnVEfVWLn+xdfcBm8Xo+DmvPDGgTfd+S0Bmzxn/xA2V5df6t3e1Ek/vV7WXRgrXHrNw8uQ9rhtJkaI61OhcLNfxX6lGLgS9T2PhJvVWLm1ff/nyUZUFx09Nm56pVnJ8schbAKI6V1UuAlSU5xSuad/55rqV5Tf3pdHzd4Wj/RFJD6Qf07kmfUmRAmo4QIxxO6mypu6KZcPqrVnzw57kXXV7Wkc/zlyzZhOAODJF0F+d0PnWxhEv8hKiGg7UurEqHYveeGYt2P+oSs8aciNFimJqNEAE7WTiU8/3yGoIYrjBIMV9h6hX+C+A9Iy0i75+7bktKB/gmJ57VBbTF4EOp/bsn7VvlqdIUTE1FiCbc3JOEaGR9dirAAk2yb5OIGC86OQWN/U7rWXX35+4YMJf/quWlXj2ooRxfwXNa9nt1pOr0hWJ1HtbkE0OttzOKClS7A81V4OoXIKqt93oh3sl70hPq/rukatWbfE7Ms2YwHgAMfzLiLk4r3dvX1Hh2rfBFgn+m6pStXByKCIi/7Qq+xwgj+efdMyw/Jyz9zV9itLk9ejd7MT/u73RwbZjf6mxAFHlAkVmZq9YsXVPsluys5say/mInQCAEDTCMQAxMRMRjortSrti4eTJO1HeM2Ku35POGPxHkOPzevRuti/2ezYyxVXz6bDZuXvctijFnrGS/mJGwPf2wbZjf6mRANG4nvai3sd7kl3fqGlrPEaB2VIvI+PfAKJYVa0DsOCV0V8q8o3jSncAa/VNEXJb//auVlXpTS/Y/qGCVdLO2ScnhACAG9N9S18DjMrPHjhq1vFPHaz8axTVpoq2btE55D/YpuwPNRIgG7KyWgpkiuN+WdH32xs3rrelSVa/zVk5C1y/O8caczoSu10WLowAqKgnIrXyevf2ASD2U1HaAjjp6f9SJWKiWmUtMmPyi5tVybdizt0XHyxmMYA45qR9SV+SQfOPzhg+o2m19SimD+gdZfcHOxwRxRMwZOyqdJj+cKBGNq826p6FKGGjxQHy07HHtvT5Ap3E04tjRs5R1ZioeUs1MrDuqlVTBWxSVlQsAnZL9Ehgg8T4rzrS56yr7q79+fPDt7Xpfue7VuT+1t37X6JiP3BU3q+9qs6X5eZjCdPFo9IHi1XhqC5W4TJPtfm+lkOS+uGMa4xjHgVyqpVQid9tram9vzYcdESjIAQKo3WAHw+2OftKjdQggj0bZZPf0+s2ZTV7Y0tWzo8+NzAf5U+eYCz02bBre8O6K7+/qd6qVVNKBgeAoh6ADabXAYgV7HrLs3bY523SdwGEC9d1t0b7qdr1BhmgIp9ub7p1Y+tu/d5s1fPOO9reeEe7Fp1Dfteaj8XQ9MRut5TbIGKPGLsYwEBuKNRxv24cjshmEbJHfnlsy+qkE9QPIJFwcH/yPxRQlQhANC1a/2Dbsj/UzPEHyskY6oE8iTJb1bysjjctHA5/1mhtfHOGqkgGjKvE+yF/f+EH4L7k94kpJE8DT3PppYGWdXPOdsXfSYy90LFytfUZx+/bEvaURYKQTsb5wIvV8sHGFiN+AJ9z+cpmhFi0pySVEVZvXUBcjBu8Gliwt+lUjV8EJBg87AME2ArgF/fog23I/lAzAWLs76LG1NJlu2Y05Kdd1U2uGE9Q1HHq7FH4vffCC2Aq8T/Ouuru2kV1ou09j7MdY89G2W6MVLtjGNvlLXJqxV+neU5z2PcAwRasw8lErb0EeLQaKf0AnkYP/wARswEUz9MGB9uU/aFGAqTeihUVds73FsFaEFTNngOkDJ+/PWIH8J/E3z5zd8c1a0bnNytQJF1dexLwz33VFT19/YbArNqeIidWJ51IPEDcmHPYB4iqXS8iqJpUDbK/qGAFMHDkwbJBBB2VL0tQWkm8BimFKvL4jKxzrOu7UcQerWiBtfr+3acvL3eaVUiwI2fJTwKNhuXnHHFv22Xb9pT/47NOyPHw/CCo0Vsfy292uWclCAQF/CL63KDTlr5XQ+7+7GiiBnEcUgGyvyjxAPE8qXYNUsOGLAZaYUqPZIVezkp77CvnLXzSCZT4bqqCMfxm5Fe51w9u9/1VZVUJug6kkcE0A+YkPx/9fou6bvom585zftwAMPqLrHM913nGw56cXH1gRC5RBVNiMYKqRoHDJkDEeusxBpFUgOw3BlUQjBvLqAl9p/W67ThP/QNR2ojqD57qisDGTcNmvvfq9qrSechiBwV2B8joSQRtlu8dRC8AQNmsqu+KMdmgZ4voFaFpWUeGziszg8DKOgzg2RNJBMjj+ScdE7NFczwyjnx0VvapD562fJH1uf8SqKXKDhESw7s6DdHizr1AgYP7Qk2UzYHCcWS9Kqhy3MG2ZX/YrwBp1b3vCY74X7Nqv4oW1um/cHKo9BFpoZA5eeXmM/zW9LCqHsYuj+768blyC5vUKAJC8fnqnNzrzjOP2JT2daKPsde06d5/pGe1H+ADUBGMCNH6R3XI69270+znn690VM3BrgJBkCOSh5Fqk+y+yeBQlTcCdZb0vPN4wqH8RukZNn2biLjpmf5TgDLnIuqPIDgOpwCvA3he5BEx0hDANaYxsCgisQv94sSaLl06b1V2bvy5jth3B7Vd/lh1/D5YdO7c2VkWbNR4q/qKlk4YtT75uSAb4nvCanPiVeNhuUVsuQBp06PfP2I2/GDZpa4V4Yg7HDTPiOQF/RtfBT4HyOvZrzPITXbZtnNE4h1vIwLq4A82HpTXre9VsyeMmZnUo6IqCIk2N0d3G5Th96Kf7MosehQYAvGltkeYyOV46pf09DdmPz+8XLu+Vc8770AZVLxQUu00VScgRs9E5GwtTB8A/KlSh2K6ETeeNhZ2GgBbcMxvACx8W/Dvxt0Gh5bExubn+bbGNt8rDi7YdWmZ339VVpVV+6PBQT1zCsCwr3JbgPYCUFhaV4/8FOAPbVcUl8OoWaog4nnOHh8UPjYr57cW85TFjr3ntGUDIV7bAdzVhcJinbOb5eFpL3CaW/GWuJuC98eOCmc5ym1RsQP3pn9UGa16DOiyTBmmotmZxCKndu8/3rEFD82e+Py6GKyIP2ST9BO73ZK1aMILy/c1n4NJuQeFqnKmj0C5CXt53fqentf7niOS79t37hxEpViuwPVtjcsNeFRVJqlyNcI6z+NRYFlSToQGVnwTj+42aHdzSiVxd9E0gKN3ba2DiCNGOgGS13PAsEwTWwfmJRznL1oUXtfqxttL2diy511nOCrJu+4yK7SeM/6p8/0b11+Kxn8wNr6BdqVYv7sh+TpgfA2GTmuepUgbACNEal3+w2ujvmr2+XZv6w+O4zwIRFWcQXceT7isLsdxfgRQo6cAOOhIRBwAE4vd36ft7HLnKKqKByCqteLvkcdm5Q4b+VX278rKephBAhkG6T966gmNAbym2UNsdu764dMaH/vnGc3OGvlV7lys5COmL6IXGMyt9qiCXuJ5/6dws0/LT8sZld+sx2Ozcl8IzW9W4fBsm8635QK06drvdqP2DRXNhvgInBFu8Zzga4DMe+WJFaqsBwg6wSpPPj6UKRcgAsYabVvyo9bd+32sxvclRUXPJT+MZDTuhMTvWBY+/2bc4wvb3nhHOyv2fgCxOn5u9hGnzJ/4xENzxj3RLGLsWYouARAht5GNFU8KjPdBwEk2sTLcIxO2tGvTfcDbqnqPlLRVCDqO82Tx3C1ANHo9iE+VCNgu8155Yh6XXhqIHn3UJISggrVe7M2Svp7a7c6+eT36zW3T4874nT0S3r1bo9UGmQ2cjWj8KT9KK6AzImciUhfVLyIS6zC47ZLXKipYD/kxbqo0HTUj51oRuTT+jc6+64wVlWwwofEmlqMZAKO+yD1D4R7FKTXdf/QkgoKemNBvYrX0JgBr3AhQy8lIe9/nyHQRTi2RLIrlJW9H5K/GyC4AFS01KDLy89z2qrykcHPtiJbbBDyv58Dmmu5f1KZHv3/gyNMCq401l1uhjuL1ADBIx7zuA7rHLwozAcSaXhX7e+hTLkCsYEDyku9b9RjQWUTOAbAqZwJ07Bhy1dqHkjIqdiyAGre9iIiCLSqq8ztCoeIpJf99+akv1Orfku8FrVc2b03UINb1Z8aFxEE0fm6hqidWx6vKlrjlcqIWZlySTOuom/jB6FdrrP+7Nt1vv6FNgxOnJGs5g71nwatjpibl2/TsHzLGPKPIqSh3ABT6osU1iBHq922xcCdG/pvIvzAm9goh2iawcXO9Qe2WnlWyeZRk0PyjM+JlpcVHwakrJTvYRwyb3rxp2XQJf+M1iJUMAC9g6yYK61clxbyc438tSPG1M2K7A7iGLxN6WojggqrFjo2J7RBMD9QddPr3N99z3pofVDTRB9SOSR1PLSEgfl5K6lWN15yl8LvrUBWQa6y1r0VjkVPzJ4z+97xXntg6d9zT4xVdC6DGaxc32/4jbo60b9X9jtYV+nyIU64PIqiHktui5+CGGz3d4ao3TJPDjaJN8nr3Tt9asK2/EdMawMLmOtSdDBBD6zkAqhIIbvw18Nek3lN79s8Sj+L15TGf833ytUq8iaWJGsRfYLZQ3F1P4NFz9qtPTmzds98bqNwAIEZPBt4BsKJ1DGBFftVYouvBTY/r1LC1+vj8CU+NSqpq07N/COXhYtXiDAPIXLZ2l83OTRjoNQCwMe8d4zitEAk6Kk9GjPQeeNnmUqNhg+YfnXF0OP1GxfSWCG1GzKr9YEHb74fVmpW7HSFTiA9fx0eqJNdXK/LF6PycTne1XVbqwB9RPASMY2oB+CPuBuuzGKg//MvjexEr+tr43C6idnc/C0CkxYj83DY7dNeXtSh5NKT0uPu0pRPKlCSelR8dAdTc9NhXTSaGjVkT2eoMBUoOb5f7Qc9+fvi2Nj36LwdyPDHPfP3ac1vK2B9DAOs0AZiTXXdcm+VbBwCnODift+7W/28YPQPku7njnig3NH4oUsEols4WMZcEbPTJRkYcRYsn/omIaCRtpDj25uIxe09enD4xVAQgLi+hdrCISQP3jTbd+40QMVMUPQHVMzASH1my8sWCCaN3P31XVUTQRJMtbNesDrC7uyDYv8x+9amJADYmix0nkQxaFAtZ+zrGnGWgbvypPGtU7fioE3hq4YSRPwK06969XpR6IVRvL04GI5J7agVatHAKC8KJPOM1XMH7TYfUunT1aRjpJNDMZ2XqqFnNvsayFiEsxnga1gvigZCw19I+JNhRs5gJXJT4eGNsl3uar5Y3AczZVvWTYfk5V97bdtln5a6AZ9MBBrRfPGt0fu5/FC52HH0JJ1BaDrXJO76J0Tl0xtr7Rs7KXSrQzMK3g9t+P3FQ+QuMb8Wyv9vs3I3AUSr+D/0VjS+JHDd6EsG3nw1FdzTd/LTFfDJ33BOvW3SpQXJ81mtJKDSz9ZKtncRHJ1VygCYAntW5AIRC1nTtN9AzfCAiQRG6gYDaPQ4AHSpU0Adx/pF40UXQ6wBUyVfVxKiduU2QQPxznTFn03cPJtPOe+WJFYr2SXaKEWmi6M1AB0gEh+q3Rf5IqZ1KNNFJN4km1sLJk3cqWvxcYactGLFb2iue6yVocYA4weBE0N1DuKIFxsgxPoquzOs6oN+pPfu/EpO6y0V2B4eqvn18wZo/JN9vKNxiSfSHrLhHAIRC02P+TVu6qOqMEqV0CkY6IXKVqv4aIbOEzhkSsQMBPM8rvgmoxy33nffdik3bCq4B/R7kSMfKlFGfNCluPqkQr4BNfPGWCFrkbe+uyrxS5WV1PSK/d6JOeyAKYF1J7JbvDUd1NV50UGWnb93VhUIi9rK4HUm72WE974+KfTfxgTegM0Vbmmy5RDG3onoHgFhdDWCNadVq+bbrxeXfKP0Eriy+FuFI8eyC/IlPTvGsOVfhHat8JVb+mGnqVrlTzaFEuRpkzrjHnz+1253NjTEDAFD519wN313fqkHzEQ56Z1LOotOlcHsX3nuv1AjO3HFPj2/Z7dY5Lv5HwFyyuyOvP4GM82KRYd+OL1M1G7sOTFNUitd3iJrPEL1CkaklhwiN46xKvlZo0aJzyL9wcigy+/nh2/K69T1fxZ2ESBNBjgeON+r0VEcxJX4qFjYb7KC54596eW4JO0Jt1xaMnHX814K2VKOLk5/fednm7aqbzxo9O/d6tfpHSh7LQPxOjsh31vNG391u+UvJH+aGYMHwBoXp4vjcmYNOW/IvgKEXrtn02Kc5l2tAZohIXYK+s4FvKK2wuKq4/4z1P8H61qPmZDfVItNGYtFFuz7KWRwKTY8BjPzy2LbGBNoUSMEkgMHtlv8/4P+Vva5lGXTWslmqnDBketYRTm2vnhuQzfe1XLVlxKzcb+I1oWwTQTt2rPP+zmO3XLxFfPMBrHF+cFAQaaker6tRFRFJlMMKg+kze/Kz35fMa8HExz+l3HOiw4MKHxTOm/DUXSd2u+XpgJN5dN2VmflMfy9Wp+PpA3c02TIZ5XSs9595rz5T6YGZiWco1+b17p1OmGOiO2y47uZjf6xsw+mYuANc1bu8WKx4irrLpp5FXp2OxmVqSdloWJb4A8lfu/gC6ZtPAWYDzJ4wZmb7zje3jgRrPaNwMSKlBgJU+V7RKVK4/eE5k1/eQAUYvL+D5LpRp9TmE/Ef/feTJ03i70vrNz/OHyhspMY0iqqzccv6bbNHXL1xB8A9JdKMavXTLuBByjCww7LFw6c1bqUZwTOL/n3c30mcVSrKTwg5RgiUTTOozfKVwMr4u+J7BIPP+GEB1ZhSX96nFVtJTE0fNP/oDInoiYn5YJsAEtdsSjKN63mr1TGIyq8WTHz807Y9+5+vnuZ5jt2+zgu89tOEUdWezX0oc1BOmNof2nfuHAwHG32HSBMAK17vea88XeEd87Retx3nxQKnxkxUnIJdM+ZWEhRlCb2clRbqtXv/4APFYzNzPlFjOqC6cFC7pVVudfRzMPqLrHOtz50OgNoxg9otu72szCndB1ztE31LVTW3cK1v8uTJ3oG280BySMzFqg4zJk8ubP2bO7rjdz4SMI66KyuTnfXys6uBah+8czCCAwDVt4EOVvSAN0fG5uf5ttmto5KdUhNzy81SBnA9jeDGB2xWBoNHApsOnJUHnp/lnPSfm7mvP/2xUe2GMGT2uMf3ax3IocTAM5aPcsTX6O7Tlh3wk3p3smWIEWkLoGoX3XXm4nLTZwBwpHgGQKyw1sGdfX0AOOxqkCSzxz9Z4RPsw50Bbb9dd6DzHDojt6ONz1ZAUWuI9a1UOBqL4o+Ps4f9vlSApPjfJaSY2rNz+1urfyp+nmLtQwNPXzW1sjTRNIn6E/MjHGKZlcn9r5AKkF8go+ZkN1VPfm3ypZtCm8QoLVZ5c3C75X8eWEXaaKxgnT/+oB9x3MN+afCeSAXI/yhDFzSp4wv7vlKVLCMaUZUYqA/w44lTZoFG1EPvv/u0paMqe7iYZNGEF1ac2qP/ZoNNL9KChT+vFwefw7KTnmLPOAX2KJBcEVxF0hEyEQkmp9zHUQWdFpFYh3tOWzpyT8GRTGS98Jmeeq0O1zUe1eGwew6SYu8ZMev464y1l6iRUyQ+b60IYY1VWaOefuaGCyYOPLfi079SxNnnAAk9MuY2LFfjdwaE/nDrN3uWf/YuVK/B0QdCD9z+yT7nGxozBqEpPndQ6A99iqeUjxw5PsPzCt177+2zrTKZkjZkBDJnJ+X31ZbdeqtXFiUpaff+2pGi5tn3JpblFEUvlqi3d7tWqDZX1Q7i7fc+SWmqerlEvVLHHOwq2PFkUTi2KfTIs70rkylpQxn5Shky5JnBoSFj3h4y5LnrKhWqbllUbvf+2VEDHKh8DhfKBUho0qQqdyUMjRjT8M9/Hlc8x0nERKqSL49uHD78xdpjx471AUyaNMmpSCqkWmXwisjW0LCxTYrTG3kD1XdFrVOpTAkbKpKv0FokQ1WvVLHH7MmzqsqiUn9K2FFVueyNHaNHTwomr19Sz96UdXXzqS57upaHMqWaWKGhY3IJ68cIm0DeCqaljfQ81x+J7nwO1WxEjlfVI0RkGsoiRW91jHO2NVbw9G84cm3ogb5fhIY88wzKTYgsReQvoYdueyE0ZMzzqvo7I3K+Qh9UL8WYIag+gLKIAN1oVn+VfLuhh8LNKC0wMjD00G3Pl7IxNOZFRX8rIu+p6qWC/Nfvr9UxEt11D+g5Avep0rVimZ1Dd9sglyblH36477QKbQ6NGQp6o0ITQeYRkOtC9922bMiQZ3soOhjV+ZyOuCAAAAXaSURBVK7r3B2L2QcqKgtOqJ+/R3+GPDui2O7KyiXMzXtlh2eHA10Q7sLqoyLmAUU7VKQzdF/f7yvw4/aK8qn4ej7zH5D1oYf7dg2FxrwLfBMK9R0cGvLMMOBUghk9pbDgyqp8PxwoFdkS4SaFRqisVtUHC8Phq++7r/tmVOuBrAcSK+l2r9WwovWI6esodRxly5Ahz3ZWpS/Ct4DB2udCfxpTcm00wHaFTKzeBzpd0TMkwg18u2GEVX0e1doYHarWjg098uxdldh+iQjDgdxIZFd/VEVVT1fR+pXKlKSEfGU2i+iHSHy9BUKQKOeHhj6bo2pfSHx2XSxmiw+8KVsWe+VPabsrLJdq2FGgqj4s9wO11NG5lemsKH1F+VRxPa2q3jR06LN1QC8GvWvs2LE+VHqgXERhQaga1/KQpWzVtx1ARF6JLy2ngYhoKHT7BSKMAo4VWJyRnrn7LEBrn1A4VsQ89OCDfb9V1fg6cUfuFnhOwcWjkrP/dCOuPAKg6NGZtevfZ0S6ipgQyi1x/VrZkWh/Ji3wTHwdu+5enyHs3KNMSYSdldn88MO3TxVkQlxMHgw9dNsLEtHzFFxBkjPbd58BUqYsquVPKbtLl0t17VC0ISJDQw/0/aIynRWlryifSq+nyhKAcFSvVHAVzLr1sfMUbQjyRWbt+gOqcS0PWUoFiOPwNxH5VtVeg8M5rpG/AoQef/lIhXGoRvH5fjN4cPfiOf+qZIuI5zj2DQAksQw2PfANyYNTlKyKc5cXjafJyW+1treuH1W40qqdBFL1sQlGpktRJL5uWnTpPstU02bVeLAr+hyKg8iMEt+VKotq+VPa7lLlUl07BFlwTEO39K7yZXRWlb4UlZSNiCxOvO4iyZaFx80AIvr2Pvt+iFEqQGLpgV1+X/BchAAx3rRWjgJge8FfVPU4EflD6P4+c0qmEWSBqjoxTybEO4GJ5lc4cqQK8d07koVZLnOz+6IoAT755gFVvUGQURXJl0VVzkgYMW9/ZKpjMxBff4/2MsY9R9DicxnLlUU1/UlSrlyqa4eYR/r06VNq360KdJZLr6oVDPtXXDbq2Pnxr/UiRD4QkR2IXg2gPt87++r7oUbpJtb28JBwpGA9sFjRBla9XkOGjLlZVW8QYYMKTig05umRI8fv3vTN1TsE3lbVDt98u/FpVOIrDWO0ROP7a5nEir+KsI6TvCgG5QJB5gENgYqbRLtT+sF2FWR9RjDzXaSiZzplZEpSUr4Km7XMQIaIJs6BN35rvZDC45AYpSlXFnvhT4V2lymXvbcjMXVIK1zPUlJnRemHTJ7sK5tPpWWTeeQ8EVEFPyJfoCxUJSAi60J/6PNd9a7loUvpAKnlDpH4es54h1ZYrMq1AKrUV6vDFb29oGDH+cUXQY3f5wv0FpHVYBs6jvO8wGY8HYtyk4gssc2P+np3XppJor2sSi2J2dMTuacbo4NAcxTtiugjIoSTa9qLSW52Zhmv0AQjwxNNvoYJe5pUIVPShmL5KmyG4iaOZgLoUTJVhBmq9m3QjoLpBRRUVBZ75U9Juyspl2rYkbyeR5XQX6HOitKHunSJlM2nsrIJ3dl1O2h8x0yRBYjGb4Ia/7+Xvh/ylAqQ0KA+GzMzGzTHmKsc47QJPdT3WTJMLzFyh2Ocs30uWVL3yCMefrjvOxkZmQ8Yca4NuMy+//7f/US6aXtMQ98NDz5465JgMJglojdizB2uoxeFunSJUDtwtxjTR9MCs8lMv14cc5GeVG/aSSfVf0Vc9zT8vnseeuj2mcZwpmOcs0MP3f7HzNoN6pDmL7UDhmMCA43IICNcbkRuzAjWGguQWbv+78Thclz3o8pkStpQUr5Sm4GHH7ptsLhyls8XfBsg1Lfvzocf6nsWPt/puE6Hhx/+/QeVlcXe+FPSjsrKZW/t4KQGvcVxztO0wJTdGVSss8L0FeRTVdn4fentjUjXjLSMmRwl9xpxrsWRpwH2xvcUKVKkSJEiRYoUv0j+P1GPgO+VXwPOAAAAAElFTkSuQmCC" /></a>
+<p class="haj" style="font-weight: bold">{% trans "Server error" %}</p>
+{% trans "<p>The Wolnelektury.pl site is currently unavailable. Meanwhile, visit our <a href='http://nowoczesnapolska.org.pl'>blog</a>.</p> <p>Inform our <a href='mailto:fundacja@nowoczesnapolska.org.pl'>administrators</a> about the error.</p>" %}
 
 <script src="http://www.google-analytics.com/urchin.js" type="text/javascript">
 </script>
diff --git a/wolnelektury/templates/503.html b/wolnelektury/templates/503.html
new file mode 100644 (file)
index 0000000..7d444a2
--- /dev/null
@@ -0,0 +1,66 @@
+{% load i18n %}
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="pl" xml:lang="pl">
+<head>
+<title>503 - {% trans "Service unavailable" %} WolneLektury.pl</title>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+<link rel="stylesheet" type="text/css" href="http://yui.yahooapis.com/2.5.2/build/reset/reset-min.css"> 
+<style type="text/css">
+
+body {
+font-family: verdana, arial, sans-serif; 
+margin: 2em;
+font-size: 100%;
+}
+
+p {
+width: 450px;
+margin-top: 0.5em;
+}
+
+img {
+    border: none;
+}
+
+.haj {
+    background-color: #ffc;
+    font-weight: bold;
+}
+
+a:link {
+    color: #037;
+    text-decoration: underline; 
+}
+
+a:visited {
+    color: #636;
+}
+
+a:active {
+    color: #900;
+    text-decoration: none;
+}
+a:hover {
+    color: #d46400;
+}
+</style>
+
+</head>
+
+<body>
+
+<a href="/"><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAABKCAYAAAD+IBtNAAAACXBIWXMAAAMTAAADEwE9ZoPHAAAgAElEQVR4nO2dd5iU1fX4P+e+78zs7MIqIIigsLusikFBWERRUazYNSr4jVKjASMqIGCJbdAkVLGiwZ+NYgnExGiMGkSwIrJUgwpIF1B63d0p7z2/P2Zm2Q4LKyXO53n22SnnnnvOfee8t7y3CDXAY5/mnOClmc+MZal15DOJ6We73ILPQ23XbqwJ/XuLKrJzWq1f+aweF7hg1wci6IHMP8X/HlITSobl5zRx1axU5QfQo0QkLfHVd9YyTZQPNvy09aMRV2/cURP5JVFFdn3oP9lVOd8xpiPQAaEegEvhsXIBa2oyvxS/PGokQABGzmq2wqj8Y0dG4J7MbdFTY0bbi6Nng14gSB0gqsoMa3nf8XuvDWqzfCXAqFm53WJil9/bdtlne5OPKhKb4p4jrr+bWr0SoQGqEYWZKuZjUXuuiJzsnl9YL1WDpNhf3JpSpPCxwrmhFgsjwFeJvydDismccUJb6/cuRuUix2GIRk3/0MtZTUO9VhSB9nOsWQtUGSDbPwycEDBOj8hU29U40sSqXYSVN4xrP3AKwtPlSgoAIlMDX6PyWSo4UtQENRcgnpmuju06dEGTOve1XLUl+XlIsLA4GTB/HDH7hA4G+0ntk92LgbctLBToUJne8BROEkkbKyIdsLpZRF61nnnJf/GueeVsmEhmTOVXGMbXlF8pftmYmlJkC51pghh/xH9hVXIFbRZ/rqo/euh1AGrtLBHNejz/pGMqTOCkPYxQX6x3vbu58Bj3gsI7KwoOgHDDtHaImKgX+3S/HUqRghoMkPvO+26FKsvV04urkgsJVlX+IXDl2Pw8X6ETfkORaNSLdSsrqxPJNMpVWEa4F0XelC5EqtIt1pyDakGwTnT2/vqTIgXUYIAktL2H0GlPYmLM3wWps0W2nR9qu3ajKu8YY7uWlYs2Srsexfq2FE3em+zF6CUY/UTaEt0X81OkKEuNBoiV2AREjhv9xQntqpJrunzxNLDrfJbOAMboOyCnDMvPaVJSTixdrZo3pQs795S3fkg9QfM85YP98yJFit3UaIDck7fiSxVdYl29uSq5Ll3wgAlWuSYU6ujuNJEpAG6MK5Iy+kHwOISO1rOv7E3eERu8GBEjsfD7++NDihQlqdkmFiAwHvT/QvmN0quSi4h9WYR6mZetuDDUZvVaVBdax7k8+X3YZ6+3sCqtU9H0vcrYlU4WXRnoxHf750GKFLup8QCJoeMRrZ0RC1xbldwf2q74DsuXFuf3AIJMEbXnj83P8wEYlevxzPg9Pc/QSfiLpmR0MlYvdaymmlcpapQaD5B72y5bpapTxXUGDc9vep5q5U/rrcOLKlwxdFrzLM9Gp4hI2k7deLL+M72RKGdorGBcRen0ZdJiU/zXhaemvRqrG1zvGPs+4BShz9S0Pyl+2dR4gADYSPQ+taijvo8em52zbPjM3NCjM0/MLitXEAyMF9FVbnr4tvXBoo9R9Tzx5YVredcqfJ52GUtLykc+Ss+LTQmOiTUJrlXj/A2hgwjjY55e6H5W2LDWRZGvfw5/UvxyqbG5WBXxp1nNTg6g3VTNjSLaGJhurX2lwAn/LdR2bQHAiJnZ3R1jnosaudbx9C0xZkC/rWuuVWPf8J9f9JI+RSDcPP0WY7S3CC2BHxQmx2xsUvDC6MzUlJIUPyc/a4AkCSkmMKdZR8dqF1G5GiRDlMkxx76cs2zZjFVNc9/CcAWqhSYWbnnzpg2SuTG8Otw4vYfj6AOgR1vh7yYae9a9OPZpMih0Ko1j1t8LkTPFSHM8/cC9KPz7A+FTil8GByRAShJSjP/LZu0dV6911FzjmegthXkrPw7Mzr6QHYHFiSfyEpmW9rqxXGPVDPMHCsbKOawDCH9Ac3UDFzrWXILoJQhWkQXGyvyota8GOxV9dKB9SvG/ywEPEIBpWVlpzWOxlkE1tQs0Nr/R2tILq/RdmsbSgiuwDN4Z079lOF4HFd+FRvQChMaoWoWZ1upfA2H/q3LljgO6MCvFL4cDEiAKZn3j3NN9fr0cy6UoLTGJmcQqmwuNd2bj5csXFcuHMNGz074VkRNKKNlkRaeK5/3LF057LxUUKQ4EP1uAKJjNTZpcIPi6IVyWWOkXQfnCWpkS1aIP/a5rRM0XotKrzqqlpYZ0dTwZXuPAOQjiRM3X0qlw9c9la4oUlVGjAbIlO7upetoHIyermjwRGmH5XsR7Rz1nSuSHnZ805Kddmpfn27Jp0/Wo8yiAGys6PXPNmk15vXunazjtJN/6TUt21orYQFqjfIx8LMibtVce8dH06aFYTdqbIsWeqNEA2dgku6uIjBXkO1HmYL2JdX5Y8XHy+zWNGh3ndwK/cx3prUgdPJ6P+Hk0LeZmXJP9m9XbmmydIUJbsN/FdhRc5Dsi0MB6/l6C/Y0VEdT+Uzwz+Yg1R05JBUuKA8HP3gdZddRxjWqn+6+3jnYRy5kI21HzWlhiI45ZsWLF6nonNE6vFVvy23bt265Or79Q4DaFQWr5fO6EJ7oDtOgc8gfStl6hoj0EuUxhsxheR+2EOeOeSq39SPGz8bMEyJbs7KZW5VqxXItwFqAiZqoSe3k+/OO8FSuKkrKbs3LuVZVzLz73ij4qsjJG7Gq/9cc8Y9/xbFGrBRP+8t+Supt1G9SgthO7UTx6iqGVqn5rxYTmj3t80s/hS4pfNjUaIGsbNUoPumn/VCMXoqqKzLTGvhkJh//aeO3aCjvZW7JyFlnVEfVWLn+xdfcBm8Xo+DmvPDGgTfd+S0Bmzxn/xA2V5df6t3e1Ek/vV7WXRgrXHrNw8uQ9rhtJkaI61OhcLNfxX6lGLgS9T2PhJvVWLm1ff/nyUZUFx09Nm56pVnJ8schbAKI6V1UuAlSU5xSuad/55rqV5Tf3pdHzd4Wj/RFJD6Qf07kmfUmRAmo4QIxxO6mypu6KZcPqrVnzw57kXXV7Wkc/zlyzZhOAODJF0F+d0PnWxhEv8hKiGg7UurEqHYveeGYt2P+oSs8aciNFimJqNEAE7WTiU8/3yGoIYrjBIMV9h6hX+C+A9Iy0i75+7bktKB/gmJ57VBbTF4EOp/bsn7VvlqdIUTE1FiCbc3JOEaGR9dirAAk2yb5OIGC86OQWN/U7rWXX35+4YMJf/quWlXj2ooRxfwXNa9nt1pOr0hWJ1HtbkE0OttzOKClS7A81V4OoXIKqt93oh3sl70hPq/rukatWbfE7Ms2YwHgAMfzLiLk4r3dvX1Hh2rfBFgn+m6pStXByKCIi/7Qq+xwgj+efdMyw/Jyz9zV9itLk9ejd7MT/u73RwbZjf6mxAFHlAkVmZq9YsXVPsluys5say/mInQCAEDTCMQAxMRMRjortSrti4eTJO1HeM2Ku35POGPxHkOPzevRuti/2ezYyxVXz6bDZuXvctijFnrGS/mJGwPf2wbZjf6mRANG4nvai3sd7kl3fqGlrPEaB2VIvI+PfAKJYVa0DsOCV0V8q8o3jSncAa/VNEXJb//auVlXpTS/Y/qGCVdLO2ScnhACAG9N9S18DjMrPHjhq1vFPHaz8axTVpoq2btE55D/YpuwPNRIgG7KyWgpkiuN+WdH32xs3rrelSVa/zVk5C1y/O8caczoSu10WLowAqKgnIrXyevf2ASD2U1HaAjjp6f9SJWKiWmUtMmPyi5tVybdizt0XHyxmMYA45qR9SV+SQfOPzhg+o2m19SimD+gdZfcHOxwRxRMwZOyqdJj+cKBGNq826p6FKGGjxQHy07HHtvT5Ap3E04tjRs5R1ZioeUs1MrDuqlVTBWxSVlQsAnZL9Ehgg8T4rzrS56yr7q79+fPDt7Xpfue7VuT+1t37X6JiP3BU3q+9qs6X5eZjCdPFo9IHi1XhqC5W4TJPtfm+lkOS+uGMa4xjHgVyqpVQid9tram9vzYcdESjIAQKo3WAHw+2OftKjdQggj0bZZPf0+s2ZTV7Y0tWzo8+NzAf5U+eYCz02bBre8O6K7+/qd6qVVNKBgeAoh6ADabXAYgV7HrLs3bY523SdwGEC9d1t0b7qdr1BhmgIp9ub7p1Y+tu/d5s1fPOO9reeEe7Fp1Dfteaj8XQ9MRut5TbIGKPGLsYwEBuKNRxv24cjshmEbJHfnlsy+qkE9QPIJFwcH/yPxRQlQhANC1a/2Dbsj/UzPEHyskY6oE8iTJb1bysjjctHA5/1mhtfHOGqkgGjKvE+yF/f+EH4L7k94kpJE8DT3PppYGWdXPOdsXfSYy90LFytfUZx+/bEvaURYKQTsb5wIvV8sHGFiN+AJ9z+cpmhFi0pySVEVZvXUBcjBu8Gliwt+lUjV8EJBg87AME2ArgF/fog23I/lAzAWLs76LG1NJlu2Y05Kdd1U2uGE9Q1HHq7FH4vffCC2Aq8T/Ouuru2kV1ou09j7MdY89G2W6MVLtjGNvlLXJqxV+neU5z2PcAwRasw8lErb0EeLQaKf0AnkYP/wARswEUz9MGB9uU/aFGAqTeihUVds73FsFaEFTNngOkDJ+/PWIH8J/E3z5zd8c1a0bnNytQJF1dexLwz33VFT19/YbArNqeIidWJ51IPEDcmHPYB4iqXS8iqJpUDbK/qGAFMHDkwbJBBB2VL0tQWkm8BimFKvL4jKxzrOu7UcQerWiBtfr+3acvL3eaVUiwI2fJTwKNhuXnHHFv22Xb9pT/47NOyPHw/CCo0Vsfy292uWclCAQF/CL63KDTlr5XQ+7+7GiiBnEcUgGyvyjxAPE8qXYNUsOGLAZaYUqPZIVezkp77CvnLXzSCZT4bqqCMfxm5Fe51w9u9/1VZVUJug6kkcE0A+YkPx/9fou6bvom585zftwAMPqLrHM913nGw56cXH1gRC5RBVNiMYKqRoHDJkDEeusxBpFUgOw3BlUQjBvLqAl9p/W67ThP/QNR2ojqD57qisDGTcNmvvfq9qrSechiBwV2B8joSQRtlu8dRC8AQNmsqu+KMdmgZ4voFaFpWUeGziszg8DKOgzg2RNJBMjj+ScdE7NFczwyjnx0VvapD562fJH1uf8SqKXKDhESw7s6DdHizr1AgYP7Qk2UzYHCcWS9Kqhy3MG2ZX/YrwBp1b3vCY74X7Nqv4oW1um/cHKo9BFpoZA5eeXmM/zW9LCqHsYuj+768blyC5vUKAJC8fnqnNzrzjOP2JT2daKPsde06d5/pGe1H+ADUBGMCNH6R3XI69270+znn690VM3BrgJBkCOSh5Fqk+y+yeBQlTcCdZb0vPN4wqH8RukZNn2biLjpmf5TgDLnIuqPIDgOpwCvA3he5BEx0hDANaYxsCgisQv94sSaLl06b1V2bvy5jth3B7Vd/lh1/D5YdO7c2VkWbNR4q/qKlk4YtT75uSAb4nvCanPiVeNhuUVsuQBp06PfP2I2/GDZpa4V4Yg7HDTPiOQF/RtfBT4HyOvZrzPITXbZtnNE4h1vIwLq4A82HpTXre9VsyeMmZnUo6IqCIk2N0d3G5Th96Kf7MosehQYAvGltkeYyOV46pf09DdmPz+8XLu+Vc8770AZVLxQUu00VScgRs9E5GwtTB8A/KlSh2K6ETeeNhZ2GgBbcMxvACx8W/Dvxt0Gh5bExubn+bbGNt8rDi7YdWmZ339VVpVV+6PBQT1zCsCwr3JbgPYCUFhaV4/8FOAPbVcUl8OoWaog4nnOHh8UPjYr57cW85TFjr3ntGUDIV7bAdzVhcJinbOb5eFpL3CaW/GWuJuC98eOCmc5ym1RsQP3pn9UGa16DOiyTBmmotmZxCKndu8/3rEFD82e+Py6GKyIP2ST9BO73ZK1aMILy/c1n4NJuQeFqnKmj0C5CXt53fqentf7niOS79t37hxEpViuwPVtjcsNeFRVJqlyNcI6z+NRYFlSToQGVnwTj+42aHdzSiVxd9E0gKN3ba2DiCNGOgGS13PAsEwTWwfmJRznL1oUXtfqxttL2diy511nOCrJu+4yK7SeM/6p8/0b11+Kxn8wNr6BdqVYv7sh+TpgfA2GTmuepUgbACNEal3+w2ujvmr2+XZv6w+O4zwIRFWcQXceT7isLsdxfgRQo6cAOOhIRBwAE4vd36ft7HLnKKqKByCqteLvkcdm5Q4b+VX278rKephBAhkG6T966gmNAbym2UNsdu764dMaH/vnGc3OGvlV7lys5COmL6IXGMyt9qiCXuJ5/6dws0/LT8sZld+sx2Ozcl8IzW9W4fBsm8635QK06drvdqP2DRXNhvgInBFu8Zzga4DMe+WJFaqsBwg6wSpPPj6UKRcgAsYabVvyo9bd+32sxvclRUXPJT+MZDTuhMTvWBY+/2bc4wvb3nhHOyv2fgCxOn5u9hGnzJ/4xENzxj3RLGLsWYouARAht5GNFU8KjPdBwEk2sTLcIxO2tGvTfcDbqnqPlLRVCDqO82Tx3C1ANHo9iE+VCNgu8155Yh6XXhqIHn3UJISggrVe7M2Svp7a7c6+eT36zW3T4874nT0S3r1bo9UGmQ2cjWj8KT9KK6AzImciUhfVLyIS6zC47ZLXKipYD/kxbqo0HTUj51oRuTT+jc6+64wVlWwwofEmlqMZAKO+yD1D4R7FKTXdf/QkgoKemNBvYrX0JgBr3AhQy8lIe9/nyHQRTi2RLIrlJW9H5K/GyC4AFS01KDLy89z2qrykcHPtiJbbBDyv58Dmmu5f1KZHv3/gyNMCq401l1uhjuL1ADBIx7zuA7rHLwozAcSaXhX7e+hTLkCsYEDyku9b9RjQWUTOAbAqZwJ07Bhy1dqHkjIqdiyAGre9iIiCLSqq8ztCoeIpJf99+akv1Orfku8FrVc2b03UINb1Z8aFxEE0fm6hqidWx6vKlrjlcqIWZlySTOuom/jB6FdrrP+7Nt1vv6FNgxOnJGs5g71nwatjpibl2/TsHzLGPKPIqSh3ABT6osU1iBHq922xcCdG/pvIvzAm9goh2iawcXO9Qe2WnlWyeZRk0PyjM+JlpcVHwakrJTvYRwyb3rxp2XQJf+M1iJUMAC9g6yYK61clxbyc438tSPG1M2K7A7iGLxN6WojggqrFjo2J7RBMD9QddPr3N99z3pofVDTRB9SOSR1PLSEgfl5K6lWN15yl8LvrUBWQa6y1r0VjkVPzJ4z+97xXntg6d9zT4xVdC6DGaxc32/4jbo60b9X9jtYV+nyIU64PIqiHktui5+CGGz3d4ao3TJPDjaJN8nr3Tt9asK2/EdMawMLmOtSdDBBD6zkAqhIIbvw18Nek3lN79s8Sj+L15TGf833ytUq8iaWJGsRfYLZQ3F1P4NFz9qtPTmzds98bqNwAIEZPBt4BsKJ1DGBFftVYouvBTY/r1LC1+vj8CU+NSqpq07N/COXhYtXiDAPIXLZ2l83OTRjoNQCwMe8d4zitEAk6Kk9GjPQeeNnmUqNhg+YfnXF0OP1GxfSWCG1GzKr9YEHb74fVmpW7HSFTiA9fx0eqJNdXK/LF6PycTne1XVbqwB9RPASMY2oB+CPuBuuzGKg//MvjexEr+tr43C6idnc/C0CkxYj83DY7dNeXtSh5NKT0uPu0pRPKlCSelR8dAdTc9NhXTSaGjVkT2eoMBUoOb5f7Qc9+fvi2Nj36LwdyPDHPfP3ac1vK2B9DAOs0AZiTXXdcm+VbBwCnODift+7W/28YPQPku7njnig3NH4oUsEols4WMZcEbPTJRkYcRYsn/omIaCRtpDj25uIxe09enD4xVAQgLi+hdrCISQP3jTbd+40QMVMUPQHVMzASH1my8sWCCaN3P31XVUTQRJMtbNesDrC7uyDYv8x+9amJADYmix0nkQxaFAtZ+zrGnGWgbvypPGtU7fioE3hq4YSRPwK06969XpR6IVRvL04GI5J7agVatHAKC8KJPOM1XMH7TYfUunT1aRjpJNDMZ2XqqFnNvsayFiEsxnga1gvigZCw19I+JNhRs5gJXJT4eGNsl3uar5Y3AczZVvWTYfk5V97bdtln5a6AZ9MBBrRfPGt0fu5/FC52HH0JJ1BaDrXJO76J0Tl0xtr7Rs7KXSrQzMK3g9t+P3FQ+QuMb8Wyv9vs3I3AUSr+D/0VjS+JHDd6EsG3nw1FdzTd/LTFfDJ33BOvW3SpQXJ81mtJKDSz9ZKtncRHJ1VygCYAntW5AIRC1nTtN9AzfCAiQRG6gYDaPQ4AHSpU0Adx/pF40UXQ6wBUyVfVxKiduU2QQPxznTFn03cPJtPOe+WJFYr2SXaKEWmi6M1AB0gEh+q3Rf5IqZ1KNNFJN4km1sLJk3cqWvxcYactGLFb2iue6yVocYA4weBE0N1DuKIFxsgxPoquzOs6oN+pPfu/EpO6y0V2B4eqvn18wZo/JN9vKNxiSfSHrLhHAIRC02P+TVu6qOqMEqV0CkY6IXKVqv4aIbOEzhkSsQMBPM8rvgmoxy33nffdik3bCq4B/R7kSMfKlFGfNCluPqkQr4BNfPGWCFrkbe+uyrxS5WV1PSK/d6JOeyAKYF1J7JbvDUd1NV50UGWnb93VhUIi9rK4HUm72WE974+KfTfxgTegM0Vbmmy5RDG3onoHgFhdDWCNadVq+bbrxeXfKP0Eriy+FuFI8eyC/IlPTvGsOVfhHat8JVb+mGnqVrlTzaFEuRpkzrjHnz+1253NjTEDAFD519wN313fqkHzEQ56Z1LOotOlcHsX3nuv1AjO3HFPj2/Z7dY5Lv5HwFyyuyOvP4GM82KRYd+OL1M1G7sOTFNUitd3iJrPEL1CkaklhwiN46xKvlZo0aJzyL9wcigy+/nh2/K69T1fxZ2ESBNBjgeON+r0VEcxJX4qFjYb7KC54596eW4JO0Jt1xaMnHX814K2VKOLk5/fednm7aqbzxo9O/d6tfpHSh7LQPxOjsh31vNG391u+UvJH+aGYMHwBoXp4vjcmYNOW/IvgKEXrtn02Kc5l2tAZohIXYK+s4FvKK2wuKq4/4z1P8H61qPmZDfVItNGYtFFuz7KWRwKTY8BjPzy2LbGBNoUSMEkgMHtlv8/4P+Vva5lGXTWslmqnDBketYRTm2vnhuQzfe1XLVlxKzcb+I1oWwTQTt2rPP+zmO3XLxFfPMBrHF+cFAQaaker6tRFRFJlMMKg+kze/Kz35fMa8HExz+l3HOiw4MKHxTOm/DUXSd2u+XpgJN5dN2VmflMfy9Wp+PpA3c02TIZ5XSs9595rz5T6YGZiWco1+b17p1OmGOiO2y47uZjf6xsw+mYuANc1bu8WKx4irrLpp5FXp2OxmVqSdloWJb4A8lfu/gC6ZtPAWYDzJ4wZmb7zje3jgRrPaNwMSKlBgJU+V7RKVK4/eE5k1/eQAUYvL+D5LpRp9TmE/Ef/feTJ03i70vrNz/OHyhspMY0iqqzccv6bbNHXL1xB8A9JdKMavXTLuBByjCww7LFw6c1bqUZwTOL/n3c30mcVSrKTwg5RgiUTTOozfKVwMr4u+J7BIPP+GEB1ZhSX96nFVtJTE0fNP/oDInoiYn5YJsAEtdsSjKN63mr1TGIyq8WTHz807Y9+5+vnuZ5jt2+zgu89tOEUdWezX0oc1BOmNof2nfuHAwHG32HSBMAK17vea88XeEd87Retx3nxQKnxkxUnIJdM+ZWEhRlCb2clRbqtXv/4APFYzNzPlFjOqC6cFC7pVVudfRzMPqLrHOtz50OgNoxg9otu72szCndB1ztE31LVTW3cK1v8uTJ3oG280BySMzFqg4zJk8ubP2bO7rjdz4SMI66KyuTnfXys6uBah+8czCCAwDVt4EOVvSAN0fG5uf5ttmto5KdUhNzy81SBnA9jeDGB2xWBoNHApsOnJUHnp/lnPSfm7mvP/2xUe2GMGT2uMf3ax3IocTAM5aPcsTX6O7Tlh3wk3p3smWIEWkLoGoX3XXm4nLTZwBwpHgGQKyw1sGdfX0AOOxqkCSzxz9Z4RPsw50Bbb9dd6DzHDojt6ONz1ZAUWuI9a1UOBqL4o+Ps4f9vlSApPjfJaSY2rNz+1urfyp+nmLtQwNPXzW1sjTRNIn6E/MjHGKZlcn9r5AKkF8go+ZkN1VPfm3ypZtCm8QoLVZ5c3C75X8eWEXaaKxgnT/+oB9x3MN+afCeSAXI/yhDFzSp4wv7vlKVLCMaUZUYqA/w44lTZoFG1EPvv/u0paMqe7iYZNGEF1ac2qP/ZoNNL9KChT+vFwefw7KTnmLPOAX2KJBcEVxF0hEyEQkmp9zHUQWdFpFYh3tOWzpyT8GRTGS98Jmeeq0O1zUe1eGwew6SYu8ZMev464y1l6iRUyQ+b60IYY1VWaOefuaGCyYOPLfi079SxNnnAAk9MuY2LFfjdwaE/nDrN3uWf/YuVK/B0QdCD9z+yT7nGxozBqEpPndQ6A99iqeUjxw5PsPzCt177+2zrTKZkjZkBDJnJ+X31ZbdeqtXFiUpaff+2pGi5tn3JpblFEUvlqi3d7tWqDZX1Q7i7fc+SWmqerlEvVLHHOwq2PFkUTi2KfTIs70rkylpQxn5Shky5JnBoSFj3h4y5LnrKhWqbllUbvf+2VEDHKh8DhfKBUho0qQqdyUMjRjT8M9/Hlc8x0nERKqSL49uHD78xdpjx471AUyaNMmpSCqkWmXwisjW0LCxTYrTG3kD1XdFrVOpTAkbKpKv0FokQ1WvVLHH7MmzqsqiUn9K2FFVueyNHaNHTwomr19Sz96UdXXzqS57upaHMqWaWKGhY3IJ68cIm0DeCqaljfQ81x+J7nwO1WxEjlfVI0RkGsoiRW91jHO2NVbw9G84cm3ogb5fhIY88wzKTYgsReQvoYdueyE0ZMzzqvo7I3K+Qh9UL8WYIag+gLKIAN1oVn+VfLuhh8LNKC0wMjD00G3Pl7IxNOZFRX8rIu+p6qWC/Nfvr9UxEt11D+g5Avep0rVimZ1Dd9sglyblH36477QKbQ6NGQp6o0ITQeYRkOtC9922bMiQZ3soOhjV+ZyOuCAAAAXaSURBVK7r3B2L2QcqKgtOqJ+/R3+GPDui2O7KyiXMzXtlh2eHA10Q7sLqoyLmAUU7VKQzdF/f7yvw4/aK8qn4ej7zH5D1oYf7dg2FxrwLfBMK9R0cGvLMMOBUghk9pbDgyqp8PxwoFdkS4SaFRqisVtUHC8Phq++7r/tmVOuBrAcSK+l2r9WwovWI6esodRxly5Ahz3ZWpS/Ct4DB2udCfxpTcm00wHaFTKzeBzpd0TMkwg18u2GEVX0e1doYHarWjg098uxdldh+iQjDgdxIZFd/VEVVT1fR+pXKlKSEfGU2i+iHSHy9BUKQKOeHhj6bo2pfSHx2XSxmiw+8KVsWe+VPabsrLJdq2FGgqj4s9wO11NG5lemsKH1F+VRxPa2q3jR06LN1QC8GvWvs2LE+VHqgXERhQaga1/KQpWzVtx1ARF6JLy2ngYhoKHT7BSKMAo4VWJyRnrn7LEBrn1A4VsQ89OCDfb9V1fg6cUfuFnhOwcWjkrP/dCOuPAKg6NGZtevfZ0S6ipgQyi1x/VrZkWh/Ji3wTHwdu+5enyHs3KNMSYSdldn88MO3TxVkQlxMHgw9dNsLEtHzFFxBkjPbd58BUqYsquVPKbtLl0t17VC0ISJDQw/0/aIynRWlryifSq+nyhKAcFSvVHAVzLr1sfMUbQjyRWbt+gOqcS0PWUoFiOPwNxH5VtVeg8M5rpG/AoQef/lIhXGoRvH5fjN4cPfiOf+qZIuI5zj2DQAksQw2PfANyYNTlKyKc5cXjafJyW+1treuH1W40qqdBFL1sQlGpktRJL5uWnTpPstU02bVeLAr+hyKg8iMEt+VKotq+VPa7lLlUl07BFlwTEO39K7yZXRWlb4UlZSNiCxOvO4iyZaFx80AIvr2Pvt+iFEqQGLpgV1+X/BchAAx3rRWjgJge8FfVPU4EflD6P4+c0qmEWSBqjoxTybEO4GJ5lc4cqQK8d07koVZLnOz+6IoAT755gFVvUGQURXJl0VVzkgYMW9/ZKpjMxBff4/2MsY9R9DicxnLlUU1/UlSrlyqa4eYR/r06VNq360KdJZLr6oVDPtXXDbq2Pnxr/UiRD4QkR2IXg2gPt87++r7oUbpJtb28JBwpGA9sFjRBla9XkOGjLlZVW8QYYMKTig05umRI8fv3vTN1TsE3lbVDt98u/FpVOIrDWO0ROP7a5nEir+KsI6TvCgG5QJB5gENgYqbRLtT+sF2FWR9RjDzXaSiZzplZEpSUr4Km7XMQIaIJs6BN35rvZDC45AYpSlXFnvhT4V2lymXvbcjMXVIK1zPUlJnRemHTJ7sK5tPpWWTeeQ8EVEFPyJfoCxUJSAi60J/6PNd9a7loUvpAKnlDpH4es54h1ZYrMq1AKrUV6vDFb29oGDH+cUXQY3f5wv0FpHVYBs6jvO8wGY8HYtyk4gssc2P+np3XppJor2sSi2J2dMTuacbo4NAcxTtiugjIoSTa9qLSW52Zhmv0AQjwxNNvoYJe5pUIVPShmL5KmyG4iaOZgLoUTJVhBmq9m3QjoLpBRRUVBZ75U9Juyspl2rYkbyeR5XQX6HOitKHunSJlM2nsrIJ3dl1O2h8x0yRBYjGb4Ia/7+Xvh/ylAqQ0KA+GzMzGzTHmKsc47QJPdT3WTJMLzFyh2Ocs30uWVL3yCMefrjvOxkZmQ8Yca4NuMy+//7f/US6aXtMQ98NDz5465JgMJglojdizB2uoxeFunSJUDtwtxjTR9MCs8lMv14cc5GeVG/aSSfVf0Vc9zT8vnseeuj2mcZwpmOcs0MP3f7HzNoN6pDmL7UDhmMCA43IICNcbkRuzAjWGguQWbv+78Thclz3o8pkStpQUr5Sm4GHH7ptsLhyls8XfBsg1Lfvzocf6nsWPt/puE6Hhx/+/QeVlcXe+FPSjsrKZW/t4KQGvcVxztO0wJTdGVSss8L0FeRTVdn4fentjUjXjLSMmRwl9xpxrsWRpwH2xvcUKVKkSJEiRYoUv0j+P1GPgO+VXwPOAAAAAElFTkSuQmCC" /></a>
+<p class="haj">{% trans "Service unavailable" %}</p>
+<p>
+{% trans "The Wolnelektury.pl site is currently unavailable due to maintainance." %}
+</p>
+
+<script src="http://www.google-analytics.com/urchin.js" type="text/javascript">
+</script>
+<script type="text/javascript">
+    _uacct = "UA-2576694-1";
+    urchinTracker();
+</script>
+</body>
+</html>
\ No newline at end of file
index d0ca937..23cfb59 100644 (file)
@@ -1,9 +1,11 @@
 {% extends "admin/base.html" %}
+{% load i18n %}
 
-{% block title %}{{ title }} | Administracja stroną WolneLektury.pl{% endblock %}
+{% block title %}{{ title }} | {% trans "Site administration" %} - WolneLektury.pl{% endblock %}
 
 {% block branding %}
-<h1 id="site-name">Administracja stroną WolneLektury.pl</h1>
+<h1 id="site-name">{% trans "Site administration" %} - WolneLektury.pl</h1>
+<p style="float: right; font-size: 11px; padding-right: 10px;"><a href="/rosetta/">{% trans "Translations" %}</a></p>
 {% endblock %}
 
 {% block nav-global %}{% endblock %}
\ No newline at end of file
index 3ded503..09e567a 100644 (file)
@@ -1,8 +1,9 @@
 {% extends "admin/change_list.html" %}
+{% load i18n %}
 
 {% block content %}
     <form action="{% url import_book %}" method="post" enctype="multipart/form-data">
-        <p><input type="file" id="id_book_xml_file" name="book_xml_file" /> <input type="submit" value="Importuj książkę"/></p>
+        <p><input type="file" id="id_book_xml_file" name="book_xml_file" /> <input type="submit" value="{% trans "Import book" %}"/></p>
     </form>
     {{ block.super }}
 {% endblock content %}
\ No newline at end of file
index 6db94aa..2e80c55 100644 (file)
@@ -1,24 +1,25 @@
 {% extends "base.html" %}
+{% load i18n %}
 
-{% block title %}Zaloguj / Zarejestruj się w WolneLektury.pl{% endblock %}
+{% block title %}{% trans "Sign in" %} / {% trans "Register on"%} WolneLektury.pl{% endblock %}
 
 {% block body %}
-    <h1>Zaloguj się / Załóż konto</h1>
+    <h1>{% trans "Sign in" %} / {% trans "Register"%}</h1>
     <form action="." method="get" accept-charset="utf-8" id="search-form">
-        <p><li>{{ search_form.q }} <input type="submit" value="Szukaj" /></li> <strong>lub</strong> <a href="{% url main_page %}">wróć do strony głównej</a></p>
+        <p><li>{{ search_form.q }} <input type="submit" value="{% trans "Search" %}" /></li> <strong>{% trans "or" %}</strong> <a href="{% url main_page %}">{% trans "return to main page" %}</a></p>
     </form>
     <form method="post" action="." id="login-form" class="cuteform">
-        <h2>Zaloguj się</h2>
+        <h2>{% trans "Sign in" %}</h2>
         <ol>
             {{ form.as_ul }}
-            <li><input type="submit" value="Zaloguj się" /></li>
+            <li><input type="submit" value="{% trans "Sign in" %}" /></li>
         </ol>
         <p><input type="hidden" name="next" value="{{ next }}" /></p>
     </form>
     
     <form action="." method="post" accept-charset="utf-8" id="registration-form">
-        <h2>Załóż konto</h2>
+        <h2>{% trans "Register" %}</h2>
 
-        <p><input type="submit" value="Załóż konto"/></p>
+        <p><input type="submit" value="{% trans "Register" %}"/></p>
     </form>
 {% endblock %}
index 260ac6e..f0eb274 100644 (file)
@@ -1,3 +1,4 @@
+{% load i18n %}
 {% load chunks compressed catalogue_tags sponsor_tags %}
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
@@ -5,45 +6,56 @@
     <head>
         <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
         <title>{% block title %}WolneLektury.pl{% endblock %}</title>
-        <link rel="icon" href="/static/img/favicon.png" type="image/x-icon" />
+        <link rel="icon" href="{{ STATIC_URL }}img/favicon.png" type="image/x-icon" />
         {% compressed_css "all" %}
+        <script type="text/javascript">var LANGUAGE_CODE = "{{ LANGUAGE_CODE}}";</script>
         {% compressed_js "jquery" %}
         {% compressed_js "all" %}
         {% block extrahead %}
         {% endblock %}
     </head>
     <body id="{% block bodyid %}base{% endblock %}">
-        <!--[if lt IE 7]><link href=/static/infobar/infobar.css rel=stylesheet>
+        <!--[if lt IE 7]><link href={{ STATIC_URL }}infobar/infobar.css rel=stylesheet>
         <div id=infobar><a href=http://browsehappy.pl/infobar>
-        Internet Explorer nie potrafi poprawnie wyświetlić tej strony. Kliknij tutaj, aby dowiedzieć się więcej...
-        </a></div><div id=viewplot><script src=/static/infobar/infobar.js></script><![endif]-->
+        {% trans "Internet Explorer cannot display this site properly. Click here to read more..." %}
+        </a></div><div id=viewplot><script src={{ STATIC_URL }}infobar/infobar.js></script><![endif]-->
         {% block bodycontent %}
         <div id="top-message">
             {% chunk "top-message" %}
         </div>
         <div id="header">
             <div id="logo">
-                <a href="/"><img src="/static/img/logo.png" alt="WolneLektury.pl - logo" /></a>
+                <a href="/"><img src="{{ STATIC_URL }}img/logo.png" alt="WolneLektury.pl - logo" /></a>
             </div>
             <div id="user-info" style="display:none">
                 {% if user.is_authenticated %}
                     <p>
-                        Witaj, <strong>{{ user.username }}</strong>
-                        | <a href="{% url user_shelves %}" id="user-shelves-link">Twoje półki</a>
+                        {% trans "Welcome" %}, <strong>{{ user.username }}</strong>
+                        | <a href="{% url user_shelves %}" id="user-shelves-link">{% trans "Your shelves" %}</a>
                         {% if user.is_staff %}
-                        | <a href="/admin/">Administracja</a>
+                        | <a href="/admin/">{% trans "Administration" %}</a>
                         {% endif %}
-                        | <a href="{% url logout %}?next={{ request.get_full_path }}">Wyloguj</a>
+                        | <a href="{% url logout %}?next={{ request.get_full_path }}">{% trans "Logout" %}</a>
                     </p>
                 {% else %}
-                    <p><a href="{% url login %}" class="login-register-link">Zaloguj się / Załóż konto</a></p>
+                    <p><a href="{% url login %}" class="login-register-link">{% trans "Sign in" %} / {% trans "Register" %}</a></p>
                 {% endif %}
             </div>
             <div class="social-links" style="float:right">
-                <a href="http://pl-pl.facebook.com/pages/Wolne-Lektury/203084073268"><img src="/static/img/social/facebook.png" /></a>
-                <a href="http://twitter.com/wolnelektury"><img src="/static/img/social/twitter.png" /></a>
-                <a href="http://nasza-klasa.pl/profile/30441509"><img src="/static/img/social/naszaklasa.png" /></a>     
+                <a href="http://pl-pl.facebook.com/pages/Wolne-Lektury/203084073268"><img src="{{ STATIC_URL }}img/social/facebook.png" alt="WolneLektury @ Facebook" /></a>
+                <a href="http://twitter.com/wolnelektury"><img src="{{ STATIC_URL }}img/social/twitter.png" alt="WolneLektury @ Twitter" /></a>
+                <a href="http://nasza-klasa.pl/profile/30441509"><img src="{{ STATIC_URL }}img/social/naszaklasa.png" alt="WolneLektury @ Nasza-Klasa" /></a>     
             </div>
+                       <div class="lang-menu" style="float:right;">
+                               <form action="/i18n/setlang/" method="post">
+                                       {% trans "Choose your interface language: " %} <select name="language">
+                                               {% for lang in LANGUAGES %}
+                                               <option value="{{ lang.0 }}"{% ifequal lang.0 LANGUAGE_CODE %} selected="selected"{% endifequal %}>{{ lang.1 }}</option>
+                                               {% endfor %}
+                                       </select>
+                                       <input type="submit" value="{% trans "Choose language" %}">
+                               </form>
+                       </div>                  
             <div class="clearboth"></div>
         </div>
         <div id="maincontent">
         <div class="clearboth"></div>
         <div id="footer">
             <p>
-                Wolne Lektury to projekt prowadzony przez <a href="http://nowoczesnapolska.org.pl/">Fundację Nowoczesna
-                Polska</a>. Reprodukcje cyfrowe wykonane przez <a href="http://www.bn.org.pl/">Bibliotekę Narodową</a>
-                z egzemplarzy pochodzących ze zbiorów BN. Hosting <a href="http://eo.pl/">EO Networks</a>.
+               {% blocktrans %}
+                               Wolne Lektury is a project lead by <a href="http://nowoczesnapolska.org.pl/">Modern Poland Foundation</a>.
+                               Digital reproductions are made by <a href="http://www.bn.org.pl/">The National Library</a>, based on TNL resources. 
+                               Hosting <a href="http://eo.pl/">EO Networks</a>.
+                               {% endblocktrans %}
             </p>
             <p>
-                Fundacja Nowoczesna Polska, 00-514 Warszawa, ul. Marszałkowska 84/92 lok. 125, tel/fax: (22) 621-30-17,
+               {% blocktrans %}
+                               Modern Poland Foundation, 00-514 Warsaw, ul. Marszałkowska 84/92 lok. 125, tel/fax: (22) 621-30-17
                 e-mail: <a href="mailto:fundacja@nowoczesnapolska.org.pl">fundacja@nowoczesnapolska.org.pl</a>
+                               {% endblocktrans %}
             </p>
 
                        {% sponsor_page "footer" %}
         </div>
         <div id="login-register-window">
-            <div class="header"><a href="#" class="jqmClose">Zamknij</a></div>
+            <div class="header"><a href="#" class="jqmClose">{% trans "Close" %}</a></div>
             <div class="target">
                 <form method="post" action="{% url login %}" id="login-form" class="cuteform">
-                    <h2>Zaloguj się / <a href="#" id="show-registration-form" style="font-size: 0.85em; font-weight: normal">Załóż konto</a></h2>
+                    <h2>{% trans "Sign in" %} / <a href="#" id="show-registration-form" style="font-size: 0.85em; font-weight: normal">{% trans "Register" %}</a></h2>
                     <p><span id="id_login-__all__"></span></p>
                     <ol>
                         {% authentication_form %}
-                        <li><input type="submit" value="Zaloguj się" /></li>
+                        <li><input type="submit" value="{% trans "Sign in" %}" /></li>
                     </ol>
                 </form>
                 <form method="post" action="{% url register %}" id="registration-form" class="cuteform" style="display: none;">
-                    <h2><a href="#" id="show-login-form" style="font-size: 0.85em; font-weight: normal">Zaloguj się</a> / Załóż konto</h2>
+                    <h2><a href="#" id="show-login-form" style="font-size: 0.85em; font-weight: normal">{% trans "Sign in" %}</a> / {% trans "Register" %}</h2>
                     <p><span id="id_registration-__all__"></span></p>
                     <ol>
                         {% user_creation_form %}
-                        <li><input type="submit" value="Załóż konto" /></li>
+                        <li><input type="submit" value="{% trans "Register" %}" /></li>
                     </ol>
                 </form>
             </div>
         </div>
         <div id="user-shelves-window">
-            <div class="header"><a href="#" class="jqmClose">Zamknij</a></div>
+            <div class="header"><a href="#" class="jqmClose">{% trans "Close" %}</a></div>
             <div class="target">
-                <p><img src="/static/img/indicator.gif" alt="*"/> Ładowanie</p>
+                <p><img src="{{ STATIC_URL }}img/indicator.gif" alt="*"/> {% trans "Loading" %}</p>
             </div>
         </div>
         {% endblock bodycontent %}
         pageTracker._trackPageview();
         </script>
     </body>
-</html>
+</html>
\ No newline at end of file
index f8991c9..e85f193 100644 (file)
@@ -1,55 +1,58 @@
 {% extends "base.html" %}
+{% load i18n %}
 {% load catalogue_tags pagination_tags %}
 
-{% block title %}Lektura {{ book.title }} w WolneLektury.pl{% endblock %}
+{% block title %}{{ book.title }} {% trans "on WolneLektury.pl" %}{% endblock %}
 
 {% block bodyid %}book-detail{% endblock %}
 
 {% block body %}
     <h1>{{ book.title }}, {{ categories.author|join:", " }}</h1>
     <form action="{% url search %}" method="get" accept-charset="utf-8" id="search-form">
-        <p>{{ form.q }} <input type="submit" value="Szukaj" /> <strong>lub</strong> <a href="{% url main_page %}">wróć do strony głównej</a></p>
+        <p>{{ form.q }} <input type="submit" value="{% trans "Search" %}" /> <strong>{% trans "or" %}</strong> <a href="{% url main_page %}">{% trans "return to main page" %}</a></p>
     </form>
     
     <div id="books-list">
         {% if extra_info.license %}
-        <p>Utwór jest udostępniony na licencji <a href="{{ extra_info.license }}">{{ extra_info.license_description }}</a>.</p>
+        <p>{% trans "Work is licensed under " %}<a href="{{ extra_info.license }}">{{ extra_info.license_description }}</a>.</p>
         {% endif %}
-        <p>Na podstawie: {{ extra_info.source_name }}</p>
+        <p>{% trans "Based on" %}: {{ extra_info.source_name }}</p>
         {% if book.has_description %}
             <div id="description">
                 {{ book.description|safe }}
             </div>
-            <div id="toggle-description"><p>Zwiń opis ▲</p></div>
+            <div id="toggle-description"><p>{% trans "Hide description" %} ▲</p></div>
         {% endif %}
         <div id="formats">
-            <p class="change-sets">Wrzuć lekturę <span><a href="{% url catalogue.views.book_sets book.slug %}" class="jqm-trigger">na półkę!</a></span></p>
+            <p class="change-sets">{% trans "Put a book" %} <span><a href="{% url catalogue.views.book_sets book.slug %}" class="jqm-trigger">{% trans "on the shelf!" %}</a></span></p>
             <div class="clearboth"></div>
             <div class="wrap">
             {% if book.html_file %}
-                <a href="{% url book_text book.slug %}">Czytaj online</a>
+                <a href="{% url book_text book.slug %}">{% trans "Read online" %}</a>
             {% endif %}
             {% if book.pdf_file %}
-                <a href="{{ book.pdf_file.url }}">Pobierz plik PDF</a>
+                <a href="{{ book.pdf_file.url }}">{% trans "Download PDF" %}</a>
             {% endif %}
             {% if book.odt_file %}
-                <a href="{{ book.odt_file.url }}">Pobierz plik ODT</a>
+                <a href="{{ book.odt_file.url }}">{% trans "Download ODT" %}</a>
             {% endif %}
             {% if book.txt_file %}
-                <a href="{{ book.txt_file.url }}">Pobierz plik TXT</a>
+                <a href="{{ book.txt_file.url }}">{% trans "Download TXT" %}</a>
             {% endif %}
             {% if book.mp3_file %}
                 <div id="czytamy-sluchajac-info">
-                    <a href="http://czytamysluchajac.pl/" id="czytamysluchajac-logo"><img src="/static/img/czytamysluchajac-logo-small.png" /></a>     
-                    <p>Czyta: {{ book.get_extra_info_value.artist_name }}</p>
-                    <p>Reżyseruje: {{ book.get_extra_info_value.director_name }}</p>
+                    <a href="http://czytamysluchajac.pl/" id="czytamysluchajac-logo"><img src="{{ STATIC_URL }}img/czytamysluchajac-logo-small.png" /></a>     
+                    <p>{% trans "Artist" %}: {{ book.get_extra_info_value.artist_name }}</p>
+                                       {% if book.get_extra_info_value.director_name %}
+                        <p>{% trans "Director" %}: {{ book.get_extra_info_value.director_name }}</p>
+                                       {% endif %}
                 </div>
             {% endif %}
-            {% if book.mp3_file %}<a href="{{ book.mp3_file.url }}">Pobierz plik MP3</a>{% endif %}
-            {% if book.ogg_file %}<a href="{{ book.ogg_file.url }}">Pobierz plik Ogg Vorbis</a>{% endif %}
+            {% if book.mp3_file %}<a href="{{ book.mp3_file.url }}">{% trans "Download MP3" %}</a>{% endif %}
+            {% if book.ogg_file %}<a href="{{ book.ogg_file.url }}">{% trans "Download Ogg Vorbis" %}</a>{% endif %}
             {% if book.mp3_file %}
-            <object type="application/x-shockwave-flash" style="margin-top: 0.5em" data="/static/player.swf" width="426" height="20">
-                <param name="movie" value="/static/player.swf" />
+            <object type="application/x-shockwave-flash" style="margin-top: 0.5em" data="{{ STATIC_URL }}player.swf" width="426" height="20">
+                <param name="movie" value="{{ STATIC_URL }}player.swf" />
                 <param name="bgcolor" value="#ffffff" />
                 <param name="FlashVars" value="mp3={{ book.mp3_file.url }}&amp;width=426&amp;showvolume=1&amp;bgcolor1=eeeeee&amp;bgcolor2=eeeeee&amp;buttoncolor=666666" />
             </object>
         
     <div id="tags-list">
         <div id="book-info">
-            <h2>O utworze</h2>
+            <h2>{% trans "Details" %}</h2>
             <ul>
                 <li>
-                    Autor
+                    {% trans "Author" %}
                     {% for tag in categories.author %}
                     <a href="{{ tag.get_absolute_url }}">{{ tag }}</a>
                     {% endfor %}
                 </li>
                 <li>
-                    Epoka:
+                    {% trans "Epoch" %}:
                     {% for tag in categories.epoch %}
                     <a href="{{ tag.get_absolute_url }}">{{ tag }}</a>
                     {% endfor %}
                 </li>
                 <li>
-                    Rodzaj:
+                    {% trans "Kind" %}:
                     {% for tag in categories.kind %}
                     <a href="{{ tag.get_absolute_url }}">{{ tag }}</a>
                     {% endfor %}
                 </li>
                 <li>
-                    Gatunek:
+                    {% trans "Genre" %}:
                     {% for tag in categories.genre %}
                     <a href="{{ tag.get_absolute_url }}">{{ tag }}</a>
                     {% endfor %}
                 </li>
             </ul>
-            <h2>W innych miejscach</h2>
+            <h2>{% trans "Other resources" %}</h2>
             <ul>
-                <li><a href="{{ extra_info.about }}">Lektura na wiki projektu</a></li>
-                <li><a href="{{ extra_info.source_url }}">Lektura w CBN Polona</a></li>
+                <li><a href="{{ extra_info.about }}">{% trans "Book on project's wiki" %}</a></li>
+                <li><a href="{{ extra_info.source_url }}">{% trans "Source of the book" %}</a></li>
                 {% if book.gazeta_link %}
-                <li><a href="{{ book.gazeta_link }}">Opis lektury w Lektury.Gazeta.pl</a></li>
+                <li><a href="{{ book.gazeta_link }}">{% trans "Book description on Lektury.Gazeta.pl" %}</a></li>
                 {% endif %}
                 {% if book.wiki_link %}
-                <li><a href="{{ book.wiki_link }}">Opis lektury w Wikipedii</a></li>
+                <li><a href="{{ book.wiki_link }}">{% trans "Book description on Wikipedia" %}</a></li>
                 {% endif %}
             </ul>
         </div>
         <div id="themes-list">
-            <h2>Motywy w utworze</h2>
+            <h2>{% trans "Work's themes " %}</h2>
             <ul>
             {% for theme in book_themes %}
                 <li><a href="{% url book_fragments book.slug,theme.slug %}">{{ theme }} ({{ theme.count }})</a></li>
         <div class="clearboth"></div>
     </div>
     <div id="set-window">
-        <div class="header"><a href="#" class="jqmClose">Zamknij</a></div>
+        <div class="header"><a href="#" class="jqmClose">{% trans "Close" %}</a></div>
         <div class="target">
-            <p><img src="/static/img/indicator.gif" alt="*"/> Ładowanie</p>
+            <p><img src="{{ STATIC_URL }}img/indicator.gif" alt="*"/> {% trans "Loading" %}</p>
         </div>
     </div>
-{% endblock %}
+{% endblock %}
\ No newline at end of file
index f21b1a8..550787d 100644 (file)
@@ -1,14 +1,15 @@
 {% extends "base.html" %}
+{% load i18n %}
 {% load catalogue_tags pagination_tags %}
 
-{% block title %}Motyw {{ theme }} w utworze {{ book }} w WolneLektury.pl{% endblock %}
+{% block title %}{% trans "Theme" %} {{ theme }} {% trans "in work " %} {{ book }} {% "on " %} WolneLektury.pl{% endblock %}
 
 {% block bodyid %}tagged-object-list{% endblock %}
 
 {% block body %}
-    <h1>Motyw {{ theme }} w utworze {{ book }}</h1>
+    <h1>{% trans "Theme" %} {{ theme }} {% trans "in work " %} {{ book }} </h1>
     <form action="{% url search %}" method="get" accept-charset="utf-8" id="search-form">
-        <p>{{ form.q }} <input type="submit" value="Szukaj" /> <strong>lub</strong> <a href="{{ book.get_absolute_url }}">wróć do strony utworu</a></p>
+        <p>{{ form.q }} <input type="submit" value="{% trans "Search" %}" /> <strong>{% trans "or" %}</strong> <a href="{{ book.get_absolute_url }}">{% trans "return to book's page" %}</a></p>
     </form>
 
     {% autopaginate fragments 10 %}
     </div>
     <div id="tags-list">
         <div id="categories-list">
-            Zobacz opis <a href="{{ book.get_absolute_url }}">utworu {{ book }}</a>
+            {% trans "See description" %} <a href="{{ book.get_absolute_url }}">{% trans "of the book "%} {{ book }}</a>
         </div>
         <div id="themes-list">
         </div>
         <div class="clearboth"></div>
     </div>
     <div id="set-window">
-        <div class="header"><a href="#" class="jqmClose">Zamknij</a></div>
+        <div class="header"><a href="#" class="jqmClose">{% trans "Close" %}</a></div>
         <div class="target">
-            <p><img src="/static/img/indicator.gif" alt="*"/> Ładowanie</p>
+            <p><img src="{{ STATIC_URL }}img/indicator.gif" alt="*"/> {% trans "Loading" %}</p>
         </div>
     </div>
 {% endblock %}
\ No newline at end of file
index 1f87217..3e6a9c5 100644 (file)
@@ -1,14 +1,15 @@
 {% extends "base.html" %}
+{% load i18n %}
 {% load catalogue_tags chunks %}
 
 {% block bodyid %}book-a-list{% endblock %}
 
-{% block title %}Alfabetyczny spis utworów w WolneLektury.pl{% endblock %}
+{% block title %}{% trans "Alphabetical listing of works on WolneLektury.pl" %}{% endblock %}
 
 {% block body %}
-    <h1>Alfabetyczny spis utworów</h1>
+    <h1>{% trans "Alphabetical listing of works" %}</h1>
     <form action="{% url search %}" method="GET" accept-charset="utf-8" id="search-form">
-        <p>{{ form.q }} <input type="submit" value="Szukaj" /> <strong>lub</strong> <a href="{% url main_page %}">wróć do strony głównej</a></p>
+        <p>{{ form.q }} <input type="submit" value="{% trans "Search" %}" /> <strong>{% trans "or" %}</strong> <a href="{% url main_page %}">{% trans "return to main page" %}</a></p>
     </form>
     
     <div id="book-list">
@@ -23,4 +24,4 @@
         </div>
         {% endfor %}    
     </div>
-{% endblock %}
+{% endblock %}
\ No newline at end of file
index fb5f6ae..e17c513 100644 (file)
@@ -1,17 +1,18 @@
-<h2>Wrzuć lekturę na półkę</h2>
+{% load i18n %}
+<h2>{% trans "Put a book on the shelf!" %}</h2>
 {% if not user.tag_set.count %}
-    <p>Nie posiadasz żadnych półek. Jeśli chcesz, możesz utworzyć nową półkę poniżej.</p>
+    <p>{% trans "You do not have any shelves. You can create one below, if you want to."%}</p>
 {% else %}
     <form action="{% url catalogue.views.book_sets book.slug %}" method="POST" accept-charset="utf-8" class="cuteform">
     <ol>
         <li>{{ form.set_ids }}</li>
-        <li><input type="submit" value="Wrzuć na półkę"/></li>
+        <li><input type="submit" value="{% trans "Put on the shelf!" %}"/></li>
     </ol>
     </form>
 {% endif %}
 <hr />
 <form action="{% url catalogue.views.new_set %}" method="POST" accept-charset="utf-8" class="cuteform">
 <ol>
-    <li>{{ new_set_form.name }} <input type="submit" value="Utwórz nową półkę"/></li>
+    <li>{{ new_set_form.name }} <input type="submit" value="{% trans "Create new shelf" %}"/></li>
 </ol>
 </form>
\ No newline at end of file
index 3012069..8b36718 100644 (file)
@@ -1,6 +1,7 @@
+{% load i18n %}
 <div class="book">
     <div class="change-sets">
-        <a href="{% url catalogue.views.book_sets book.slug %}" class="jqm-trigger">Na półkę!</a>
+        <a href="{% url catalogue.views.book_sets book.slug %}" class="jqm-trigger">{% trans "Put on the shelf!" %}</a>
     </div>
     {% if book.children.all|length %}
         <div class="book-parent-thumbnail"></div>
@@ -10,8 +11,8 @@
     <div class="book-description">
         <h2><a href="{{ book.get_absolute_url }}">{{ book.title }}</a></h2>
         {% if formats %}
-            <p style="margin: 0">Na skróty: {{ formats|join:", " }}</p>
+            <p style="margin: 0">{% trans "Jump to" %}: {{ formats|join:", " }}</p>
         {% endif %}
-        <p style="margin: 0">Utwór w kategoriach: {{ tags|join:", " }}</p>
+        <p style="margin: 0">{% trans "Categories" %}: {{ tags|join:", " }}</p>
     </div>
-</div>
+</div>
\ No newline at end of file
diff --git a/wolnelektury/templates/catalogue/book_stub_detail.html b/wolnelektury/templates/catalogue/book_stub_detail.html
new file mode 100644 (file)
index 0000000..5f19220
--- /dev/null
@@ -0,0 +1,36 @@
+{% extends "base.html" %}
+{% load i18n %}
+{% load catalogue_tags pagination_tags %}
+
+{% block title %}{{ book.title }} w WolneLektury.pl{% endblock %}
+
+{% block bodyid %}book-stub-detail{% endblock %}
+
+{% block body %}
+    <h1>{{ book.title }}, {{ book.author }}</h1>
+    <form action="{% url search %}" method="get" accept-charset="utf-8" id="search-form">
+        <p>{{ form.q }} <input type="submit" value="{% trans "Search" %}" /> <strong>{% trans "or" %}</strong> <a href="{% url main_page %}">{% trans "return to main page" %}</a></p>
+    </form>
+    
+    <div id="books-list">
+    {% if book.in_pd %}
+               {% trans "This work is in public domain and will be published on Internet school library of Wolne Lektury soon." %} 
+       {% else %}
+           {% if book.pd %}
+                       {% trans "This work will become part of public domain and will be allowed to be published without restrictions in" %}
+                   {% include "catalogue/pd_counter.html" %}
+                       {% trans "Find out why Internet libraries can't publish this work." %}
+               {% else %}
+                   {% trans "This work is copyrighted." %}
+               {% endif %}
+       {% endif %}
+    {% include "info/join_us.html" %}
+    </div>
+
+    <div id="set-window">
+        <div class="header"><a href="#" class="jqmClose">{% trans "Close" %}</a></div>
+        <div class="target">
+            <p><img src="{{ STATIC_URL }}img/indicator.gif" alt="*"/> {% trans "Loading" %}</p>
+        </div>
+    </div>
+{% endblock %}
\ No newline at end of file
index b3d18ea..d828265 100644 (file)
@@ -1,3 +1,4 @@
+{% load i18n %}
 {% load chunks compressed catalogue_tags %}
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
@@ -5,7 +6,7 @@
     <head>
         <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
         <title>{% block title %}WolneLektury.pl{% endblock %}</title>
-        <link rel="icon" href="/static/img/favicon.png" type="image/x-icon" />
+        <link rel="icon" href="{{ STATIC_URL }}img/favicon.png" type="image/x-icon" />
         {% compressed_css "book" %}
         {% compressed_js "jquery" %}
         {% compressed_js "book" %}
     <body>
         <div id="menu">
             <ul>
-                <li><a href="#toc">Spis treści</a></li>
-                <li><a href="#themes">Motywy</a></li>
+                <li><a href="#toc">{% trans "Table of contents" %}</a></li>
+                <li><a href="#themes">{% trans "Themes" %}</a></li>
             </ul>
         </div>
         <div id="header">
             <div id="logo">
-                <a href="/"><img src="/static/img/logo.png" alt="WolneLektury.pl - logo" /></a>
+                <a href="/"><img src="{{ STATIC_URL }}img/logo.png" alt="WolneLektury.pl - logo" /></a>
             </div>
         </div>
         <div id="themes">
index ecf8dba..000625b 100644 (file)
@@ -1,3 +1,4 @@
+{% load i18n %}
 {% load catalogue_tags %}
 <form action="{% url search %}" method="get" accept-charset="utf-8" id="search-form">
     <ol>
@@ -5,7 +6,7 @@
         <li class="category"><a href="{% catalogue_url tag %}">{{ tag }}</a> | <a href="{% catalogue_url tag_list -tag %}">x</a></li>
         {% endfor %}
         {% if search_form %}
-            <li>{{ search_form.q }} {{ search_form.tags }} <input type="submit" value="Szukaj"/></li>
+            <li>{{ search_form.q }} {{ search_form.tags }} <input type="submit" value="{% trans "Search" %}"/></li>
         {% endif %}
     </ol>
     <div class="clearboth"></div>
index 62896fc..62a5820 100644 (file)
@@ -1,6 +1,7 @@
+{% load i18n %}
 {% load catalogue_tags %}
 {% if one_tag %}
-    <p>Zobacz całą kategorię <a href="{% catalogue_url one_tag %}">{{ one_tag }}</a>.</p>
+    <p>{% trans "Show full category" %} <a href="{% catalogue_url one_tag %}">{{ one_tag }}</a>.</p>
 {% else %}
     <div class="shown-tags">
         <ul class="shown-tags">
@@ -9,7 +10,7 @@
             {% endfor %}
         </ul>
         {% if some_tags_hidden %}
-            <p><a href="#" class="show-all-tags">Zobacz więcej</a></p>
+            <p><a href="#" class="show-all-tags">{% trans "See more" %}</a></p>
         {% endif %}
     </div>
     <div class="all-tags">
@@ -18,6 +19,6 @@
                 <li><a href="{% catalogue_url choices tag %}">{{ tag }}&nbsp;({{ tag.count }})</a></li>
             {% endfor %}
         </ul>    
-        <p><a href="#" class="hide-all-tags">Zwiń</a></p>
+        <p><a href="#" class="hide-all-tags">{% trans "Hide" %}</a></p>
     </div>
 {% endif %}
index c7d641c..b270636 100644 (file)
@@ -1,17 +1,18 @@
-<h2>Półki zawierające fragment</h2>
+{% load i18n %}
+<h2>{% trans "Shelves containing fragment" %}</h2>
 {% if not user.tag_set.count %}
-    <p>Nie posiadasz żadnych półek. Jeśli chcesz, możesz utworzyć nową półkę poniżej.</p>
+    <p>{% trans "You do not own any shelves. You can create one below, if you want to." %}</p>
 {% else %}
     <form action="{% url catalogue.views.fragment_sets fragment.id %}" method="POST" accept-charset="utf-8" class="cuteform">
     <ol>
         <li>{{ form.set_ids }}</li>
-        <li><input type="submit" value="Zapisz półki"/></li>
+        <li><input type="submit" value="{% trans "Save all shelves" %}"/></li>
     </ol>
     </form>
 {% endif %}
 <hr />
 <form action="{% url catalogue.views.new_set %}" method="POST" accept-charset="utf-8" class="cuteform">
 <ol>
-    <li>{{ new_set_form.name }} <input type="submit" value="Utwórz nową półkę"/></li>
+    <li>{{ new_set_form.name }} <input type="submit" value="{% trans "Create new shelf" %}"/></li>
 </ol>
 </form>
\ No newline at end of file
index aaab2bf..ccca721 100644 (file)
@@ -1,22 +1,20 @@
+{% load i18n %}
 <div class="fragment">
-    {# <div class="change-sets"> #}
-    {#     <a href="{% url catalogue.views.fragment_sets fragment.id %}" class="jqm-trigger">Półki</a> #}
-    {# </div> #}
     {% if fragment.short_text %}
     <div class='fragment-short-text'>
         {{ fragment.short_text|safe }}
-        <a href="#">↓ Rozwiń fragment ↓</a>
+        <a href="#">↓ {% trans "Expand fragment" %} ↓</a>
     </div>
     {% endif %}
     <div class="fragment-text" {% if fragment.short_text %}style="display:none;"{% endif %}>
         {{ fragment.text|safe }}
         {% if fragment.short_text %}
-            <a href="#">↑ Zwiń fragment ↑</a>
+            <a href="#">↑ {% trans "Hide fragment" %} ↑</a>
         {% endif %}
     </div>
     <div class="fragment-metadata">
         <p><a href="{{ book.get_absolute_url }}">{{ book.title }}</a>, {{ book_authors|join:"," }}
-           <a href="{{ fragment.get_absolute_url }}">(Zobacz w utworze)</a></p>
+           <a href="{{ fragment.get_absolute_url }}">({% trans "See in a book" %})</a></p>
     </div>
     <div class="clearboth"></div>
 </div>
index 756bd74..2b9e1fa 100644 (file)
@@ -1,4 +1,5 @@
 {% extends "base.html" %}
+{% load i18n %}
 {% load catalogue_tags chunks cache %}
 
 {% block bodyid %}main-page{% endblock %}
     </div>
     <div class="clearboth"></div>
     <form action="{% url search %}" method="get" accept-charset="utf-8" id="search-form">
-        <p>{{ form.q }} {{ form.tags }} <input type="submit" value="Szukaj" /> <strong>lub</strong> <a href="{% url catalogue.views.book_list %}">zobacz spis utworów</a> w naszym zbiorze</p>
+        <p>{{ form.q }} {{ form.tags }} <input type="submit" value="{% trans "Search" %}" /> <strong>{% trans "or" %}</strong> <a href="{% url catalogue.views.book_list %}">{% trans "check list of books" %}</a> {% trans "in our repository" %}</p>
     </form>
     
     <div id="intro">
-        <p id="tags-description">↓ Przeglądaj lektury według wybranych kategorii ↓</p>
+        <p id="tags-description">↓ {% trans "Browse books by categories" %} ↓</p>
         <div id="propaganda">
-            <h2>Twoje półki z lekturami</h2>
+            <h2>{% trans "Your shelves with books" %}</h2>
             {% if user.is_authenticated %}
                 {% if shelves %}
                 <ul class="shelf-list">
                 {% for shelf in shelves %}
-                    <li><a href="{% url delete_shelf shelf.slug %}" class="delete-shelf">usuń</a> <a href="{{ shelf.get_absolute_url }}" class="visit-shelf">{{ shelf.name }} ({{ shelf.book_count }})</a></li>
+                    <li><a href="{% url delete_shelf shelf.slug %}" class="delete-shelf">{% trans "delete" %}</a> <a href="{{ shelf.get_absolute_url }}" class="visit-shelf">{{ shelf.name }} ({{ shelf.book_count }})</a></li>
                 {% endfor %}
                 </ul>
                 {% else %}
-                <p>Nie posiadasz żadnych półek. Jeśli chcesz, możesz utworzyć półkę poniżej.</p>
+                <p>{% trans "You do not own any shelves. You can create one below, if you want to." %}</p>
                 {% endif %}
                 <hr />
                 <form action="{% url catalogue.views.new_set %}" method="post" accept-charset="utf-8" class="cuteform">
                 <ol>
-                    <li>{{ new_set_form.name }} <input type="submit" value="Utwórz półkę"/></li>
+                    <li>{{ new_set_form.name }} <input type="submit" value="{% trans "Create shelf" %}"/></li>
                 </ol>
                 </form>
             {% else %}
-                <p>Stwórz własny zestaw lektur. Możesz się nim później podzielić z innymi, przesyłając im link do Twojej półki.</p>
-                <p>Aby zarządzać swoimi półkami, musisz się <a class="login-register-link" href="#">zalogować</a>.</p>
+                <p>{% trans "Create your own book set. You can share it with friends by sending them link to your shelf." %}</p>
+                <p>{% trans "You need to " %}<a class="login-register-link" href="#">{% trans "sign in" %}</a> {% trans "to manage your shelves." %}</p>
             {% endif %}
             <div id="lessons">
-                <h2><a href="{% url lessons_document_list %}">Materiały pomocnicze dla nauczycieli</a></h2>
-                <p>Scenariusze lekcji i inne pomysły na wykorzytanie serwisu WolneLektury.pl podczas nauczania.</p>
-                <p class="see-more"><a href="{% url lessons_document_list %}">Zobacz więcej ⇒</a></p>
+                <h2><a href="{% url lessons_document_list %}">{% trans "Hand-outs for teachers" %}</a></h2>
+                <p>{% trans "Lessons' prospects and other ideas for using Wolnelektury.pl for teaching." %}</p>
+                <p class="see-more"><a href="{% url lessons_document_list %}">{% trans "See more" %} ⇒</a></p>
             </div>
             <div id="czytamysluchajac">
-                <a href="http://czytamysluchajac.pl/"><img src="/static/img/czytamysluchajac-logo-small.png" /></a>
-                <p><a href="http://czytamysluchajac.pl/">Czytamy Słuchając</a> to profesjonalne nagrania tekstów literackich z naszego zbioru dostępne na wolnej licencji w formatach MP3, Ogg Vorbis oraz w systemie DAISY.</p>
-                <p class="see-more"><a href="http://czytamysluchajac.pl/index.php/o-projekcie/">Zobacz więcej ⇒</a></p>
+                <a href="http://czytamysluchajac.pl/"><img src="{{ STATIC_URL }}img/czytamysluchajac-logo-small.png" alt="CzytamySluchajac - logo" /></a>
+                <p><a href="http://czytamysluchajac.pl/">Czytamy Słuchając</a> {% trans "are professional recordings of literary texts from our repository, available on free license in MP3 and Ogg Vorbis formats as well as in DAISY system." %}</p>
+                <p class="see-more"><a href="http://czytamysluchajac.pl/index.php/o-projekcie/">{% trans "See more" %} ⇒</a></p>
             </div>
         </div>
         <div id="tags-list">
             <div id="categories-list">
                 {% if categories.author %}
-                    <h2>Autorzy</h2>
+                    <h2>{% trans "Authors" %}</h2>
                     {% folded_tag_list categories.author %}
                 {% endif %}
                 {% if categories.kind %}
-                    <h2>Rodzaje</h2>
+                    <h2>{% trans "Kinds" %}</h2>
                     {% folded_tag_list categories.kind %}
                 {% endif %}
                 {% if categories.genre %}
-                    <h2>Gatunki</h2>
+                    <h2>{% trans "Genres" %}</h2>
                     {% folded_tag_list categories.genre %}
                 {% endif %}
                 {% if categories.epoch %}
-                    <h2>Epoki</h2>
+                    <h2>{% trans "Epochs" %}</h2>
                     {% folded_tag_list categories.epoch %}
                 {% endif %}
             </div>
             <div id="themes-list">
                 {% if fragment_tags %}
-                    <h2>Motywy i tematy</h2>
+                    <h2>{% trans "Themes and topics" %}</h2>
                     {% folded_tag_list fragment_tags %}
                 {% endif %}
-                <h2>Rodziny motywów</h2>
+                <h2>{% trans "Themes groups" %}</h2>
                 <div class="shown-tags">
                     <ol>
                         <li>środowisko miejskie i wiejskie
@@ -83,7 +84,7 @@
                         <li>przyroda
                         <span class="subcategories"><a href="/katalog/natura/">Natura</a>, <a href="/katalog/zywioly/">Żywioły</a>, <a href="/katalog/ogien/">Ogień</a>, <a href="/katalog/ziemia/">Ziemia</a>, <a href="/katalog/wiatr/">Wiatr</a>, <a href="/katalog/woda/">Woda</a>, <a href="/katalog/wiosna/">Wiosna</a>, <a href="/katalog/lato/">Lato</a>, <a href="/katalog/jesien/">Jesień</a>, <a href="/katalog/zima/">Zima</a>, <a href="/katalog/przemijanie/">Przemijanie</a>, <a href="/katalog/slonce/">Słońce</a>, <a href="/katalog/ksiezyc/">Księżyc</a>, <a href="/katalog/gwiazda/">Gwiazda</a>, <a href="/katalog/oblok/">Obłok</a>, <a href="/katalog/noc/">Noc</a>, <a href="/katalog/swiatlo/">Światło</a>, <a href="/katalog/gora/">Góra</a>, <a href="/katalog/rzeka/">Rzeka</a>, <a href="/katalog/morze/">Morze</a>, <a href="/katalog/burza/">Burza</a>, <a href="/katalog/deszcz/">Deszcz</a>, <a href="/katalog/bloto/">Błoto</a>, <a href="/katalog/przyroda-nieozywiona/">Przyroda nieożywiona</a>, <a href="/katalog/rosliny/">Rośliny</a>, <a href="/katalog/kwiaty/">Kwiaty</a>, <a href="/katalog/ogrod/">Ogród</a>, <a href="/katalog/sielanka/">Sielanka</a>, <a href="/katalog/raj/">Raj</a>, <a href="/katalog/jablko/">Jabłko</a>, <a href="/katalog/drzewo/">Drzewo</a>, <a href="/katalog/zwierzeta/">Zwierzęta</a>, <a href="/katalog/ptak/">Ptak</a>, <a href="/katalog/motyl/">Motyl</a>, <a href="/katalog/kot/">Kot</a>, <a href="/katalog/kon/">Koń</a>, <a href="/katalog/pies/">Pies</a>, <a href="/katalog/waz/">Wąż</a>, <a href="/katalog/potwor/">Potwór</a></span></li>
                     </ol>
-                    <p><a href="#" class="show-all-tags">Zobacz więcej</a></p>
+                    <p><a href="#" class="show-all-tags">{% trans "See more" %}</a></p>
                 </div>
                 <div class="all-tags">
                     <ol>
                     <li>żywioły
                     <span class="subcategories"><a href="/katalog/zywioly/">Żywioły</a>, <a href="/katalog/ogien/">Ogień</a>, <a href="/katalog/ziemia/">Ziemia</a>, <a href="/katalog/wiatr/">Wiatr</a>, <a href="/katalog/woda/">Woda</a>, <a href="/katalog/przestrzen/">Przestrzeń</a></span></li>
                     </ol>
-                    <p><a href="#" class="hide-all-tags">Zwiń</a></p>
+                    <p><a href="#" class="hide-all-tags">{% trans "Hide" %}</a></p>
                 </div>
             </div>
             <div class="clearboth"></div>
     
     <div id="site-info">
         <div id="latest-blog-posts">
-            <h2>Aktualności</h2>
+            <h2>{% trans "News" %}</h2>
             {% cache 1800 latest-blog-posts %}
             {% latest_blog_posts "http://www.nowoczesnapolska.org.pl/tematy/wolne-lektury/feed/" %}
             {% endcache %}
-            <p class="see-more"><a href="http://www.nowoczesnapolska.org.pl/">Zobacz nasz blog ⇒</a></p>
+            <p class="see-more"><a href="http://www.nowoczesnapolska.org.pl/">{% trans "See our blog" %} ⇒</a></p>
         </div>
         <div id="you-can-help">
-            <h2>Możesz nam pomóc!</h2>
-            <p>Utwory włączane sukcesywnie do naszej biblioteki staramy się opracowywać jak najdokładniej. Jest to możliwe tylko dzięki współpracującym z nami wolontariuszom.</p>
-            <p>Zapraszamy wszystkie osoby, które chcą współtworzyć szkolną bibliotekę internetową Wolne Lektury.</p>
-            <p class="see-more"><a href="{% url help_us %}">Zobacz więcej ⇒</a></p>
+            <h2>{% trans "You can help us!" %}</h2>
+            <p>{% trans "We try our best to elaborate works appended to our library. It is possible only due to support of our volunteers." %}</p>
+            <p>{% trans "We invite people who want to take part in developing Internet school library Wolne Lektury." %}</p>
+            <p class="see-more"><a href="{% url help_us %}">{% trans "See more" %} ⇒</a></p>
         </div>
         <div id="about-us">
-            <h2>O projekcie</h2>
-            <p>Biblioteka internetowa z lekturami szkolnymi „Wolne Lektury” (<a href="http://wolnelektury.pl">www.wolnelektury.pl</a>) to projekt realizowany przez Fundację Nowoczesna Polska. Działa od 2007 roku i udostępnia w swoich zbiorach lektury szkolne, które są zalecane do użytku przez Ministerstwo Edukacji Narodowej i które trafiły już do domeny publicznej.
+            <h2>{% trans "About us" %}</h2>
+            <p>
+               {% blocktrans %}
+                       Internet library with school readings “Wolne Lektury” (<a href="http://wolnelektury.pl">www.wolnelektury.pl</a>) is a project made by Modern Poland Foundation. It started in 2007 and shares school readings, which are recommended by Ministry of National Education and are in public domain.
+                       {% endblocktrans %}
             </p>
-            <p class="see-more"><a href="{% url about_us %}">Zobacz więcej ⇒</a></p>
+            <p class="see-more"><a href="{% url about_us %}">{% trans "See more" %} ⇒</a></p>
         </div>
     </div>
-{% endblock %}
+{% endblock %}
\ No newline at end of file
diff --git a/wolnelektury/templates/catalogue/pd_counter.html b/wolnelektury/templates/catalogue/pd_counter.html
new file mode 100644 (file)
index 0000000..7296317
--- /dev/null
@@ -0,0 +1,6 @@
+<div id='countdown'></div>
+<script>
+$.countdown.setDefaults($.countdown.regional['{{ LANGUAGE_CODE }}']);
+d = new Date({{ pd_counter }}, 1, 1);
+$('#countdown').countdown({until: d, format: 'ydHMS', serverSync: serverTime}); 
+</script>
diff --git a/wolnelektury/templates/catalogue/search_multiple_hits.html b/wolnelektury/templates/catalogue/search_multiple_hits.html
new file mode 100644 (file)
index 0000000..cc93d56
--- /dev/null
@@ -0,0 +1,27 @@
+{% extends "base.html" %}
+{% load catalogue_tags pagination_tags %}
+
+{% block title %}Wyszukiwanie w WolneLektury.pl{% endblock %}
+
+{% block bodyid %}tagged-object-list{% endblock %}
+
+{% block body %}
+    <h1>{% title_from_tags tags %}</h1>
+    {% breadcrumbs tags %}
+    
+    <div id="books-list">
+        <p>Znaleziono więcej niż jeden wynik odpowiadający kryteriom wyszukiwania.</p>
+               <ul class='matches'>
+        {% for match, link, type in results %}
+            <li><a href='{{ link }}'>{{ match.name }}</a> ({{ type }})</li>
+        {% endfor %}
+               </ul>
+    </div>
+
+    <div id="set-window">
+        <div class="header"><a href="#" class="jqmClose">Zamknij</a></div>
+        <div class="target">
+            <p><img src="{{ STATIC_URL }}img/indicator.gif" alt="*"/> Ładowanie</p>
+        </div>
+    </div>
+{% endblock %}
\ No newline at end of file
diff --git a/wolnelektury/templates/catalogue/search_no_hits.html b/wolnelektury/templates/catalogue/search_no_hits.html
new file mode 100644 (file)
index 0000000..8965a0e
--- /dev/null
@@ -0,0 +1,28 @@
+{% extends "base.html" %}
+{% load i18n %}
+{% load catalogue_tags pagination_tags %}
+
+{% block title %}{% trans "Search in WolneLektury.pl" %}{% endblock %}
+
+{% block bodyid %}tagged-object-list{% endblock %}
+
+{% block body %}
+    <h1>{% title_from_tags tags %}</h1>
+    {% breadcrumbs tags %}
+    
+    <div id="books-list">
+       <p>Przepraszamy! Brak wyników spełniających kryteria podane w zapytaniu.
+        <p>{% trans "Sorry! Search cirteria did not match any resources." %}</p>
+               
+               <p>{% blocktrans %}Search engine supports following criteria: title, author, theme/topic, epoch, kind and genre.
+               As for now we do not support full text search.{% endblocktrans %}</p>
+        {% include "info/join_us.html" %}
+    </div>
+
+    <div id="set-window">
+        <div class="header"><a href="#" class="jqmClose">{% trans "Close" %}</a></div>
+        <div class="target">
+            <p><img src="{{ STATIC_URL }}img/indicator.gif" alt="*"/> {% trans "Loading" %}</p>
+        </div>
+    </div>
+{% endblock %}
\ No newline at end of file
diff --git a/wolnelektury/templates/catalogue/search_too_short.html b/wolnelektury/templates/catalogue/search_too_short.html
new file mode 100644 (file)
index 0000000..db9e585
--- /dev/null
@@ -0,0 +1,23 @@
+{% extends "base.html" %}
+{% load catalogue_tags pagination_tags %}
+
+{% block title %}Wyszukiwanie w WolneLektury.pl{% endblock %}
+
+{% block bodyid %}tagged-object-list{% endblock %}
+
+{% block body %}
+    <h1>{% title_from_tags tags %}</h1>
+    {% breadcrumbs tags %}
+    
+    <div id="books-list">
+        <p>Przepraszamy! Wyszukiwana fraza musi mieć co najmniej dwa znaki.</p>
+        {% include "info/join_us.html" %}
+    </div>
+
+    <div id="set-window">
+        <div class="header"><a href="#" class="jqmClose">Zamknij</a></div>
+        <div class="target">
+            <p><img src="{{ STATIC_URL }}img/indicator.gif" alt="*"/> Ładowanie</p>
+        </div>
+    </div>
+{% endblock %}
\ No newline at end of file
index 673135c..e96320e 100644 (file)
@@ -1,6 +1,7 @@
+{% load i18n %}
 {% load catalogue_tags %}
 {% if one_tag %}
-    <p>Zobacz całą kategorię <a href="{% catalogue_url one_tag %}">{{ one_tag }}</a></p>
+    <p>{% trans "See full category" %} <a href="{% catalogue_url one_tag %}">{{ one_tag }}</a></p>
 {% else %}
     <ul>
         {% for tag in tags %}
index 88c06f2..66d6ee4 100644 (file)
@@ -1,4 +1,5 @@
 {% extends "base.html" %}
+{% load i18n %}
 {% load catalogue_tags pagination_tags %}
 
 {% block title %}{% title_from_tags tags %} w WolneLektury.pl{% endblock %}
@@ -11,8 +12,8 @@
     
     {% if shelf_is_set and not object_list %}
     <div id="books-list">
-        <h2>Twoja półka jest pusta</h2>
-        <p>Możesz wrzucić książkę na półkę, wchodząc na stronę danej lektury i klikając na przycisk „Na półkę!”.</p>
+        <h2>{% trans "Your shelf is empty" %}</h2>
+        <p>{% trans "You can put a book on a shelf by entering page of the reading and clicking 'Put on the shelf'." %}</p>
     </div>
     {% else %}
     {% autopaginate object_list 10 %}
                 {{ last_tag.description|safe }}
             </div>
             <div class="clearboth"></div>
-            <div id="toggle-description"><p>Zwiń opis ▲</p></div>
+            <div id="toggle-description"><p>{% trans "Hide description" %} ▲</p></div>
         {% endif %}
         {% if shelf_is_set %}
             <a id="download-shelf" href="{% url download_shelf last_tag.slug %}">
-                Pobierz wszystkie książki z tej półki
+                {% trans "Download all books from this shelf" %}
             </a>
             <div id="download-shelf-menu" style="display:none;">
                 <form action="{% url download_shelf last_tag.slug %}" method="get" accept-charset="utf-8" id="download-formats-form" data-formats-feed="{% url shelf_book_formats last_tag.slug %}">
-                    <p>Wybierz formaty książek, które chcesz pobrać:</p>
-                    <li data-format="pdf"><label for="id_formats_2"><input type="checkbox" name="formats" value="pdf" id="id_formats_2" /> PDF</label> <em><strong>do czytania</strong> i drukowania przy pomocy <a href="http://get.adobe.com/reader/">Adobe Reader</a></em></li>
-                    <li data-format="odt"><label for="id_formats_3"><input type="checkbox" name="formats" value="odt" id="id_formats_3" /> ODT</label> <em><strong>do czytania</strong> i edytowania przy pomocy <a href="http://pl.openoffice.org/">OpenOffice.org</a></em></li>
-                    <li data-format="txt"><label for="id_formats_4"><input type="checkbox" name="formats" value="txt" id="id_formats_4" /> TXT</label> <em><strong>do czytania</strong> na małych ekranach, np. na komórce</em></li>
-                    <li data-format="mp3"><label for="id_formats_0"><input type="checkbox" name="formats" value="mp3" id="id_formats_0" /> MP3</label> <em><strong>do słuchania</strong> w ulubionym odtwarzaczu MP3</em></li>
-                    <li data-format="ogg"><label for="id_formats_1"><input type="checkbox" name="formats" value="ogg" id="id_formats_1" /> Ogg Vorbis</label> <em><strong>do słuchania</strong> &mdash; otwarty format <a href="http://www.vorbis.com/">Fundacji Xiph.Org</a></em></li>
-                    <li id="download-formats-form-submit-li"><label><input type="submit" name="submit" value="Pobierz" id="download-formats-form-submit" disabled="disabled" />&nbsp;<img src="/static/img/indicator.gif" /></label> <span id="updating-formats">Uaktualniam listę formatów książek na półce.</span><span id="formats-updated" style="display:none;">lub <a href="#" id="download-formats-form-cancel">anuluj</a></span></li>
+                    <p>{% trans "Choose books' formats which you want to download:" %}</p>
+                    <li data-format="pdf"><label for="id_formats_2"><input type="checkbox" name="formats" value="pdf" id="id_formats_2" /> PDF</label> <em><strong>{% trans "for reading" %}</strong> {% trans "and printing using" %} <a href="http://get.adobe.com/reader/">Adobe Reader</a></em></li>
+                    <li data-format="odt"><label for="id_formats_3"><input type="checkbox" name="formats" value="odt" id="id_formats_3" /> ODT</label> <em><strong>{% trans "for reading" %}</strong> {% trans "and editing using" %} <a href="http://pl.openoffice.org/">OpenOffice.org</a></em></li>
+                    <li data-format="txt"><label for="id_formats_4"><input type="checkbox" name="formats" value="txt" id="id_formats_4" /> TXT</label> <em><strong>{% trans "for reading" %}</strong> {% trans "on small displays, for example mobile phones" %}</em></li>
+                    <li data-format="mp3"><label for="id_formats_0"><input type="checkbox" name="formats" value="mp3" id="id_formats_0" /> MP3</label> <em><strong>{% trans "for listening" %}</strong> {% trans "on favourite MP3 player" %}</em></li>
+                    <li data-format="ogg"><label for="id_formats_1"><input type="checkbox" name="formats" value="ogg" id="id_formats_1" /> Ogg Vorbis</label> <em><strong>{% trans "for listening" %}</strong> &mdash; {% trans "open format" %} <a href="http://www.vorbis.com/">{% trans "Xiph.org Foundation" %}</a></em></li>
+                    <li id="download-formats-form-submit-li"><label><input type="submit" name="submit" value="{% trans "Download" %}" id="download-formats-form-submit" disabled="disabled" />&nbsp;<img src="{{ STATIC_URL }}img/indicator.gif" /></label> <span id="updating-formats">{% trans "Updating list of books' formats on the shelf" %}</span><span id="formats-updated" style="display:none;">{% trans "or" %} <a href="#" id="download-formats-form-cancel">{% trans "cancel" %}</a></span></li>
                     <div class="clearboth"></div>
                 </form>
             </div>
+            {% if user_is_owner %}
+            <div id="toggle-share-shelf"><p>{% trans "Share this shelf" %}</p></div>
+            <div id="share-shelf">
+                <p>{% trans "Copy this link and share it with other people to let them see your shelf." %}
+                <input id="share-shelf-url" value='http://{{ request.META.HTTP_HOST }}{{ request.path }}' />
+                </p>
+            </div>
+                       {% endif %}
         {% endif %}
         {% if last_tag.gazeta_link %}
         <p><a href="{{ last_tag.gazeta_link }}">
-            {% ifequal last_tag.category "author" %}Przeczytaj omówienia utworów autora w serwisie Lektury.Gazeta.pl{% endifequal %}
-            {% ifequal last_tag.category "epoch"  %}Przeczytaj omówienia z epoki {{ last_tag }} w serwisie Lektury.Gazeta.pl{% endifequal %}
+            {% ifequal last_tag.category "author" %}
+                               {% trans "Read work's study of this author on Lektury.Gazeta.pl" %}
+                       {% endifequal %}
+            {% ifequal last_tag.category "epoch" %}
+                               {% trans "Read study of epoch" %} {{ last_tag }} {% trans "on Lektury.Gazeta.pl" %}
+                       {% endifequal %}
         </a></p>
         {% endif %}
         {% if last_tag.wiki_link %}
         <p><a href="{{ last_tag.wiki_link }}">
-            {% ifequal last_tag.category "author" %}Przeczytaj artykuł o autorze w Wikipedii{% endifequal %}
-            {% ifequal last_tag.category "epoch"  %}Przeczytaj artykuł o epoce {{ last_tag }} w Wikipedii{% endifequal %}
+            {% ifequal last_tag.category "author" %}
+                               {% trans "Read article about this author on Wikipedia" %}
+                       {% endifequal %}
+            {% ifequal last_tag.category "epoch"  %}
+                               {% trans "Read article about epoch" %} {{ last_tag }} {% trans "on Wikipedia" %}
+                       {% endifequal %}
         </a></p>
         {% endif %}
-        <ol>
-        {% for book in object_list %}
-            <li>
-                {% if user_is_owner %}
-                    <a href="{% url remove_from_shelf last_tag.slug book.slug %}" class="remove-from-shelf">Usuń</a>
+
+        {% if object_list %}
+            <ol>
+            {% for book in object_list %}
+                <li>
+                    {% if user_is_owner %}
+                        <a href="{% url remove_from_shelf last_tag.slug book.slug %}" class="remove-from-shelf">{% trans "Delete" %}</a>
+                    {% endif %}
+                    {{ book.short_html }}</li>
+            {% endfor %}
+            </ol>
+        {% else %}
+                       {% if only_author %}
+                   {% if last_tag.alive %}
+                                       {% trans "This author's works are copyrighted." %}
+                               {% else %}{% comment %} Is dead {% endcomment %}
+                   {% if last_tag.in_pd %}
+                                               <p>{% trans "This author's works are in public domain and will be published on Internet school library of Wolne Lektury soon." %}</p>            
+                       {% else %}
+                                               {% comment %} Is dead, but not yet in public domain {% endcomment %}
+                       <div>
+                               <p>{% trans "This author's works will become part of public domain and will be allowed to be published without restrictions in" %}</p>
+                                                       {% include "catalogue/pd_counter.html" %}
+                            <p>{% trans "Find out why Internet libraries can't publish this author's works." %}</p>
+                                               </div>
+                       {% endif %}
                 {% endif %}
-                {{ book.short_html }}</li>
-        {% endfor %}
-        </ol>
+                       {% else %}
+                           {% trans "No works of this author found." %}
+                       {% endif %}
+            {% include "info/join_us.html" %}
+        {% endif %}
         {% endwith %}
         {% paginate %}
     </div>
+       {% if object_list %}
+       {% comment %} If we didn't find anything there will be nothing on the right side as well {% endcomment %}
     <div id="tags-list">
         <div id="categories-list">
             {% if categories.author %}
-                <h2>Autorzy</h2>
+                <h2>{% trans "Authors" %}</h2>
                 {% tag_list categories.author tags %}
             {% endif %}
             {% if categories.kind %}
-                <h2>Rodzaje</h2>
+                <h2>{% trans "Kinds" %}</h2>
                 {% tag_list categories.kind tags %}
             {% endif %}
             {% if categories.genre %}
-                <h2>Gatunki literackie</h2>
+                <h2>{% trans "Genres" %}</h2>
                 {% tag_list categories.genre tags %}
             {% endif %}
             {% if categories.epoch %}
-                <h2>Epoki</h2>
+                <h2>{% trans "Epochs" %}</h2>
                 {% tag_list categories.epoch tags %}
             {% endif %}        
         </div>
         <div id="themes-list">
             {% if categories.theme %}
-                <h2>Motywy</h2>
+                <h2>{% trans "Themes" %}</h2>
                 {% tag_list categories.theme tags %}
             {% endif %}
         </div>
         <div class="clearboth"></div>
     </div>
+       {% endif %}
     {% endif %}
     <div id="set-window">
-        <div class="header"><a href="#" class="jqmClose">Zamknij</a></div>
+        <div class="header"><a href="#" class="jqmClose">{% trans "Close" %}</a></div>
         <div class="target">
-            <p><img src="/static/img/indicator.gif" alt="*"/> Ładowanie</p>
+            <p><img src="{{ STATIC_URL }}img/indicator.gif" alt="*"/> {% trans "Loading" %}</p>
         </div>
     </div>
 {% endblock %}
\ No newline at end of file
index ab34fe4..28c1222 100644 (file)
@@ -1,16 +1,17 @@
-<h2>Twoje półki z lekturami</h2>
+{% load i18n %}
+<h2>{% trans "Your shelves with books" %}</h2>
 {% if shelves %}
 <ul class="shelf-list">
 {% for shelf in shelves %}
-    <li><a href="{% url delete_shelf shelf.slug %}" class="delete-shelf">usuń</a> <a href="{{ shelf.get_absolute_url }}" class="visit-shelf">{{ shelf.name }} ({{ shelf.book_count }})</a></li>
+    <li><a href="{% url delete_shelf shelf.slug %}" class="delete-shelf">{% trans "remove" %}</a> <a href="{{ shelf.get_absolute_url }}" class="visit-shelf">{{ shelf.name }} ({{ shelf.book_count }})</a></li>
 {% endfor %}
 </ul>
 {% else %}
-<p>Nie posiadasz żadnych półek. Jeśli chcesz, możesz utworzyć półkę poniżej.</p>
+<p>{% trans "You do not own any shelves. You can create one below if you want to" %}.</p>
 {% endif %}
 <hr />
 <form action="{% url catalogue.views.new_set %}" method="post" accept-charset="utf-8" class="cuteform">
 <ol>
-    <li>{{ new_set_form.name }} <input type="submit" value="Utwórz półkę"/></li>
+    <li>{{ new_set_form.name }} <input type="submit" value="{% trans "Create shelf" %}"/></li>
 </ol>
 </form>
\ No newline at end of file
index 37ad44b..7f43802 100644 (file)
@@ -1,4 +1,5 @@
 {% extends "base.html" %}
+{% load chunks %}
 
 {% block title %}O projekcie WolneLektury.pl{% endblock %}
 
     </form>
 
     <div class="column-left">
-        <h2>Wolne Lektury</h2>
-    
-        <p>Biblioteka internetowa z lekturami szkolnymi „Wolne Lektury” (www.wolnelektury.pl) to projekt realizowany
-        przez Fundację Nowoczesna Polska. Działa od 2007 roku i udostępnia w swoich zbiorach lektury szkolne, które są
-        zalecane do użytku przez Ministerstwo Edukacji Narodowej i które trafiły już do domeny publicznej. Są one
-        opracowane, opatrzone komentarzem i udostępnione w kilku formatach (html, odt, txt i pdf). Można je zgodnie z
-        prawem, bezpłatnie przeglądać, ściągać na swój komputer, a także udostępniać innym i cytować.</p>
-        
-         <p>Zespół projektu Wolne Lektury składa się z doświadczonych redaktorek i nauczycielek, co zapewnia rzetelność
-        naszego portalu. W tworzeniu go współpracujemy z Biblioteką Narodową, która dostarcza nam najlepszych dostępnych
-        wydań i opracowań krytycznych lektur szkolnych, opublikowanych w Cyfrowej Bibliotece Narodowej Polona. Wspólnie
-        staramy się, aby teksty lektur – nasze dziedzictwo kulturowe – były dostępne dla wszystkich, niezależnie od
-        miejsca zamieszkania, zasobności portfela, sprawności lub jej braku. Jest to możliwe dzięki istnieniu domeny
-        publicznej, czyli zbioru dzieł nieobjętych restrykcjami prawa autorskiego, oraz dzięki nowoczesnym technologiom
-        – narzędziom, które pozwalają zwielokrotnić dostępność treści upublicznianych w Internecie.</p>
-        
-         <p>Projekt Wolne Lektury jest całkowicie niekomercyjny i realizowany pro publico bono. Dlatego tak ważne jest
-        dla nas poparcie udzielone przez wybitne osobistości kultury i nauki. Patronat honorowy nad projektem Wolne 
-        Lektury sprawuje Ministerstwo Kultury i Dziedzictwa Narodowego, Ministerstwo Edukacji Narodowej oraz
-        Stowarzyszenie Pisarzy Polskich.. W Komitecie Honorowym Wolnych Lektur zgodzili się uczestniczyć prof. Maria
-        Janion, prof. Grażyna Borkowska, prof. Przemysław
-        Czapliński, prof. Mieczysław Dąbrowski, prof. Ewa Kraskowska, prof. Małgorzata Czermińska, prof. Jerzy Jarzębski
-        i prof. Piotr Śliwiński.</p>
-        
-         <p>Digitalizacją i korektą tekstów zajmuje się Biblioteka Narodowa. Serwis internetowy został zaprojektowany
-        przez 2ia. Autorem języka składu tekstów Wolnych Lektur opartego na języku XML jest Dariusz Gałecki. Obsługę
-        prawną Wolnych Lektur zapewnia Kancelaria Grynhoff,
-        Woźny, Maliński. Hosting serwisu zapewnia
-        firma EO Networks. W opracowaniu technicznym tekstów pomaga wydawnictwo Korporacja Ha!Art. Logo Wolne Lektury
-        jest dziełem agencji PZL. Projekt objęli patronatem medialnym: Dziennik, Elle, Tok.fm, Biblioteka Analiz,
-        Tygodnik Powszechny, Przekrój i TVP Kultura.</p>
-        
-        <h2>Jak korzystać z Wolnych Lektur?</h2>
-        
-         <p>Najważniejszą innowacją Wolnych Lektur, odróżniającą ten projekt od innych bibliotek internetowych, jest
-        możliwość przeszukiwania tekstów z zastosowaniem różnych kryteriów: tradycyjnych, takich jak tytuł, autor,
-        epoka, rodzaj, gatunek literacki, ale i niespotykanych nigdzie indziej, to jest odnoszących się do treści wielu
-        utworów naraz – motywów i tematów literackich. Takie przeszukiwanie jest możliwe dzięki specjalnemu opracowaniu
-        tekstów lektur szkolnych, to znaczy opisaniu ich za pomocą wymienionych kryteriów. Tym właśnie Wolne Lektury
-        różnią się od licznych stron z opracowaniami lektur szkolnych – dostarczają narzędzi do twórczej pracy z
-        tekstem, a nie gotowych ściąg. Jeśli np. uczeń dostanie temat pracy domowej: „Motyw dziecka w romantyzmie na
-        podstawie wybranych utworów“, to trzy kliknięcia na naszej stronie sprawią, że na ekranie komputera pojawi mu
-        się komplet tekstów do analizy. Dzięki takim narzędziom czytanie lektur staje się fascynującą podróżą po świecie
-        kultury.</p>
-        
-         <p>Kolejną użyteczną funkcjonalnością Wolnych Lektur, przydatną w pracy w szkole, jest możliwość układania
-        całych zestawów tekstów przerabianych na przykład w ciągu roku przez daną klasę. Takie półki z lekturami są
-        tworzone przez nauczycielkę/la, która/y może następnie wysłać swoim uczniom odnośnik (link) do tego zbioru, a
-        oni jednym kliknięciem ściągną cały zestaw na swój komputer.</p>
-        
-        <p>Wszystkie teksty książek ze stron Wolne Lektury (w postaci plików html, pdf, txt) dostępne są poza restrykcjami prawa autorskiego i można je swobodnie wykorzystywać bez żadnych warunków.</p>
-
-        <p>Jeśli teksty te są opatrzone dodatkowymi materiałami (przypisy, motywy literackie etc.) które podlegają prawu
-        autorskiemu, to te dodatkowe materiały udostępnione są na licencji Creative Commons Uznanie Autorstwa - Na Tych
-        Samych Warunkach 3.0 PL (<a href="http://creativecommons.org/licenses/by-sa/3.0/">http://creativecommons.org/licenses/by-sa/3.0/</a>).</p>
+        {% chunk "about_us_left" %}
     </div>
-    
     <div class="column-right">
-        <h2>O Fundacji</h2>
-
-        <p>Fundacja Nowoczesna Polska powstała, ponieważ kształcenie dzieci jest kluczem do przyszłości Polski. Jednym z
-        najważniejszych zadań, jakie stoją przed polską edukacją, jest walka z cyfrowym wykluczeniem. Umiejętność
-        korzystania z komputera i internetu w czasach społeczeństwa informacyjnego jest rodzajem elementarza. Ci, którzy
-        go nie znają, skazani są na wegetację na obrzeżach nowoczesnego świata. </p>
-        
-         <p>Dlatego od siedmiu lat pomagamy dzieciom zrozumieć i wykorzystywać zaawansowane technologie. Fundacja
-        Nowoczesna Polska tysiącom dzieci chce dać to, co najcenniejsze: wiedzę i umiejętności pozwalające rozumieć
-        współczesny świat i wykorzystywać możliwości, jakie on oferuje.</p>
-        
-         <p>Fundacja Nowoczesna Polska – poza projektem Wolne Lektury – koordynuje także projekt Wolne Podręczniki,
-        tworzony przez ruch społeczny nauczycieli wolontariuszy, którzy korzystając z Internetu piszą nowe podręczniki
-        dla polskich uczniów. Wolne Podręczniki są publikowane na wolnych licencjach – czyli takich, które zezwalają
-        każdemu na bezpłatne kopiowanie, rozpowszechnianie i aktualizowanie ich bez konieczności pytania o zgodę zespołu
-        autorów. Każdy nauczyciel będzie mógł te podręczniki uzupełniać, rozszerzać i poprawiać zgodnie z własnymi
-        potrzebami i doświadczeniem. Dzięki wolnym licencjom można poważnie obniżyć koszt podręczników wydawanych w
-        tradycyjnej postaci książek drukowanych na papierze, a podręczniki rozpowszechniane w formie elektronicznej będą
-        dostępne za darmo.</p>
-        
-        <h2>O domenie publicznej</h2>
-        
-        <p>W serwisie Wolne Lektury możemy zgodnie z prawem publikować tylko te książki, które należą do domeny
-        publicznej, a więc te, które wyszły już spod działania prawa autorskiego. Domena publiczna to rodzaj skarbca
-        kultury, wspólnego dobra, z którego wszyscy mogą korzystać na równych prawach, bez ograniczeń i opłat. Istnienie
-        domeny publicznej jest gwarantem dostępu do dóbr kultury, ten zaś jest naszym obywatelskim prawem zapisanym w
-        konstytucji.</p>
-        
-         <p>Choć zasadą jest, że po jakimś czasie wszystkie dzieła mają zasilić domenę publiczną, to czas działania
-        polskiego prawa autorskiego został w ciągu ostatnich kilkunastu lat znacznie wydłużony. Jeszcze na początku lat
-        90. było to 25 lat od śmierci autora, potem ten czas wydłużono na 50 lat, dziś jest to już lat 70. To oznacza,
-        że wielu pozycji z ministerialnych list lektur nie będziemy mogli udostępnić jeszcze przez wiele lat. Dopiero w
-        2020 roku opublikujemy utwory Marka Hłaski i Witolda Gombrowicza. Powieści Kuncewiczowej zasilą domenę publiczną
-        w roku 2060, a poezje Miłosza w 2075 roku.</p>
-        
-         <p>Ograniczenia prawa autorskiego odnoszą się także do wydań krytycznych i tłumaczeń. Do 1 stycznia 2012 roku
-        będziemy czekać na "Wielki Testament" Villona i inne utwory tłumaczone przez Boya-Żeleńskiego, a do 2068 roku na
-        „Kubusia Puchatka” Milne'a i inne przekłady Ireny Tuwim. Nie mamy także prawa upubliczniać wstępów i przedmów
-        napisanych przez autorów, którzy zmarli mniej niż 70 lat temu. Z tego właśnie powodu zdarza się czasem, że jakiś
-        utwór znajduje się na Wolnych Lekturach, ale link do odpowiadającej mu pozycji w Cyfrowej Bibliotece Narodowej
-        Polona odsyła do strony, która mówi, że utwór objęty jest ochroną prawa autorskiego. Znaczy to, że choć sam
-        tekst należy do domeny publicznej i dlatego umieszczamy go w Wolnych Lekturach, to jego wydanie krytyczne,
-        którym dysponuje CBN Polona (ze wszystkimi wstępami, przedmowami i komentarzami redaktorów), wciąż jest objęte
-        działaniem prawa autorskiego.</p>
-        
-         <p>O domenę publiczną należy dbać i otaczać ją ochroną. Kultura to misterny gmach, w którym kolejne piętra mogą
-        być budowane tylko na solidnych podstawach wypracowanych przez poprzedników. Kochanowski bezpośrednio czerpał z
-        tradycji antycznej. Mickiewicz twórczość ludową wykorzystał do stworzenia najpiękniejszych polskich wierszy.
-        Każde kolejne pokolenie może sięgać wzrokiem dalej, wypracowywać własny literacki język i trwale wpisywać się w
-        historię literatury tylko dzięki osiągnięciom poprzedników. Dlatego tak ważna jest wolność w udostępnianiu i
-        wykorzystywaniu najważniejszych dzieł polskiej i światowej literatury. Bez domeny publicznej zbudowanie tego
-        wspaniałego gmachu, jakim jest kultura, byłoby niemożliwe.</p>
+        {% chunk "about_us_right" %}
     </div>
 {% endblock %}
-
index edb229a..ebe45fe 100644 (file)
@@ -1,4 +1,5 @@
 {% extends "base.html" %}
+{% load chunks %}
 
 {% block title %}Możesz nam pomóc w WolneLektury.pl{% endblock %}
 
@@ -9,93 +10,9 @@
     </form>
 
     <div class="column-left">
-        <h2>Wolontariat</h2>
-
-        <p>Utwory włączane sukcesywnie do naszej biblioteki staramy się opracowywać jak najdokładniej. Jest to możliwe
-        tylko dzięki współpracującym z nami wolontariuszom.</p>
-        
-        <p>Zapraszamy wszystkie osoby, które chcą współtworzyć szkolną bibliotekę internetową Wolne Lektury.</p>
-        
-        <p>Wszystkim wolontariuszom oferujemy szkolenia i praktykę w zakresie edycji tekstów: redakcji technicznej i
-        merytorycznej. Wolontariusze mogą poznać problemy, ale i możliwości, jakie wiążą się z publikacjami internetowymi.
-        Współorganizują z nami szkolenia i konferencje, pomagają w przygotowaniu wersji tekstów do słuchania (tzw.
-        audiobooków). Wystawiamy umowy i zaświadczenia o tym, kiedy i jakie prace wykonywał wolontariusz na rzecz naszej
-        fundacji. Szkolenia, praktyka edytorska i potwierdzające je zaświadczenia mogą się okazać istotne w procesie
-        podnoszenia kwalifikacji i awansu zawodowego.</p>
-        
-        <h2>Co jest do zrobienia?</h2>
-        
-        <p>Najwięcej pracy mamy przy przygotowaniu lektur do publikacji. Z tekstu, który otrzymujemy z Biblioteki
-        Narodowej, należy usunąć literówki i inne mechaniczne błędy, a następnie opatrzyć tekst przypisami, pamiętając o
-        tym, że nasza oferta skierowana jest przede wszystkim do uczniów, dla których wiele słów i zwrotów będzie brzmiało
-        anachronicznie. Wydania dawniejsze (rygor wyznacza tu prawo autorskie) poddawane są koniecznym uwspółcześnieniom
-        językowym, np. w zakresie ortografii lub fleksji, przy czym pilnujemy, aby nie naruszać artystycznej swoistości
-        tekstu. Ostatnim etapem jest wyszukiwanie motywów i tematów literackich, mające pomóc przejrzeć literaturę "na
-        wskroś". We wszystkich tych pracach wspomagają nas wolontariusze – nauczyciele i studenci – często służąc również
-        radami i uwagami przy podejmowaniu ważnych decyzji.</p>
-        
-        <h2>Jak się do nas zgłosić?</h2>
-        
-        <p>Wszystkie zainteresowane osoby prosimy o przysłanie maila na adres <a href="mail:fundacja@nowoczesnapolska.org.pl">fundacja@nowoczesnapolska.org.pl</a>.</p>
-
-        <p>Zapraszamy także na <a href="http://redmine.nowoczesnapolska.org.pl/projects/wl-publikacje">stronę redakcji Wolnych Lektur</a>, na której znajdują się wszystkie niezbędne informacje o tym, jak włączyć się w prace redakcyjne.</p>
+        {% chunk "help_us_left" %}
     </div>
     <div class="column-right">
-        <h2>Najbardziej zasłużeni wolontariusze</h2>
-
-       <h3>Agatapaszkowska (współpracuje z nami od 15 marca 2008)</h3>
-
-        <p>m.in. opracowywała „W pustyni i w puszczy” Sienkiewicza ; zob. też na naszej stronie „wkład użytkownika”: <a href="http://wiki.wolnepodreczniki.pl/Specjalna:Wk%C5%82ad/Agatapaszkowska">http://wiki.wolnepodreczniki.pl/Specjalna:Wk%C5%82ad/Agatapaszkowska</a></p>
-
-
-       <h3>AgnieszkaKappa (współpracuje z nami od 16 marca 2008)</h3>
-
-        <p>m.in. brała udział w tworzeniu listy i opisie użycia motywów i tematów literackich; opracowywała teksty Sienkiewicza (Latarnik, Janko Muzykant, Quo vadis); zob. też na naszej stronie „wkład użytkownika”: <a href="http://wiki.wolnepodreczniki.pl/index.php?title=Specjalna:Wk%C5%82ad&amp;limit=500&amp;target=AgnieszkaKappa">http://wiki.wolnepodreczniki.pl/index.php?title=Specjalna:Wk%C5%82ad&amp;limit=500&amp;target=AgnieszkaKappa</a></p>
-
-
-        <h3>Anerys (współpracuje z nami od 5 lipca 2007)</h3>
-
-        <p>m.in. brała udział w tworzeniu listy motywów i tematów literackich, dyskusji o gatunkach literackich, opracowywała Bogurodzicę, „Fraszki” i „Treny” Kochanowskiego; wiersze Słowackiego (Grób Agamemnona), „Świętoszka” Moliere'a, czy poezje Kasprowicza (w tym hymn „Dies Irae”); zob. też na naszej stronie „wkład użytkownika”: <a href="http://wiki.wolnepodreczniki.pl/Specjalna:Wk%C5%82ad/Anerys">http://wiki.wolnepodreczniki.pl/Specjalna:Wk%C5%82ad/Anerys</a></p>
-
-
-       <h3>EmiliaZdankiewicz (współpracuje z nami od 17 marca 2008)</h3>
-
-        <p>m.in. motywy literackie dyskusja i opisywanie, Słowacki, Kordian; Konopnicka, Nasza szkapa; Mickiewicz, Dziady cz. III; zob. też na naszej stronie „wkład użytkownika”: <a href="http://wiki.wolnepodreczniki.pl/Specjalna:Wk%C5%82ad/EmiliaZdankiewicz">http://wiki.wolnepodreczniki.pl/Specjalna:Wk%C5%82ad/EmiliaZdankiewicz</a></p>
-
-
-       <h3>Ewa_Serafin (współpracuje z nami od 15 marca 2008)</h3>
-
-        <p>m.in. brała udział w tworzeniu listy i opisie użycia motywów i tematów literackich, opracowywała poezje Kasprowicza („Z wichrów i hal”, „Nad przepaściami”, „W ciemności schodzi moja dusza” oraz „Nad Niemnem” Orzeszkowej; zob. też na naszej stronie „wkład użytkownika”: <a href="http://wiki.wolnepodreczniki.pl/Specjalna:Wk%C5%82ad/Ewa_Serafi">http://wiki.wolnepodreczniki.pl/Specjalna:Wk%C5%82ad/Ewa_Serafi</a>n</p>
-
-
-       <h3>Hanna_Golab (współpracuje z nami od 15 marca 2008)</h3>
-
-        <p>opracowywała „Króla Edypa” Sofoklesa oraz „Dusiołka” Leśmiana; zob. też na naszej stronie „wkład użytkownika”: <a href="http://wiki.wolnepodreczniki.pl/Specjalna:Wk%C5%82ad/Hanna_Golab">http://wiki.wolnepodreczniki.pl/Specjalna:Wk%C5%82ad/Hanna_Golab</a></p>
-
-
-       <h3>Ingene (współpracuje z nami od 16 lipca 2008)</h3>
-
-        <p>m.in. opracowywała „Siłaczkę” Żeromskiego, „Quo vadis” Sienkiewicza oraz „Treny” Kochanowskiego; zob. też na naszej stronie „wkład użytkownika”: <a href="http://wiki.wolnepodreczniki.pl/Specjalna:Wk%C5%82ad/Ingene">http://wiki.wolnepodreczniki.pl/Specjalna:Wk%C5%82ad/Ingene</a></p>
-    
-       <h3>Jmyszkowska (współpracuje z nami od 26 marca 2008)</h3>
-
-        <p>m. in. opracowywała „Giaura” Byrona oraz „Quo vadis” Sienkiewicza; zob. też na naszej stronie „wkład użytkownika”: <a href="http://wiki.wolnepodreczniki.pl/Specjalna:Wk%C5%82ad/Jmyszkowska">http://wiki.wolnepodreczniki.pl/Specjalna:Wk%C5%82ad/Jmyszkowska</a></p>
-
-
-        <h3>Marysiabailey (współpracuje z nami od 1 października 2007)</h3>
-
-        <p>m.in. brała udział w tworzeniu listy motywów i tematów literackich oraz opisywaniu ich użycia; opracowywała „Chłopów” Reymonta, „Lalkę” Prusa, „Nie-Boską komedię” Krasińskiego, „Bajki” i „Satyry” Krasickiego, „Antygonę” Sofoklesa oraz utwory Kochanowskiego, Morsztyna, Mickiewicza, Kasprowicza, Goethego, Oppmana, Kasprowicza; zob. też na naszej stronie „wkład użytkownika”: <a href="http://wiki.wolnepodreczniki.pl/index.php?title=Specjalna:Wk%C5%82ad&amp;limit=500&amp;target=Marysiabailey">http://wiki.wolnepodreczniki.pl/index.php?title=Specjalna:Wk%C5%82ad&amp;limit=500&amp;target=Marysiabailey</a>; ponadto przygotowywała i prowadziła szkolenie dla wolontariuszy 15 marca 2008; zajmowała się proofreadingiem, a obecnie kieruje pracami nad audiobookami.</p>
-
-
-        <h3>Olga_Wojtczak (współpracuje z nami od 21 października 2008)</h3>
-
-        <p>m.in. opracowywała powieści Sienkiewicza (Trylogia), Reymonta, Żeromskiego, dramaty Shakespeare'a; zob. też na naszej stronie „wkład użytkownika”: <a href="http://wiki.wolnepodreczniki.pl/Specjalna:Wk%C5%82ad/Olga_Wojtczak">http://wiki.wolnepodreczniki.pl/Specjalna:Wk%C5%82ad/Olga_Wojtczak</a></p>
-
-
-        <h3>Renataml (współpracuje z nami od 30 czerwca 2007)</h3>
-
-        <p>brała udział w dyskusji na temat listy motywów, wspomagała nas radami jako doświadczona nauczycielka, edytowała „Lalkę” Prusa, „Balladynę” Słowackiego, „Siłaczkę” Żeromskiego.
-
-        <a href="http://wiki.wolnepodreczniki.pl/Specjalna:Wk%C5%82ad/Renataml">http://wiki.wolnepodreczniki.pl/Specjalna:Wk%C5%82ad/Renataml</a></p>
+        {% chunk "help_us_right" %}
     </div>
 {% endblock %}
diff --git a/wolnelektury/templates/info/join_us.html b/wolnelektury/templates/info/join_us.html
new file mode 100644 (file)
index 0000000..f3b3f1d
--- /dev/null
@@ -0,0 +1,7 @@
+<p>W serwisie Wolne Lektury już teraz opublikowanych jest ponad 1000 utworów!
+Pomóż w rozwijaniu biblioteki i uwalnianiu nowych lektur przekazując nam
+darowiznę lub 1% podatku. {% comment %}<a href='{}'>Więcej...</a>{% endcomment %}</p>
+
+<p>Zostań redaktorem lub redaktorką Wolnych Lektur! Sprawdź, czy obecnie
+pracujemy nad publikacją wyszukiwanej przez ciebie lektury i samodzielnie
+przygotuj publikację logując się na Platformie Redakcyjnej. <a href='{% url help_us %}'>Więcej...</a></p>
index b835671..e6253d7 100644 (file)
@@ -1,4 +1,5 @@
 {% extends "base.html" %}
+{% load chunks %}
 
 {% block title %}Wolontariat dla wiedzy w WolneLektury.pl{% endblock %}
 
     </form>
 
     <div id="volontariat" class="column-left">
-    <h2>Wolontariat</h2>
-       <p>
-               Biogramy pisarzy oraz definicje epok i gatunków literackich w
-               bibliotece internetowej Wolne Lektury napisali uczniowie
-               czterech szkół podczas warsztatów „Wolontariat dla wiedzy”.
-       </p>
-
-       <p>
-               Uczniowie pracowali wspólnie pod merytorycznym nadzorem
-               nauczycieli i specjalistów – literaturoznawców na
-               <a href="http://wolnepodreczniki.pl/wolontariat/">
-                       platformie internetowej wiki</a>
-               przygotowanej przez zespół
-               <a href="http://2ia.pl/">2ia</a>. Praca nad notami była jednocześnie nauką wykorzystywania
-               komputerów i internetu do wyszukiwania informacji, a także
-               weryfikowania ich wiarygodności.
-       </p>
-
-       <p>
-               Projekt "Wolontariat dla wiedzy" został zrealizowany dzięki
-               wsparciu
-               <a href="http://www.menis.gov.pl/">
-                       Ministerstwa Edukacji Narodowej</a>. Nagrody dla uczniów ufundowali
-               <a href="http://www.nk.com.pl/engine/index.php?page=glowna">
-                       Wydawnictwo Nasza Księgarnia</a>
-               i
-               <a href="http://helion.pl/">Wydawnictwo Helion</a>. Noty zweryfikowano w serwisie
-               <a href="http://www.plagiat.pl/webplagiat/main.action">
-                       Plagiat.pl</a>.
-       </p>
-
-       <p>
-               <a href="/static/wolontariat.pdf">Raport z realizacji projektu „Wolontariat dla wiedzy"</a>.
-       </p>
-       <p>
-       <img src="/static/img/wolontariat/okladka.jpg" style='margin: 10px' />
-       <img src="/static/img/wolontariat/01.jpg" style='margin: 10px' />
-       <img src="/static/img/wolontariat/02.jpg" style='margin: 10px' />
-    <img src="/static/img/wolontariat/03.jpg" style='margin: 10px' />
-    <img src="/static/img/wolontariat/04.jpg" style='margin: 10px' />
-       </p>
-       
+        {% chunk "voluntary_services_left" %}
     </div>
-    
-    
     <div class="column-right">
-       <h2>Autorzy</h2>
-
-       <ol>
-               <li>
-                       <p>
-                               Gimnazjum nr 40 z Oddziałami Integracyjnymi w Zespole
-                               Szkół nr 69 im. Armii Krajowej Grupy Bojowej „KRYBAR”,
-                               ul. Drewniana 8, 00-345 Warszawa; klasa druga pod opieką
-                               Anny Budziarek-Friedrich:
-                       </p>
-
-                       <ul>
-                               <li>Burdon Filip;</li>
-                               <li>Dębski Bartek;</li>
-                               <li>Jackowicz Kamil;</li>
-                               <li>Kurek Paweł;</li>
-                               <li>Makles Bartosz;</li>
-                               <li>Markiewicz Jeremiasz;</li>
-                               <li>Migdał Katarzyna;</li>
-                               <li>Mioduszewski Michał;</li>
-                               <li>Pfeiffer Ida;</li>
-                               <li>Płaskowicka Karolina;</li>
-                               <li>Sailer Ewa.</li>
-                       </ul>
-               </li>
-
-               <li>
-                       <p>
-                               XXXIII Liceum Ogólnokształcące im. Mikołaja Kopernika,
-                               ul. Bema 76, 01-225 Warszawa; klasa pierwsza o profilu
-                               biologiczno-chemicznym pod opieką Elżbiety Konkowskiej:
-                       </p>
-
-                       <ul>
-                               <li>Chwil Bartłomiej;</li>
-                               <li>Czarnecka Natalia;</li>
-                               <li>Gawrońska Iga;</li>
-                               <li>Grabarczyk Marta;</li>
-                               <li>Jastrząb Katarzyna;</li>
-                               <li>Krawczak Olga;</li>
-                               <li>Krawczyk Marianna;</li>
-                               <li>Kur Natalia;</li>
-                               <li>Kwiatek Marta;</li>
-                               <li>Laśkiewicz Joanna;</li>
-                               <li>Machczyńska Daria;</li>
-                               <li>Miecznikowska Izabela;</li>
-                               <li>Moczulska Karolina;</li>
-                               <li>Mościcka Aneta;</li>
-                               <li>Narloch Sabina;</li>
-                               <li>Przybysz Paweł;</li>
-                               <li>Puchta Marek;</li>
-                               <li>Ryska Cezary;</li>
-                               <li>Sandomierski Bartłomiej;</li>
-                               <li>Sławiński Tomasz;</li>
-                               <li>Słowik Olga;</li>
-                               <li>Starzycka Katarzyna;</li>
-                               <li>Strzelczak Karolina;</li>
-                               <li>Szafran Danuta;</li>
-                               <li>Szmigielska Magdalena;</li>
-                               <li>Szulkowska Alicja;</li>
-                               <li>Ślusarczyk Anna;</li>
-                               <li>Tytkowska Anna.</li>
-                       </ul>
-                       <li>
-                               <p>
-                                       Autorskie Niepubliczne Liceum Ogólnokształcące nr
-                                       42, ul. Iwicka 47 B, 00-735 Warszawa; klasa pierwsza
-                                       o profilu ogólnym pod opieką Michała Friedricha:
-                               </p>
-
-                               <ul>
-                                       <li>Baraniecka Marta;</li>
-                                       <li>Gołaszewska Ewa;</li>
-                                       <li>Kwiatkowski Michał;</li>
-                                       <li>Machnikowska Monika;</li>
-                                       <li>Pietrzak Bartosz;</li>
-                                       <li>Przespolewski Przemysław;</li>
-                                       <li>Rosińska Zuzanna;</li>
-                                       <li>Sibiga Magdalena.</li>
-                               </ul>
-                       </li>
-                       <li>
-                               <p>
-                                       LXIV Liceum Ogólnokształcące im. St. I. Witkiewicza,
-                                       ul. Elbląska 51, 01-737 Warszawa; klasa pierwsza o
-                                       profilu ogólnym pod opieką Daniela Zycha:
-                               </p>
-
-                               <ul>
-                                       <li>Andrzejczak Kamil;</li>
-                                       <li>Czubaj Konrad;</li>
-                                       <li>Drągowska Katarzyna;</li>
-                                       <li>Gajewska Magdalena;</li>
-                                       <li>Głowacki Jan;</li>
-                                       <li>Grad Paweł;</li>
-                                       <li>Hnatowski Bartek;</li>
-                                       <li>Karwowski Marcin;</li>
-                                       <li>Kłos Aneta;</li>
-                                       <li>Kozieł Barbara;</li>
-                                       <li>Kozłowska Anna;</li>
-                                       <li>Krug Pamela;</li>
-                                       <li>Krzosek Jakub;</li>
-                                       <li>Lubaś Michał;</li>
-                                       <li>Masewicz Natalia;</li>
-                                       <li>Mastalerz Agnieszka;</li>
-                                       <li>Modelska Marta;</li>
-                                       <li>Nowak Aleksandra;</li>
-                                       <li>Pabian Agnieszka;</li>
-                                       <li>Paszkowska Aleksandra;</li>
-                                       <li>Pielat Zofia;</li>
-                                       <li>Poniecka Agnieszka;</li>
-                                       <li>Pytlak Urszula;</li>
-                                       <li>Rosa Karolina;</li>
-                                       <li>Smyczyńska Kamila;</li>
-                                       <li>Stolińska Barbara;</li>
-                                       <li>Szymańska Katarzyna;</li>
-                                       <li>Ścibior Ewa;</li>
-                                       <li>Witczak Magda;</li>
-                                       <li>Witkowska Justyna;</li>
-                                       <li>Wyrzykowska Jowita;</li>
-                                       <li>Ziółkowska Adrianna.</li>
-                               </ul>
-                       </li>
-         </li>
-         </ol>
-         </div>
+        {% chunk "voluntary_services_right" %}
+    </div>
 {% endblock %}
 
index 93bccb3..699b50f 100644 (file)
@@ -1,5 +1,6 @@
+{% load i18n %}
 <div>
-    <p class="download"><a href="{{ object.file.url }}">Pobierz</a> {% if object.author %}(autor: {{ object.author }}){% endif %}</p>
+    <p class="download"><a href="{{ object.file.url }}">{% trans "Download" %}</a> {% if object.author %}({% trans "author" %}: {{ object.author }}){% endif %}</p>
     <h2>{{ object.title }}</h2>
     {% if object.slideshare_id %}
     <object style="margin:0px" width="480" height="400">
index 9ee604e..afc3d69 100644 (file)
@@ -1,15 +1,16 @@
 {% extends 'base.html' %}
+{% load i18n %}
 
 {% block title %}{{ object.title }} w WolneLektury.pl{% endblock %}
 
 {% block body %}
     <h1>{{ object.title }}</h1>
     <form action="{% url search %}" method="GET" accept-charset="utf-8" id="search-form">
-        <p>{{ form.q }} <input type="submit" value="Szukaj" /> <strong>lub</strong> <a href="{% url lessons_document_list %}">wróć do listy materiałów</a></p>
+        <p>{{ form.q }} <input type="submit" value="{% trans "Search" %}" /> <strong>{% trans "or" %}</strong> <a href="{% url lessons_document_list %}">{% trans "return to list of materials" %}</a></p>
     </form>
     
     <div id="document-detail">
-        <p class="download"><a href="{{ object.file.url }}">Pobierz</a> {% if object.author %}(autor: {{ object.author }}){% endif %}</p>
+        <p class="download"><a href="{{ object.file.url }}">{% trans "Download" %}</a> {% if object.author %}({% trans "author" %}: {{ object.author }}){% endif %}</p>
         {% if object.slideshare_id %}
         <object style="margin:0px" width="480" height="400">
             <param name="movie" value="http://static.slidesharecdn.com/swf/{{ object.slideshare_player }}?doc={{ object.slideshare_id }}&rel=0&stripped_title={{ object.name|slugify }}" /><param name="allowFullScreen" value="true"/><param name="allowScriptAccess" value="always"/><embed src="http://static.slidesharecdn.com/swf/{{ object.slideshare_player }}?doc={{ object.slideshare_id }}&rel=0&stripped_title={{ object.name|slugify }}" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="480" height="400"></embed></object>
index 1a5ab01..79ba80d 100644 (file)
@@ -1,9 +1,10 @@
 {% extends "base.html" %}
+{% load i18n %}
 {% load catalogue_tags chunks %}
 
 {% block bodyid %}document-list-body{% endblock %}
 
-{% block title %}Materiały pomocnicze dla nauczycieli w WolneLektury.pl{% endblock %}
+{% block title %}{% trans "Hand-outs for teachers on " %}WolneLektury.pl{% endblock %}
 
 {% block extrahead %}
     <script type="text/javascript" charset="utf-8">
@@ -45,9 +46,9 @@
     </script>
 {% endblock extrahead %}
 {% block body %}
-    <h1>Materiały pomocnicze dla nauczycieli</h1>
+    <h1>{% trans "Hand-outs for teachers" %}</h1>
     <form action="{% url search %}" method="GET" accept-charset="utf-8" id="search-form">
-        <p>{{ form.q }} <input type="submit" value="Szukaj" /> <strong>lub</strong> <a href="{% url main_page %}">wróć do strony głównej</a></p>
+        <p>{{ form.q }} <input type="submit" value="{% trans "Search" %}" /> <strong>{% trans "or" %}</strong> <a href="{% url main_page %}">{% trans "return to main page" %}</a></p>
     </form>
     
     <div id="document-list">
index 36c297b..7489d4a 100644 (file)
@@ -1,9 +1,10 @@
+{% load i18n %}
 {% if is_paginated %}
 <div class="pagination">
     {% if page_obj.has_previous %}
-        <a href="?page={{ page_obj.previous_page_number }}{{ getvars }}" class="prev">&lsaquo;&lsaquo; poprzedni</a>
+        <a href="?page={{ page_obj.previous_page_number }}{{ getvars }}" class="prev">&lsaquo;&lsaquo; {% trans "previous" %}</a>
     {% else %}
-        <span class="disabled prev">&lsaquo;&lsaquo; poprzedni</span>
+        <span class="disabled prev">&lsaquo;&lsaquo; {% trans "previous" %}</span>
     {% endif %}
     {% for page in pages %}
         {% if page %}
@@ -17,9 +18,9 @@
         {% endif %}
     {% endfor %}
     {% if page_obj.has_next %}
-        <a href="?page={{ page_obj.next_page_number }}{{ getvars }}" class="next">następny &rsaquo;&rsaquo;</a>
+        <a href="?page={{ page_obj.next_page_number }}{{ getvars }}" class="next">{% trans "next" %} &rsaquo;&rsaquo;</a>
     {% else %}
-        <span class="disabled next">następny &rsaquo;&rsaquo;</span>
+        <span class="disabled next">{% trans "next" %} &rsaquo;&rsaquo;</span>
     {% endif %}
 </div>
 {% endif %}
index 4f9d4ed..a7cd45f 100644 (file)
@@ -48,4 +48,10 @@ urlpatterns = patterns('',
     url(r'^%s(?P<path>.*)$' % settings.STATIC_URL[1:], 'django.views.static.serve',
         {'document_root': settings.STATIC_ROOT, 'show_indexes': True}),
     url(r'^$', 'django.views.generic.simple.redirect_to', {'url': 'katalog/'}),
+    url(r'^i18n/', include('django.conf.urls.i18n')),    
 )
+
+if 'rosetta' in settings.INSTALLED_APPS:
+    urlpatterns += patterns('',
+        url(r'^rosetta/', include('rosetta.urls')),
+    )
diff --git a/wolnelektury/wolnelektury.fcgi b/wolnelektury/wolnelektury.fcgi
deleted file mode 100644 (file)
index 5115758..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/usr/bin/env python
-import os
-from os.path import abspath, dirname, join
-import sys
-
-# Redirect sys.stdout to sys.stderr for bad libraries like geopy that use
-# print statements for optional import exceptions.
-sys.stdout = sys.stderr
-
-# Add apps and lib directories to PYTHONPATH
-sys.path.insert(0, abspath(join(dirname(__file__), '../apps')))
-sys.path.insert(0, abspath(join(dirname(__file__), '../lib')))
-
-# Emulate manage.py path hacking.
-sys.path.insert(0, abspath(join(dirname(__file__), "../")))
-sys.path.insert(0, abspath(join(dirname(__file__), ".")))
-
-# Run Django
-os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
-
-from django.core.servers.fastcgi import runfastcgi
-runfastcgi(method='threaded', daemonize='false')
-
diff --git a/wolnelektury/wolnelektury.wsgi b/wolnelektury/wolnelektury.wsgi
deleted file mode 100644 (file)
index acafede..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/usr/bin/env python
-import os
-from os.path import abspath, dirname, join
-import sys
-
-# Redirect sys.stdout to sys.stderr for bad libraries like geopy that use
-# print statements for optional import exceptions.
-sys.stdout = sys.stderr
-
-# Add apps and lib directories to PYTHONPATH
-sys.path.insert(0, abspath(join(dirname(__file__), '../apps')))
-sys.path.insert(0, abspath(join(dirname(__file__), '../lib')))
-
-# Emulate manage.py path hacking.
-sys.path.insert(0, abspath(join(dirname(__file__), "../")))
-sys.path.insert(0, abspath(join(dirname(__file__), ".")))
-
-# Run Django
-os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
-
-from django.core.handlers.wsgi import WSGIHandler
-application = WSGIHandler()
-