Rearrange source to src dir.
authorRadek Czajka <rczajka@rczajka.pl>
Wed, 27 Feb 2019 22:15:37 +0000 (23:15 +0100)
committerRadek Czajka <rczajka@rczajka.pl>
Wed, 27 Feb 2019 22:15:37 +0000 (23:15 +0100)
757 files changed:
README.md
apps/apiclient/__init__.py [deleted file]
apps/apiclient/migrations/0001_initial.py [deleted file]
apps/apiclient/migrations/0002_auto__add_field_oauthconnection_beta.py [deleted file]
apps/apiclient/migrations/0003_auto__chg_field_oauthconnection_user__del_unique_oauthconnection_user.py [deleted file]
apps/apiclient/migrations/__init__.py [deleted file]
apps/apiclient/models.py [deleted file]
apps/apiclient/settings.py [deleted file]
apps/apiclient/urls.py [deleted file]
apps/apiclient/views.py [deleted file]
apps/catalogue/__init__.py [deleted file]
apps/catalogue/admin.py [deleted file]
apps/catalogue/constants.py [deleted file]
apps/catalogue/ebook_utils.py [deleted file]
apps/catalogue/feeds.py [deleted file]
apps/catalogue/fixtures/stages.json [deleted file]
apps/catalogue/forms.py [deleted file]
apps/catalogue/helpers.py [deleted file]
apps/catalogue/locale/pl/LC_MESSAGES/django.mo [deleted file]
apps/catalogue/locale/pl/LC_MESSAGES/django.po [deleted file]
apps/catalogue/management/__init__.py [deleted file]
apps/catalogue/management/commands/__init__.py [deleted file]
apps/catalogue/management/commands/add_parent.py [deleted file]
apps/catalogue/management/commands/assign_from_redmine.py [deleted file]
apps/catalogue/management/commands/fixdc.py [deleted file]
apps/catalogue/management/commands/import_wl.py [deleted file]
apps/catalogue/management/commands/insert_isbn.py [deleted file]
apps/catalogue/management/commands/mark_final.py [deleted file]
apps/catalogue/management/commands/merge_books.py [deleted file]
apps/catalogue/management/commands/prune_audience.py [deleted file]
apps/catalogue/managers.py [deleted file]
apps/catalogue/migrations/0001_initial.py [deleted file]
apps/catalogue/migrations/0002_stages.py [deleted file]
apps/catalogue/migrations/0003_from_hg.py [deleted file]
apps/catalogue/migrations/0004_fix_revisions.py [deleted file]
apps/catalogue/migrations/0005_auto__add_field_chunk_gallery_start.py [deleted file]
apps/catalogue/migrations/0006_auto__add_field_book_public.py [deleted file]
apps/catalogue/migrations/0007_auto__add_field_book_dc_slug.py [deleted file]
apps/catalogue/migrations/0008_auto.py [deleted file]
apps/catalogue/migrations/0009_auto__add_field_book__on_track.py [deleted file]
apps/catalogue/migrations/0010_auto__add_field_book_dc_cover_image.py [deleted file]
apps/catalogue/migrations/0011_auto__add_project__add_field_book_project.py [deleted file]
apps/catalogue/migrations/0012_auto__add_imagepublishrecord__add_imagechange__add_unique_imagechange_.py [deleted file]
apps/catalogue/migrations/0013_auto__add_field_image_project.py [deleted file]
apps/catalogue/migrations/__init__.py [deleted file]
apps/catalogue/models/__init__.py [deleted file]
apps/catalogue/models/book.py [deleted file]
apps/catalogue/models/chunk.py [deleted file]
apps/catalogue/models/image.py [deleted file]
apps/catalogue/models/listeners.py [deleted file]
apps/catalogue/models/project.py [deleted file]
apps/catalogue/models/publish_log.py [deleted file]
apps/catalogue/signals.py [deleted file]
apps/catalogue/tasks.py [deleted file]
apps/catalogue/templates/catalogue/active_users_list.html [deleted file]
apps/catalogue/templates/catalogue/activity.html [deleted file]
apps/catalogue/templates/catalogue/base.html [deleted file]
apps/catalogue/templates/catalogue/book_append_to.html [deleted file]
apps/catalogue/templates/catalogue/book_detail.html [deleted file]
apps/catalogue/templates/catalogue/book_edit.html [deleted file]
apps/catalogue/templates/catalogue/book_html.html [deleted file]
apps/catalogue/templates/catalogue/book_list/book.html [deleted file]
apps/catalogue/templates/catalogue/book_list/book_list.html [deleted file]
apps/catalogue/templates/catalogue/book_list/chunk.html [deleted file]
apps/catalogue/templates/catalogue/book_text.html [deleted file]
apps/catalogue/templates/catalogue/chunk_add.html [deleted file]
apps/catalogue/templates/catalogue/chunk_edit.html [deleted file]
apps/catalogue/templates/catalogue/document_create_missing.html [deleted file]
apps/catalogue/templates/catalogue/document_list.html [deleted file]
apps/catalogue/templates/catalogue/document_upload.html [deleted file]
apps/catalogue/templates/catalogue/image_detail.html [deleted file]
apps/catalogue/templates/catalogue/image_list.html [deleted file]
apps/catalogue/templates/catalogue/image_short.html [deleted file]
apps/catalogue/templates/catalogue/image_table.html [deleted file]
apps/catalogue/templates/catalogue/main_tabs.html [deleted file]
apps/catalogue/templates/catalogue/mark_final.html [deleted file]
apps/catalogue/templates/catalogue/mark_final_completed.html [deleted file]
apps/catalogue/templates/catalogue/my_page.html [deleted file]
apps/catalogue/templates/catalogue/upload_pdf.html [deleted file]
apps/catalogue/templates/catalogue/user_list.html [deleted file]
apps/catalogue/templates/catalogue/user_page.html [deleted file]
apps/catalogue/templates/catalogue/wall.html [deleted file]
apps/catalogue/templatetags/__init__.py [deleted file]
apps/catalogue/templatetags/book_list.py [deleted file]
apps/catalogue/templatetags/catalogue.py [deleted file]
apps/catalogue/templatetags/common_tags.py [deleted file]
apps/catalogue/templatetags/set_get_parameter.py [deleted file]
apps/catalogue/templatetags/wall.py [deleted file]
apps/catalogue/test_utils.py [deleted file]
apps/catalogue/tests/__init__.py [deleted file]
apps/catalogue/tests/book.py [deleted file]
apps/catalogue/tests/files/chunk1.xml [deleted file]
apps/catalogue/tests/files/chunk2.xml [deleted file]
apps/catalogue/tests/files/expected.xml [deleted file]
apps/catalogue/tests/gallery.py [deleted file]
apps/catalogue/tests/publish.py [deleted file]
apps/catalogue/tests/xml_updater.py [deleted file]
apps/catalogue/urls.py [deleted file]
apps/catalogue/views.py [deleted file]
apps/catalogue/xml_tools.py [deleted file]
apps/cover/__init__.py [deleted file]
apps/cover/forms.py [deleted file]
apps/cover/locale/pl/LC_MESSAGES/django.mo [deleted file]
apps/cover/locale/pl/LC_MESSAGES/django.po [deleted file]
apps/cover/management/__init__.py [deleted file]
apps/cover/management/commands/__init__.py [deleted file]
apps/cover/management/commands/refresh_covers.py [deleted file]
apps/cover/migrations/0001_initial.py [deleted file]
apps/cover/migrations/0002_auto__chg_field_image_download_url.py [deleted file]
apps/cover/migrations/0003_auto__chg_field_image_source_url.py [deleted file]
apps/cover/migrations/__init__.py [deleted file]
apps/cover/models.py [deleted file]
apps/cover/templates/cover/add_image.html [deleted file]
apps/cover/templates/cover/image_detail.html [deleted file]
apps/cover/templates/cover/image_list.html [deleted file]
apps/cover/tests.py [deleted file]
apps/cover/urls.py [deleted file]
apps/cover/utils.py [deleted file]
apps/cover/views.py [deleted file]
apps/dvcs/__init__.py [deleted file]
apps/dvcs/locale/pl/LC_MESSAGES/django.mo [deleted file]
apps/dvcs/locale/pl/LC_MESSAGES/django.po [deleted file]
apps/dvcs/models.py [deleted file]
apps/dvcs/signals.py [deleted file]
apps/dvcs/storage.py [deleted file]
apps/dvcs/tests/__init__.py [deleted file]
apps/email_mangler/__init__.py [deleted file]
apps/email_mangler/locale/pl/LC_MESSAGES/django.mo [deleted file]
apps/email_mangler/locale/pl/LC_MESSAGES/django.po [deleted file]
apps/email_mangler/models.py [deleted file]
apps/email_mangler/templatetags/__init__.py [deleted file]
apps/email_mangler/templatetags/email.py [deleted file]
apps/fileupload/__init__.py [deleted file]
apps/fileupload/forms.py [deleted file]
apps/fileupload/locale/pl/LC_MESSAGES/django.mo [deleted file]
apps/fileupload/locale/pl/LC_MESSAGES/django.po [deleted file]
apps/fileupload/models.py [deleted file]
apps/fileupload/static/fileupload/css/bootstrap-image-gallery.min.css [deleted file]
apps/fileupload/static/fileupload/css/bootstrap.min.css [deleted file]
apps/fileupload/static/fileupload/css/jquery.fileupload-ui.css [deleted file]
apps/fileupload/static/fileupload/css/style.css [deleted file]
apps/fileupload/static/fileupload/img/glyphicons-halflings-white.png [deleted file]
apps/fileupload/static/fileupload/img/glyphicons-halflings.png [deleted file]
apps/fileupload/static/fileupload/img/loading.gif [deleted file]
apps/fileupload/static/fileupload/img/progressbar.gif [deleted file]
apps/fileupload/static/fileupload/js/bootstrap-image-gallery.min.js [deleted file]
apps/fileupload/static/fileupload/js/bootstrap.min.js [deleted file]
apps/fileupload/static/fileupload/js/canvas-to-blob.min.js [deleted file]
apps/fileupload/static/fileupload/js/csrf.js [deleted file]
apps/fileupload/static/fileupload/js/jquery.fileupload-fp.js [deleted file]
apps/fileupload/static/fileupload/js/jquery.fileupload-ui.js [deleted file]
apps/fileupload/static/fileupload/js/jquery.fileupload.js [deleted file]
apps/fileupload/static/fileupload/js/jquery.iframe-transport.js [deleted file]
apps/fileupload/static/fileupload/js/jquery.ui.widget.js [deleted file]
apps/fileupload/static/fileupload/js/load-image.min.js [deleted file]
apps/fileupload/static/fileupload/js/locale-en.js [deleted file]
apps/fileupload/static/fileupload/js/locale-pl.js [deleted file]
apps/fileupload/static/fileupload/js/main.js [deleted file]
apps/fileupload/static/fileupload/js/tmpl.min.js [deleted file]
apps/fileupload/templates/fileupload/picture_form.html [deleted file]
apps/fileupload/templatetags/__init__.py [deleted file]
apps/fileupload/templatetags/upload_tags.py [deleted file]
apps/fileupload/urls.py [deleted file]
apps/fileupload/views.py [deleted file]
apps/toolbar/__init__.py [deleted file]
apps/toolbar/admin.py [deleted file]
apps/toolbar/fixtures/initial_toolbar.yaml [deleted file]
apps/toolbar/locale/pl/LC_MESSAGES/django.mo [deleted file]
apps/toolbar/locale/pl/LC_MESSAGES/django.po [deleted file]
apps/toolbar/management/__init__.py [deleted file]
apps/toolbar/management/commands/__init__.py [deleted file]
apps/toolbar/management/commands/fixbuttons.py [deleted file]
apps/toolbar/migrations/0001_initial.py [deleted file]
apps/toolbar/migrations/0002_auto__del_field_button_key_mod__chg_field_button_key.py [deleted file]
apps/toolbar/migrations/0003_button_key_rename_to_accesskey.py [deleted file]
apps/toolbar/migrations/0004_auto__chg_field_button_accesskey.py [deleted file]
apps/toolbar/migrations/0005_initial_data.py [deleted file]
apps/toolbar/migrations/__init__.py [deleted file]
apps/toolbar/models.py [deleted file]
apps/toolbar/templates/toolbar/button.html [deleted file]
apps/toolbar/templates/toolbar/toolbar.html [deleted file]
apps/toolbar/templatetags/__init__.py [deleted file]
apps/toolbar/templatetags/toolbar_tags.py [deleted file]
apps/wiki/__init__.py [deleted file]
apps/wiki/admin.py [deleted file]
apps/wiki/fixtures/initial_themes.yaml [deleted file]
apps/wiki/forms.py [deleted file]
apps/wiki/helpers.py [deleted file]
apps/wiki/locale/pl/LC_MESSAGES/django.mo [deleted file]
apps/wiki/locale/pl/LC_MESSAGES/django.po [deleted file]
apps/wiki/migrations/0001_initial.py [deleted file]
apps/wiki/migrations/0002_auto__add_theme.py [deleted file]
apps/wiki/migrations/__init__.py [deleted file]
apps/wiki/models.py [deleted file]
apps/wiki/nice_diff.py [deleted file]
apps/wiki/settings.py [deleted file]
apps/wiki/templates/admin/wiki/theme/change_list.html [deleted file]
apps/wiki/templates/wiki/diff_table.html [deleted file]
apps/wiki/templates/wiki/document_details.html [deleted file]
apps/wiki/templates/wiki/document_details_base.html [deleted file]
apps/wiki/templates/wiki/document_details_readonly.html [deleted file]
apps/wiki/templates/wiki/pubmark_dialog.html [deleted file]
apps/wiki/templates/wiki/revert_dialog.html [deleted file]
apps/wiki/templates/wiki/save_dialog.html [deleted file]
apps/wiki/templates/wiki/tabs/annotations_view.html [deleted file]
apps/wiki/templates/wiki/tabs/annotations_view_item.html [deleted file]
apps/wiki/templates/wiki/tabs/gallery_view.html [deleted file]
apps/wiki/templates/wiki/tabs/gallery_view_item.html [deleted file]
apps/wiki/templates/wiki/tabs/history_view.html [deleted file]
apps/wiki/templates/wiki/tabs/history_view_item.html [deleted file]
apps/wiki/templates/wiki/tabs/search_view.html [deleted file]
apps/wiki/templates/wiki/tabs/search_view_item.html [deleted file]
apps/wiki/templates/wiki/tabs/source_editor.html [deleted file]
apps/wiki/templates/wiki/tabs/source_editor_item.html [deleted file]
apps/wiki/templates/wiki/tabs/summary_view.html [deleted file]
apps/wiki/templates/wiki/tabs/summary_view_item.html [deleted file]
apps/wiki/templates/wiki/tabs/wysiwyg_editor.html [deleted file]
apps/wiki/templates/wiki/tabs/wysiwyg_editor_item.html [deleted file]
apps/wiki/tests/xslt/auto/auto_test.js [deleted file]
apps/wiki/tests/xslt/auto/data/akap.html [deleted file]
apps/wiki/tests/xslt/auto/data/big.xml [deleted file]
apps/wiki/tests/xslt/auto/data/unknown_tag.html [deleted file]
apps/wiki/tests/xslt/auto/data/unknown_tag.xml [deleted file]
apps/wiki/urls.py [deleted file]
apps/wiki/views.py [deleted file]
apps/wiki_img/__init__.py [deleted file]
apps/wiki_img/forms.py [deleted file]
apps/wiki_img/locale/pl/LC_MESSAGES/django.mo [deleted file]
apps/wiki_img/locale/pl/LC_MESSAGES/django.po [deleted file]
apps/wiki_img/models.py [deleted file]
apps/wiki_img/templates/wiki_img/diff_table.html [deleted file]
apps/wiki_img/templates/wiki_img/document_details.html [deleted file]
apps/wiki_img/templates/wiki_img/document_details_base.html [deleted file]
apps/wiki_img/templates/wiki_img/document_details_readonly.html [deleted file]
apps/wiki_img/templates/wiki_img/tabs/history_view.html [deleted file]
apps/wiki_img/templates/wiki_img/tabs/motifs_editor.html [deleted file]
apps/wiki_img/templates/wiki_img/tabs/motifs_editor_item.html [deleted file]
apps/wiki_img/templates/wiki_img/tabs/objects_editor.html [deleted file]
apps/wiki_img/templates/wiki_img/tabs/objects_editor_item.html [deleted file]
apps/wiki_img/templates/wiki_img/tabs/source_editor.html [deleted file]
apps/wiki_img/templates/wiki_img/tabs/source_editor_item.html [deleted file]
apps/wiki_img/templates/wiki_img/tabs/summary_view.html [deleted file]
apps/wiki_img/templates/wiki_img/tabs/summary_view_item.html [deleted file]
apps/wiki_img/tests.py [deleted file]
apps/wiki_img/urls.py [deleted file]
apps/wiki_img/views.py [deleted file]
manage.py [deleted file]
redakcja/__init__.py [deleted file]
redakcja/celery.py [deleted file]
redakcja/context_processors.py [deleted file]
redakcja/locale/pl/LC_MESSAGES/django.mo [deleted file]
redakcja/locale/pl/LC_MESSAGES/django.po [deleted file]
redakcja/localsettings.sample [deleted file]
redakcja/settings/__init__.py [deleted file]
redakcja/settings/common.py [deleted file]
redakcja/settings/compress.py [deleted file]
redakcja/settings/integration_test.py [deleted file]
redakcja/settings/test.py [deleted file]
redakcja/static/contextmenu/images/cut.png [deleted file]
redakcja/static/contextmenu/images/door.png [deleted file]
redakcja/static/contextmenu/images/page_white_copy.png [deleted file]
redakcja/static/contextmenu/images/page_white_delete.png [deleted file]
redakcja/static/contextmenu/images/page_white_edit.png [deleted file]
redakcja/static/contextmenu/images/page_white_paste.png [deleted file]
redakcja/static/contextmenu/index.html [deleted file]
redakcja/static/contextmenu/jquery.contextMenu.css [deleted file]
redakcja/static/contextmenu/jquery.contextMenu.js [deleted file]
redakcja/static/contextmenu/jquery.ui.position.js [deleted file]
redakcja/static/contextmenu2/images/cut.png [deleted file]
redakcja/static/contextmenu2/images/door.png [deleted file]
redakcja/static/contextmenu2/images/page_white_copy.png [deleted file]
redakcja/static/contextmenu2/images/page_white_delete.png [deleted file]
redakcja/static/contextmenu2/images/page_white_edit.png [deleted file]
redakcja/static/contextmenu2/images/page_white_paste.png [deleted file]
redakcja/static/contextmenu2/index.html [deleted file]
redakcja/static/contextmenu2/jquery.contextMenu.css [deleted file]
redakcja/static/contextmenu2/jquery.contextMenu.js [deleted file]
redakcja/static/css/book.css [deleted file]
redakcja/static/css/book_list.css [deleted file]
redakcja/static/css/dialogs.css [deleted file]
redakcja/static/css/filelist.css [deleted file]
redakcja/static/css/gallery.css [deleted file]
redakcja/static/css/history.css [deleted file]
redakcja/static/css/html.css [deleted file]
redakcja/static/css/html_print.css [deleted file]
redakcja/static/css/imgareaselect-default.css [deleted file]
redakcja/static/css/jquery.autocomplete.css [deleted file]
redakcja/static/css/master.css [deleted file]
redakcja/static/css/summary.css [deleted file]
redakcja/static/css/toolbar.css [deleted file]
redakcja/static/css/xmlcolors_20100906.css [deleted file]
redakcja/static/email_mangler/email_mangler.js [deleted file]
redakcja/static/icons/clock.png [deleted file]
redakcja/static/icons/close.png [deleted file]
redakcja/static/icons/go-home.png [deleted file]
redakcja/static/icons/go-next.png [deleted file]
redakcja/static/icons/go-previous.png [deleted file]
redakcja/static/icons/image-x-generic.png [deleted file]
redakcja/static/icons/ok.png [deleted file]
redakcja/static/icons/revert.png [deleted file]
redakcja/static/icons/revert_.png [deleted file]
redakcja/static/icons/stop.png [deleted file]
redakcja/static/icons/system-search.png [deleted file]
redakcja/static/icons/user.png [deleted file]
redakcja/static/icons/zoom.png [deleted file]
redakcja/static/icons/zoom_in.png [deleted file]
redakcja/static/icons/zoom_out.png [deleted file]
redakcja/static/img/angel-left.png [deleted file]
redakcja/static/img/angel-right.png [deleted file]
redakcja/static/img/arrow-down.png [deleted file]
redakcja/static/img/arrow-up.png [deleted file]
redakcja/static/img/gallery.png [deleted file]
redakcja/static/img/jquery.imgareaselect/border-anim-h.gif [deleted file]
redakcja/static/img/jquery.imgareaselect/border-anim-v.gif [deleted file]
redakcja/static/img/jquery.imgareaselect/border-h.gif [deleted file]
redakcja/static/img/jquery.imgareaselect/border-v.gif [deleted file]
redakcja/static/img/logo-220.png [deleted file]
redakcja/static/img/logo.png [deleted file]
redakcja/static/img/pr-icon.png [deleted file]
redakcja/static/img/sample_cover.png [deleted file]
redakcja/static/img/spinner.gif [deleted file]
redakcja/static/img/wl-orange.png [deleted file]
redakcja/static/img/wl-orange.svg [deleted file]
redakcja/static/js/book_text/book.js [deleted file]
redakcja/static/js/book_text/jquery.eventdelegation.js [deleted file]
redakcja/static/js/book_text/jquery.highlightfade.js [deleted file]
redakcja/static/js/book_text/jquery.scrollto.js [deleted file]
redakcja/static/js/button_scripts.js [deleted file]
redakcja/static/js/catalogue/book_list.js [deleted file]
redakcja/static/js/catalogue/catalogue.js [deleted file]
redakcja/static/js/lib/codemirror-0.8/codemirror.js [deleted file]
redakcja/static/js/lib/codemirror-0.8/editor.js [deleted file]
redakcja/static/js/lib/codemirror-0.8/parsexml.js [deleted file]
redakcja/static/js/lib/codemirror-0.8/select.js [deleted file]
redakcja/static/js/lib/codemirror-0.8/stringstream.js [deleted file]
redakcja/static/js/lib/codemirror-0.8/tokenize.js [deleted file]
redakcja/static/js/lib/codemirror-0.8/undo.js [deleted file]
redakcja/static/js/lib/codemirror-0.8/util.js [deleted file]
redakcja/static/js/lib/jquery-1.7.2.min.js [deleted file]
redakcja/static/js/lib/jquery/jquery.autocomplete.js [deleted file]
redakcja/static/js/lib/jquery/jquery.blockui.js [deleted file]
redakcja/static/js/lib/jquery/jquery.elastic.js [deleted file]
redakcja/static/js/lib/jquery/jquery.imgareaselect.js [deleted file]
redakcja/static/js/lib/jquery/jquery.xmlns.js [deleted file]
redakcja/static/js/slugify.js [deleted file]
redakcja/static/js/wiki/base.js [deleted file]
redakcja/static/js/wiki/dialog_pubmark.js [deleted file]
redakcja/static/js/wiki/dialog_revert.js [deleted file]
redakcja/static/js/wiki/dialog_save.js [deleted file]
redakcja/static/js/wiki/loader.js [deleted file]
redakcja/static/js/wiki/loader_readonly.js [deleted file]
redakcja/static/js/wiki/toolbar.js [deleted file]
redakcja/static/js/wiki/view_annotations.js [deleted file]
redakcja/static/js/wiki/view_column_diff.js [deleted file]
redakcja/static/js/wiki/view_editor_source.js [deleted file]
redakcja/static/js/wiki/view_editor_wysiwyg.js [deleted file]
redakcja/static/js/wiki/view_gallery.js [deleted file]
redakcja/static/js/wiki/view_history.js [deleted file]
redakcja/static/js/wiki/view_search.js [deleted file]
redakcja/static/js/wiki/view_summary.js [deleted file]
redakcja/static/js/wiki/wikiapi.js [deleted file]
redakcja/static/js/wiki/xslt.js [deleted file]
redakcja/static/js/wiki_img/base.js [deleted file]
redakcja/static/js/wiki_img/loader.js [deleted file]
redakcja/static/js/wiki_img/loader_readonly.js [deleted file]
redakcja/static/js/wiki_img/view_editor_motifs.js [deleted file]
redakcja/static/js/wiki_img/view_editor_objects.js [deleted file]
redakcja/static/js/wiki_img/wikiapi.js [deleted file]
redakcja/static/xsl/wl2html_client.xsl [deleted file]
redakcja/templates/404.html [deleted file]
redakcja/templates/500.html [deleted file]
redakcja/templates/503.html [deleted file]
redakcja/templates/base.html [deleted file]
redakcja/templates/error_base.html [deleted file]
redakcja/templates/pagination/pagination.html [deleted file]
redakcja/templates/registration/head_login.html [deleted file]
redakcja/templates/registration/login.html [deleted file]
redakcja/urls.py [deleted file]
redakcja/wsgi.py [deleted file]
src/apiclient/__init__.py [new file with mode: 0644]
src/apiclient/migrations/0001_initial.py [new file with mode: 0644]
src/apiclient/migrations/0002_auto__add_field_oauthconnection_beta.py [new file with mode: 0644]
src/apiclient/migrations/0003_auto__chg_field_oauthconnection_user__del_unique_oauthconnection_user.py [new file with mode: 0644]
src/apiclient/migrations/__init__.py [new file with mode: 0644]
src/apiclient/models.py [new file with mode: 0644]
src/apiclient/settings.py [new file with mode: 0755]
src/apiclient/urls.py [new file with mode: 0755]
src/apiclient/views.py [new file with mode: 0644]
src/catalogue/__init__.py [new file with mode: 0644]
src/catalogue/admin.py [new file with mode: 0644]
src/catalogue/constants.py [new file with mode: 0644]
src/catalogue/ebook_utils.py [new file with mode: 0644]
src/catalogue/feeds.py [new file with mode: 0644]
src/catalogue/fixtures/stages.json [new file with mode: 0644]
src/catalogue/forms.py [new file with mode: 0644]
src/catalogue/helpers.py [new file with mode: 0644]
src/catalogue/locale/pl/LC_MESSAGES/django.mo [new file with mode: 0644]
src/catalogue/locale/pl/LC_MESSAGES/django.po [new file with mode: 0644]
src/catalogue/management/__init__.py [new file with mode: 0644]
src/catalogue/management/commands/__init__.py [new file with mode: 0644]
src/catalogue/management/commands/add_parent.py [new file with mode: 0644]
src/catalogue/management/commands/assign_from_redmine.py [new file with mode: 0644]
src/catalogue/management/commands/fixdc.py [new file with mode: 0644]
src/catalogue/management/commands/import_wl.py [new file with mode: 0644]
src/catalogue/management/commands/insert_isbn.py [new file with mode: 0644]
src/catalogue/management/commands/mark_final.py [new file with mode: 0644]
src/catalogue/management/commands/merge_books.py [new file with mode: 0644]
src/catalogue/management/commands/prune_audience.py [new file with mode: 0644]
src/catalogue/managers.py [new file with mode: 0644]
src/catalogue/migrations/0001_initial.py [new file with mode: 0644]
src/catalogue/migrations/0002_stages.py [new file with mode: 0644]
src/catalogue/migrations/0003_from_hg.py [new file with mode: 0644]
src/catalogue/migrations/0004_fix_revisions.py [new file with mode: 0644]
src/catalogue/migrations/0005_auto__add_field_chunk_gallery_start.py [new file with mode: 0644]
src/catalogue/migrations/0006_auto__add_field_book_public.py [new file with mode: 0644]
src/catalogue/migrations/0007_auto__add_field_book_dc_slug.py [new file with mode: 0644]
src/catalogue/migrations/0008_auto.py [new file with mode: 0644]
src/catalogue/migrations/0009_auto__add_field_book__on_track.py [new file with mode: 0644]
src/catalogue/migrations/0010_auto__add_field_book_dc_cover_image.py [new file with mode: 0644]
src/catalogue/migrations/0011_auto__add_project__add_field_book_project.py [new file with mode: 0644]
src/catalogue/migrations/0012_auto__add_imagepublishrecord__add_imagechange__add_unique_imagechange_.py [new file with mode: 0644]
src/catalogue/migrations/0013_auto__add_field_image_project.py [new file with mode: 0644]
src/catalogue/migrations/__init__.py [new file with mode: 0644]
src/catalogue/models/__init__.py [new file with mode: 0755]
src/catalogue/models/book.py [new file with mode: 0755]
src/catalogue/models/chunk.py [new file with mode: 0755]
src/catalogue/models/image.py [new file with mode: 0755]
src/catalogue/models/listeners.py [new file with mode: 0755]
src/catalogue/models/project.py [new file with mode: 0755]
src/catalogue/models/publish_log.py [new file with mode: 0755]
src/catalogue/signals.py [new file with mode: 0644]
src/catalogue/tasks.py [new file with mode: 0644]
src/catalogue/templates/catalogue/active_users_list.html [new file with mode: 0755]
src/catalogue/templates/catalogue/activity.html [new file with mode: 0755]
src/catalogue/templates/catalogue/base.html [new file with mode: 0644]
src/catalogue/templates/catalogue/book_append_to.html [new file with mode: 0755]
src/catalogue/templates/catalogue/book_detail.html [new file with mode: 0755]
src/catalogue/templates/catalogue/book_edit.html [new file with mode: 0755]
src/catalogue/templates/catalogue/book_html.html [new file with mode: 0755]
src/catalogue/templates/catalogue/book_list/book.html [new file with mode: 0755]
src/catalogue/templates/catalogue/book_list/book_list.html [new file with mode: 0755]
src/catalogue/templates/catalogue/book_list/chunk.html [new file with mode: 0755]
src/catalogue/templates/catalogue/book_text.html [new file with mode: 0644]
src/catalogue/templates/catalogue/chunk_add.html [new file with mode: 0755]
src/catalogue/templates/catalogue/chunk_edit.html [new file with mode: 0755]
src/catalogue/templates/catalogue/document_create_missing.html [new file with mode: 0644]
src/catalogue/templates/catalogue/document_list.html [new file with mode: 0644]
src/catalogue/templates/catalogue/document_upload.html [new file with mode: 0644]
src/catalogue/templates/catalogue/image_detail.html [new file with mode: 0755]
src/catalogue/templates/catalogue/image_list.html [new file with mode: 0755]
src/catalogue/templates/catalogue/image_short.html [new file with mode: 0755]
src/catalogue/templates/catalogue/image_table.html [new file with mode: 0755]
src/catalogue/templates/catalogue/main_tabs.html [new file with mode: 0755]
src/catalogue/templates/catalogue/mark_final.html [new file with mode: 0644]
src/catalogue/templates/catalogue/mark_final_completed.html [new file with mode: 0644]
src/catalogue/templates/catalogue/my_page.html [new file with mode: 0755]
src/catalogue/templates/catalogue/upload_pdf.html [new file with mode: 0755]
src/catalogue/templates/catalogue/user_list.html [new file with mode: 0755]
src/catalogue/templates/catalogue/user_page.html [new file with mode: 0755]
src/catalogue/templates/catalogue/wall.html [new file with mode: 0755]
src/catalogue/templatetags/__init__.py [new file with mode: 0644]
src/catalogue/templatetags/book_list.py [new file with mode: 0755]
src/catalogue/templatetags/catalogue.py [new file with mode: 0644]
src/catalogue/templatetags/common_tags.py [new file with mode: 0755]
src/catalogue/templatetags/set_get_parameter.py [new file with mode: 0755]
src/catalogue/templatetags/wall.py [new file with mode: 0755]
src/catalogue/test_utils.py [new file with mode: 0644]
src/catalogue/tests/__init__.py [new file with mode: 0644]
src/catalogue/tests/book.py [new file with mode: 0644]
src/catalogue/tests/files/chunk1.xml [new file with mode: 0755]
src/catalogue/tests/files/chunk2.xml [new file with mode: 0755]
src/catalogue/tests/files/expected.xml [new file with mode: 0755]
src/catalogue/tests/gallery.py [new file with mode: 0644]
src/catalogue/tests/publish.py [new file with mode: 0644]
src/catalogue/tests/xml_updater.py [new file with mode: 0644]
src/catalogue/urls.py [new file with mode: 0644]
src/catalogue/views.py [new file with mode: 0644]
src/catalogue/xml_tools.py [new file with mode: 0644]
src/cover/__init__.py [new file with mode: 0644]
src/cover/forms.py [new file with mode: 0755]
src/cover/locale/pl/LC_MESSAGES/django.mo [new file with mode: 0644]
src/cover/locale/pl/LC_MESSAGES/django.po [new file with mode: 0644]
src/cover/management/__init__.py [new file with mode: 0644]
src/cover/management/commands/__init__.py [new file with mode: 0644]
src/cover/management/commands/refresh_covers.py [new file with mode: 0644]
src/cover/migrations/0001_initial.py [new file with mode: 0644]
src/cover/migrations/0002_auto__chg_field_image_download_url.py [new file with mode: 0644]
src/cover/migrations/0003_auto__chg_field_image_source_url.py [new file with mode: 0644]
src/cover/migrations/__init__.py [new file with mode: 0644]
src/cover/models.py [new file with mode: 0644]
src/cover/templates/cover/add_image.html [new file with mode: 0755]
src/cover/templates/cover/image_detail.html [new file with mode: 0755]
src/cover/templates/cover/image_list.html [new file with mode: 0755]
src/cover/tests.py [new file with mode: 0644]
src/cover/urls.py [new file with mode: 0644]
src/cover/utils.py [new file with mode: 0755]
src/cover/views.py [new file with mode: 0644]
src/dvcs/__init__.py [new file with mode: 0644]
src/dvcs/locale/pl/LC_MESSAGES/django.mo [new file with mode: 0644]
src/dvcs/locale/pl/LC_MESSAGES/django.po [new file with mode: 0644]
src/dvcs/models.py [new file with mode: 0644]
src/dvcs/signals.py [new file with mode: 0755]
src/dvcs/storage.py [new file with mode: 0755]
src/dvcs/tests/__init__.py [new file with mode: 0755]
src/email_mangler/__init__.py [new file with mode: 0644]
src/email_mangler/locale/pl/LC_MESSAGES/django.mo [new file with mode: 0644]
src/email_mangler/locale/pl/LC_MESSAGES/django.po [new file with mode: 0644]
src/email_mangler/models.py [new file with mode: 0644]
src/email_mangler/templatetags/__init__.py [new file with mode: 0755]
src/email_mangler/templatetags/email.py [new file with mode: 0755]
src/fileupload/__init__.py [new file with mode: 0644]
src/fileupload/forms.py [new file with mode: 0644]
src/fileupload/locale/pl/LC_MESSAGES/django.mo [new file with mode: 0644]
src/fileupload/locale/pl/LC_MESSAGES/django.po [new file with mode: 0644]
src/fileupload/models.py [new file with mode: 0644]
src/fileupload/static/fileupload/css/bootstrap-image-gallery.min.css [new file with mode: 0644]
src/fileupload/static/fileupload/css/bootstrap.min.css [new file with mode: 0644]
src/fileupload/static/fileupload/css/jquery.fileupload-ui.css [new file with mode: 0644]
src/fileupload/static/fileupload/css/style.css [new file with mode: 0644]
src/fileupload/static/fileupload/img/glyphicons-halflings-white.png [new file with mode: 0644]
src/fileupload/static/fileupload/img/glyphicons-halflings.png [new file with mode: 0644]
src/fileupload/static/fileupload/img/loading.gif [new file with mode: 0644]
src/fileupload/static/fileupload/img/progressbar.gif [new file with mode: 0644]
src/fileupload/static/fileupload/js/bootstrap-image-gallery.min.js [new file with mode: 0644]
src/fileupload/static/fileupload/js/bootstrap.min.js [new file with mode: 0644]
src/fileupload/static/fileupload/js/canvas-to-blob.min.js [new file with mode: 0644]
src/fileupload/static/fileupload/js/csrf.js [new file with mode: 0644]
src/fileupload/static/fileupload/js/jquery.fileupload-fp.js [new file with mode: 0644]
src/fileupload/static/fileupload/js/jquery.fileupload-ui.js [new file with mode: 0644]
src/fileupload/static/fileupload/js/jquery.fileupload.js [new file with mode: 0644]
src/fileupload/static/fileupload/js/jquery.iframe-transport.js [new file with mode: 0644]
src/fileupload/static/fileupload/js/jquery.ui.widget.js [new file with mode: 0644]
src/fileupload/static/fileupload/js/load-image.min.js [new file with mode: 0644]
src/fileupload/static/fileupload/js/locale-en.js [new file with mode: 0644]
src/fileupload/static/fileupload/js/locale-pl.js [new file with mode: 0755]
src/fileupload/static/fileupload/js/main.js [new file with mode: 0644]
src/fileupload/static/fileupload/js/tmpl.min.js [new file with mode: 0644]
src/fileupload/templates/fileupload/picture_form.html [new file with mode: 0644]
src/fileupload/templatetags/__init__.py [new file with mode: 0644]
src/fileupload/templatetags/upload_tags.py [new file with mode: 0644]
src/fileupload/urls.py [new file with mode: 0644]
src/fileupload/views.py [new file with mode: 0644]
src/manage.py [new file with mode: 0755]
src/redakcja/__init__.py [new file with mode: 0644]
src/redakcja/celery.py [new file with mode: 0644]
src/redakcja/context_processors.py [new file with mode: 0644]
src/redakcja/locale/pl/LC_MESSAGES/django.mo [new file with mode: 0644]
src/redakcja/locale/pl/LC_MESSAGES/django.po [new file with mode: 0644]
src/redakcja/localsettings.sample [new file with mode: 0644]
src/redakcja/settings/__init__.py [new file with mode: 0644]
src/redakcja/settings/common.py [new file with mode: 0644]
src/redakcja/settings/compress.py [new file with mode: 0644]
src/redakcja/settings/integration_test.py [new file with mode: 0644]
src/redakcja/settings/test.py [new file with mode: 0644]
src/redakcja/static/contextmenu/images/cut.png [new file with mode: 0755]
src/redakcja/static/contextmenu/images/door.png [new file with mode: 0755]
src/redakcja/static/contextmenu/images/page_white_copy.png [new file with mode: 0755]
src/redakcja/static/contextmenu/images/page_white_delete.png [new file with mode: 0755]
src/redakcja/static/contextmenu/images/page_white_edit.png [new file with mode: 0755]
src/redakcja/static/contextmenu/images/page_white_paste.png [new file with mode: 0755]
src/redakcja/static/contextmenu/index.html [new file with mode: 0644]
src/redakcja/static/contextmenu/jquery.contextMenu.css [new file with mode: 0755]
src/redakcja/static/contextmenu/jquery.contextMenu.js [new file with mode: 0755]
src/redakcja/static/contextmenu/jquery.ui.position.js [new file with mode: 0755]
src/redakcja/static/contextmenu2/images/cut.png [new file with mode: 0644]
src/redakcja/static/contextmenu2/images/door.png [new file with mode: 0644]
src/redakcja/static/contextmenu2/images/page_white_copy.png [new file with mode: 0644]
src/redakcja/static/contextmenu2/images/page_white_delete.png [new file with mode: 0644]
src/redakcja/static/contextmenu2/images/page_white_edit.png [new file with mode: 0644]
src/redakcja/static/contextmenu2/images/page_white_paste.png [new file with mode: 0644]
src/redakcja/static/contextmenu2/index.html [new file with mode: 0644]
src/redakcja/static/contextmenu2/jquery.contextMenu.css [new file with mode: 0644]
src/redakcja/static/contextmenu2/jquery.contextMenu.js [new file with mode: 0644]
src/redakcja/static/css/book.css [new file with mode: 0644]
src/redakcja/static/css/book_list.css [new file with mode: 0644]
src/redakcja/static/css/dialogs.css [new file with mode: 0644]
src/redakcja/static/css/filelist.css [new file with mode: 0644]
src/redakcja/static/css/gallery.css [new file with mode: 0644]
src/redakcja/static/css/history.css [new file with mode: 0644]
src/redakcja/static/css/html.css [new file with mode: 0644]
src/redakcja/static/css/html_print.css [new file with mode: 0644]
src/redakcja/static/css/imgareaselect-default.css [new file with mode: 0644]
src/redakcja/static/css/jquery.autocomplete.css [new file with mode: 0644]
src/redakcja/static/css/master.css [new file with mode: 0644]
src/redakcja/static/css/summary.css [new file with mode: 0644]
src/redakcja/static/css/toolbar.css [new file with mode: 0644]
src/redakcja/static/css/xmlcolors_20100906.css [new file with mode: 0644]
src/redakcja/static/email_mangler/email_mangler.js [new file with mode: 0755]
src/redakcja/static/icons/clock.png [new file with mode: 0644]
src/redakcja/static/icons/close.png [new file with mode: 0644]
src/redakcja/static/icons/go-home.png [new file with mode: 0644]
src/redakcja/static/icons/go-next.png [new file with mode: 0644]
src/redakcja/static/icons/go-previous.png [new file with mode: 0644]
src/redakcja/static/icons/image-x-generic.png [new file with mode: 0644]
src/redakcja/static/icons/ok.png [new file with mode: 0644]
src/redakcja/static/icons/revert.png [new file with mode: 0644]
src/redakcja/static/icons/revert_.png [new file with mode: 0755]
src/redakcja/static/icons/stop.png [new file with mode: 0644]
src/redakcja/static/icons/system-search.png [new file with mode: 0644]
src/redakcja/static/icons/user.png [new file with mode: 0644]
src/redakcja/static/icons/zoom.png [new file with mode: 0644]
src/redakcja/static/icons/zoom_in.png [new file with mode: 0644]
src/redakcja/static/icons/zoom_out.png [new file with mode: 0644]
src/redakcja/static/img/angel-left.png [new file with mode: 0644]
src/redakcja/static/img/angel-right.png [new file with mode: 0644]
src/redakcja/static/img/arrow-down.png [new file with mode: 0644]
src/redakcja/static/img/arrow-up.png [new file with mode: 0644]
src/redakcja/static/img/gallery.png [new file with mode: 0644]
src/redakcja/static/img/jquery.imgareaselect/border-anim-h.gif [new file with mode: 0644]
src/redakcja/static/img/jquery.imgareaselect/border-anim-v.gif [new file with mode: 0644]
src/redakcja/static/img/jquery.imgareaselect/border-h.gif [new file with mode: 0644]
src/redakcja/static/img/jquery.imgareaselect/border-v.gif [new file with mode: 0644]
src/redakcja/static/img/logo-220.png [new file with mode: 0644]
src/redakcja/static/img/logo.png [new file with mode: 0644]
src/redakcja/static/img/pr-icon.png [new file with mode: 0644]
src/redakcja/static/img/sample_cover.png [new file with mode: 0644]
src/redakcja/static/img/spinner.gif [new file with mode: 0644]
src/redakcja/static/img/wl-orange.png [new file with mode: 0644]
src/redakcja/static/img/wl-orange.svg [new file with mode: 0644]
src/redakcja/static/js/book_text/book.js [new file with mode: 0644]
src/redakcja/static/js/book_text/jquery.eventdelegation.js [new file with mode: 0644]
src/redakcja/static/js/book_text/jquery.highlightfade.js [new file with mode: 0644]
src/redakcja/static/js/book_text/jquery.scrollto.js [new file with mode: 0644]
src/redakcja/static/js/button_scripts.js [new file with mode: 0644]
src/redakcja/static/js/catalogue/book_list.js [new file with mode: 0644]
src/redakcja/static/js/catalogue/catalogue.js [new file with mode: 0755]
src/redakcja/static/js/lib/codemirror-0.8/codemirror.js [new file with mode: 0644]
src/redakcja/static/js/lib/codemirror-0.8/editor.js [new file with mode: 0644]
src/redakcja/static/js/lib/codemirror-0.8/parsexml.js [new file with mode: 0644]
src/redakcja/static/js/lib/codemirror-0.8/select.js [new file with mode: 0644]
src/redakcja/static/js/lib/codemirror-0.8/stringstream.js [new file with mode: 0644]
src/redakcja/static/js/lib/codemirror-0.8/tokenize.js [new file with mode: 0644]
src/redakcja/static/js/lib/codemirror-0.8/undo.js [new file with mode: 0644]
src/redakcja/static/js/lib/codemirror-0.8/util.js [new file with mode: 0644]
src/redakcja/static/js/lib/jquery-1.7.2.min.js [new file with mode: 0644]
src/redakcja/static/js/lib/jquery/jquery.autocomplete.js [new file with mode: 0644]
src/redakcja/static/js/lib/jquery/jquery.blockui.js [new file with mode: 0644]
src/redakcja/static/js/lib/jquery/jquery.elastic.js [new file with mode: 0644]
src/redakcja/static/js/lib/jquery/jquery.imgareaselect.js [new file with mode: 0644]
src/redakcja/static/js/lib/jquery/jquery.xmlns.js [new file with mode: 0644]
src/redakcja/static/js/slugify.js [new file with mode: 0644]
src/redakcja/static/js/wiki/base.js [new file with mode: 0644]
src/redakcja/static/js/wiki/dialog_pubmark.js [new file with mode: 0755]
src/redakcja/static/js/wiki/dialog_revert.js [new file with mode: 0644]
src/redakcja/static/js/wiki/dialog_save.js [new file with mode: 0644]
src/redakcja/static/js/wiki/loader.js [new file with mode: 0644]
src/redakcja/static/js/wiki/loader_readonly.js [new file with mode: 0644]
src/redakcja/static/js/wiki/toolbar.js [new file with mode: 0644]
src/redakcja/static/js/wiki/view_annotations.js [new file with mode: 0644]
src/redakcja/static/js/wiki/view_column_diff.js [new file with mode: 0644]
src/redakcja/static/js/wiki/view_editor_source.js [new file with mode: 0644]
src/redakcja/static/js/wiki/view_editor_wysiwyg.js [new file with mode: 0644]
src/redakcja/static/js/wiki/view_gallery.js [new file with mode: 0644]
src/redakcja/static/js/wiki/view_history.js [new file with mode: 0644]
src/redakcja/static/js/wiki/view_search.js [new file with mode: 0644]
src/redakcja/static/js/wiki/view_summary.js [new file with mode: 0644]
src/redakcja/static/js/wiki/wikiapi.js [new file with mode: 0644]
src/redakcja/static/js/wiki/xslt.js [new file with mode: 0644]
src/redakcja/static/js/wiki_img/base.js [new file with mode: 0644]
src/redakcja/static/js/wiki_img/loader.js [new file with mode: 0644]
src/redakcja/static/js/wiki_img/loader_readonly.js [new file with mode: 0755]
src/redakcja/static/js/wiki_img/view_editor_motifs.js [new file with mode: 0644]
src/redakcja/static/js/wiki_img/view_editor_objects.js [new file with mode: 0644]
src/redakcja/static/js/wiki_img/wikiapi.js [new file with mode: 0644]
src/redakcja/static/xsl/wl2html_client.xsl [new file with mode: 0644]
src/redakcja/templates/404.html [new file with mode: 0644]
src/redakcja/templates/500.html [new file with mode: 0644]
src/redakcja/templates/503.html [new file with mode: 0644]
src/redakcja/templates/base.html [new file with mode: 0644]
src/redakcja/templates/error_base.html [new file with mode: 0755]
src/redakcja/templates/pagination/pagination.html [new file with mode: 0755]
src/redakcja/templates/registration/head_login.html [new file with mode: 0644]
src/redakcja/templates/registration/login.html [new file with mode: 0644]
src/redakcja/urls.py [new file with mode: 0644]
src/redakcja/wsgi.py [new file with mode: 0755]
src/toolbar/__init__.py [new file with mode: 0644]
src/toolbar/admin.py [new file with mode: 0644]
src/toolbar/fixtures/initial_toolbar.yaml [new file with mode: 0644]
src/toolbar/locale/pl/LC_MESSAGES/django.mo [new file with mode: 0644]
src/toolbar/locale/pl/LC_MESSAGES/django.po [new file with mode: 0644]
src/toolbar/management/__init__.py [new file with mode: 0644]
src/toolbar/management/commands/__init__.py [new file with mode: 0644]
src/toolbar/management/commands/fixbuttons.py [new file with mode: 0644]
src/toolbar/migrations/0001_initial.py [new file with mode: 0644]
src/toolbar/migrations/0002_auto__del_field_button_key_mod__chg_field_button_key.py [new file with mode: 0644]
src/toolbar/migrations/0003_button_key_rename_to_accesskey.py [new file with mode: 0644]
src/toolbar/migrations/0004_auto__chg_field_button_accesskey.py [new file with mode: 0644]
src/toolbar/migrations/0005_initial_data.py [new file with mode: 0644]
src/toolbar/migrations/__init__.py [new file with mode: 0644]
src/toolbar/models.py [new file with mode: 0644]
src/toolbar/templates/toolbar/button.html [new file with mode: 0644]
src/toolbar/templates/toolbar/toolbar.html [new file with mode: 0644]
src/toolbar/templatetags/__init__.py [new file with mode: 0644]
src/toolbar/templatetags/toolbar_tags.py [new file with mode: 0644]
src/wiki/__init__.py [new file with mode: 0644]
src/wiki/admin.py [new file with mode: 0644]
src/wiki/fixtures/initial_themes.yaml [new file with mode: 0644]
src/wiki/forms.py [new file with mode: 0644]
src/wiki/helpers.py [new file with mode: 0644]
src/wiki/locale/pl/LC_MESSAGES/django.mo [new file with mode: 0644]
src/wiki/locale/pl/LC_MESSAGES/django.po [new file with mode: 0644]
src/wiki/migrations/0001_initial.py [new file with mode: 0644]
src/wiki/migrations/0002_auto__add_theme.py [new file with mode: 0644]
src/wiki/migrations/__init__.py [new file with mode: 0644]
src/wiki/models.py [new file with mode: 0644]
src/wiki/nice_diff.py [new file with mode: 0644]
src/wiki/settings.py [new file with mode: 0644]
src/wiki/templates/admin/wiki/theme/change_list.html [new file with mode: 0755]
src/wiki/templates/wiki/diff_table.html [new file with mode: 0644]
src/wiki/templates/wiki/document_details.html [new file with mode: 0644]
src/wiki/templates/wiki/document_details_base.html [new file with mode: 0644]
src/wiki/templates/wiki/document_details_readonly.html [new file with mode: 0644]
src/wiki/templates/wiki/pubmark_dialog.html [new file with mode: 0755]
src/wiki/templates/wiki/revert_dialog.html [new file with mode: 0644]
src/wiki/templates/wiki/save_dialog.html [new file with mode: 0644]
src/wiki/templates/wiki/tabs/annotations_view.html [new file with mode: 0644]
src/wiki/templates/wiki/tabs/annotations_view_item.html [new file with mode: 0644]
src/wiki/templates/wiki/tabs/gallery_view.html [new file with mode: 0644]
src/wiki/templates/wiki/tabs/gallery_view_item.html [new file with mode: 0644]
src/wiki/templates/wiki/tabs/history_view.html [new file with mode: 0644]
src/wiki/templates/wiki/tabs/history_view_item.html [new file with mode: 0644]
src/wiki/templates/wiki/tabs/search_view.html [new file with mode: 0644]
src/wiki/templates/wiki/tabs/search_view_item.html [new file with mode: 0644]
src/wiki/templates/wiki/tabs/source_editor.html [new file with mode: 0644]
src/wiki/templates/wiki/tabs/source_editor_item.html [new file with mode: 0644]
src/wiki/templates/wiki/tabs/summary_view.html [new file with mode: 0644]
src/wiki/templates/wiki/tabs/summary_view_item.html [new file with mode: 0644]
src/wiki/templates/wiki/tabs/wysiwyg_editor.html [new file with mode: 0644]
src/wiki/templates/wiki/tabs/wysiwyg_editor_item.html [new file with mode: 0644]
src/wiki/tests/xslt/auto/auto_test.js [new file with mode: 0644]
src/wiki/tests/xslt/auto/data/akap.html [new file with mode: 0644]
src/wiki/tests/xslt/auto/data/big.xml [new file with mode: 0644]
src/wiki/tests/xslt/auto/data/unknown_tag.html [new file with mode: 0644]
src/wiki/tests/xslt/auto/data/unknown_tag.xml [new file with mode: 0644]
src/wiki/urls.py [new file with mode: 0644]
src/wiki/views.py [new file with mode: 0644]
src/wiki_img/__init__.py [new file with mode: 0644]
src/wiki_img/forms.py [new file with mode: 0644]
src/wiki_img/locale/pl/LC_MESSAGES/django.mo [new file with mode: 0644]
src/wiki_img/locale/pl/LC_MESSAGES/django.po [new file with mode: 0644]
src/wiki_img/models.py [new file with mode: 0644]
src/wiki_img/templates/wiki_img/diff_table.html [new file with mode: 0644]
src/wiki_img/templates/wiki_img/document_details.html [new file with mode: 0644]
src/wiki_img/templates/wiki_img/document_details_base.html [new file with mode: 0644]
src/wiki_img/templates/wiki_img/document_details_readonly.html [new file with mode: 0644]
src/wiki_img/templates/wiki_img/tabs/history_view.html [new file with mode: 0755]
src/wiki_img/templates/wiki_img/tabs/motifs_editor.html [new file with mode: 0644]
src/wiki_img/templates/wiki_img/tabs/motifs_editor_item.html [new file with mode: 0644]
src/wiki_img/templates/wiki_img/tabs/objects_editor.html [new file with mode: 0644]
src/wiki_img/templates/wiki_img/tabs/objects_editor_item.html [new file with mode: 0644]
src/wiki_img/templates/wiki_img/tabs/source_editor.html [new file with mode: 0644]
src/wiki_img/templates/wiki_img/tabs/source_editor_item.html [new file with mode: 0644]
src/wiki_img/templates/wiki_img/tabs/summary_view.html [new file with mode: 0644]
src/wiki_img/templates/wiki_img/tabs/summary_view_item.html [new file with mode: 0644]
src/wiki_img/tests.py [new file with mode: 0644]
src/wiki_img/urls.py [new file with mode: 0644]
src/wiki_img/views.py [new file with mode: 0644]

index 93ffb1c..f1672f4 100644 (file)
--- a/README.md
+++ b/README.md
@@ -42,10 +42,9 @@ Testy
 ====
 
     $ pip install -r requirements-test.txt
-    $ python redakcja/manage.py test --settings=settings.test
+    $ python src/manage.py test --settings=redakcja.settings.test
 
 JavaScript (wymagany node.js i xsltproc):
 
     $ npm install
-    $ ./node_modules/.bin/mocha -u tdd $(find -name *_test.js)
-    
\ No newline at end of file
+    $ ./node_modules/.bin/mocha -u tdd $(find src -name *_test.js)
diff --git a/apps/apiclient/__init__.py b/apps/apiclient/__init__.py
deleted file mode 100644 (file)
index 56ecb96..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-import urllib
-
-import json
-import oauth2
-
-from apiclient.settings import WL_CONSUMER_KEY, WL_CONSUMER_SECRET, WL_API_URL, BETA_API_URL
-
-
-if WL_CONSUMER_KEY and WL_CONSUMER_SECRET:
-    wl_consumer = oauth2.Consumer(WL_CONSUMER_KEY, WL_CONSUMER_SECRET)
-else:
-    wl_consumer = None
-
-
-class ApiError(BaseException):
-    pass
-
-
-class NotAuthorizedError(BaseException):
-    pass
-
-
-def api_call(user, path, data=None, beta=False):
-    from .models import OAuthConnection
-    api_url = BETA_API_URL if beta else WL_API_URL
-    conn = OAuthConnection.get(user=user, beta=beta)
-    if not conn.access:
-        raise NotAuthorizedError("No WL authorization for user %s." % user)
-    token = oauth2.Token(conn.token, conn.token_secret)
-    client = oauth2.Client(wl_consumer, token)
-    if data is not None:
-        data = json.dumps(data)
-        data = urllib.urlencode({"data": data})
-        resp, content = client.request(
-                "%s%s" % (api_url, path),
-                method="POST",
-                body=data)
-    else:
-        resp, content = client.request(
-                "%s%s" % (api_url, path))
-    status = resp['status']
-
-    if status == '200':
-        return json.loads(content)
-    elif status.startswith('2'):
-        return
-    elif status == '401':
-        raise ApiError('User not authorized for publishing.')
-    else:
-        raise ApiError("WL API call error %s, path: %s" % (status, path))
-
diff --git a/apps/apiclient/migrations/0001_initial.py b/apps/apiclient/migrations/0001_initial.py
deleted file mode 100644 (file)
index 4af28a5..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-# encoding: utf-8
-import datetime
-from south.db import db
-from south.v2 import SchemaMigration
-from django.db import models
-
-class Migration(SchemaMigration):
-
-    def forwards(self, orm):
-        
-        # Adding model 'OAuthConnection'
-        db.create_table('apiclient_oauthconnection', (
-            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
-            ('user', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['auth.User'], unique=True)),
-            ('access', self.gf('django.db.models.fields.BooleanField')(default=False)),
-            ('token', self.gf('django.db.models.fields.CharField')(max_length=64, null=True, blank=True)),
-            ('token_secret', self.gf('django.db.models.fields.CharField')(max_length=64, null=True, blank=True)),
-        ))
-        db.send_create_signal('apiclient', ['OAuthConnection'])
-
-
-    def backwards(self, orm):
-        
-        # Deleting model 'OAuthConnection'
-        db.delete_table('apiclient_oauthconnection')
-
-
-    models = {
-        'apiclient.oauthconnection': {
-            'Meta': {'object_name': 'OAuthConnection'},
-            'access': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'token': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'blank': 'True'}),
-            'token_secret': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'blank': 'True'}),
-            'user': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True'})
-        },
-        'auth.group': {
-            'Meta': {'object_name': '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']", 'symmetrical': 'False', 'blank': 'True'})
-        },
-        'auth.permission': {
-            'Meta': {'ordering': "('content_type__app_label', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
-            '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': {
-            'Meta': {'object_name': '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']", 'symmetrical': 'False', 'blank': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
-            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            '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']", 'symmetrical': 'False', 'blank': 'True'}),
-            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
-        },
-        'contenttypes.contenttype': {
-            'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', '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 = ['apiclient']
diff --git a/apps/apiclient/migrations/0002_auto__add_field_oauthconnection_beta.py b/apps/apiclient/migrations/0002_auto__add_field_oauthconnection_beta.py
deleted file mode 100644 (file)
index 094606f..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-# -*- coding: utf-8 -*-
-from south.utils import datetime_utils as datetime
-from south.db import db
-from south.v2 import SchemaMigration
-from django.db import models
-
-
-class Migration(SchemaMigration):
-
-    def forwards(self, orm):
-        # Adding field 'OAuthConnection.beta'
-        db.add_column(u'apiclient_oauthconnection', 'beta',
-                      self.gf('django.db.models.fields.BooleanField')(default=False),
-                      keep_default=False)
-
-
-    def backwards(self, orm):
-        # Deleting field 'OAuthConnection.beta'
-        db.delete_column(u'apiclient_oauthconnection', 'beta')
-
-
-    models = {
-        u'apiclient.oauthconnection': {
-            'Meta': {'object_name': 'OAuthConnection'},
-            'access': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'beta': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'token': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'blank': 'True'}),
-            'token_secret': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'blank': 'True'}),
-            'user': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['auth.User']", 'unique': 'True'})
-        },
-        u'auth.group': {
-            'Meta': {'object_name': 'Group'},
-            u'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': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
-        },
-        u'auth.permission': {
-            'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
-            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
-        },
-        u'auth.user': {
-            'Meta': {'object_name': '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', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
-            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            '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', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}),
-            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
-        },
-        u'contenttypes.contenttype': {
-            'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
-            'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            u'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 = ['apiclient']
\ No newline at end of file
diff --git a/apps/apiclient/migrations/0003_auto__chg_field_oauthconnection_user__del_unique_oauthconnection_user.py b/apps/apiclient/migrations/0003_auto__chg_field_oauthconnection_user__del_unique_oauthconnection_user.py
deleted file mode 100644 (file)
index ebd0fbb..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-# -*- coding: utf-8 -*-
-from south.utils import datetime_utils as datetime
-from south.db import db
-from south.v2 import SchemaMigration
-from django.db import models
-
-
-class Migration(SchemaMigration):
-
-    def forwards(self, orm):
-        # Removing unique constraint on 'OAuthConnection', fields ['user']
-        db.delete_unique(u'apiclient_oauthconnection', ['user_id'])
-
-
-        # Changing field 'OAuthConnection.user'
-        db.alter_column(u'apiclient_oauthconnection', 'user_id', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User']))
-
-    def backwards(self, orm):
-
-        # Changing field 'OAuthConnection.user'
-        db.alter_column(u'apiclient_oauthconnection', 'user_id', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['auth.User'], unique=True))
-        # Adding unique constraint on 'OAuthConnection', fields ['user']
-        db.create_unique(u'apiclient_oauthconnection', ['user_id'])
-
-
-    models = {
-        u'apiclient.oauthconnection': {
-            'Meta': {'object_name': 'OAuthConnection'},
-            'access': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'beta': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'token': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'blank': 'True'}),
-            'token_secret': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'blank': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"})
-        },
-        u'auth.group': {
-            'Meta': {'object_name': 'Group'},
-            u'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': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
-        },
-        u'auth.permission': {
-            'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
-            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
-        },
-        u'auth.user': {
-            'Meta': {'object_name': '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', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
-            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            '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', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}),
-            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
-        },
-        u'contenttypes.contenttype': {
-            'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
-            'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            u'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 = ['apiclient']
\ No newline at end of file
diff --git a/apps/apiclient/migrations/__init__.py b/apps/apiclient/migrations/__init__.py
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/apps/apiclient/models.py b/apps/apiclient/models.py
deleted file mode 100644 (file)
index 64130bc..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-from django.db import models
-from django.contrib.auth.models import User
-
-
-class OAuthConnection(models.Model):
-    user = models.ForeignKey(User)
-    access = models.BooleanField(default=False)
-    token = models.CharField(max_length=64, null=True, blank=True)
-    token_secret = models.CharField(max_length=64, null=True, blank=True)
-    beta = models.BooleanField(default=False)
-
-    @classmethod
-    def get(cls, user, beta=False):
-        try:
-            return cls.objects.get(user=user, beta=beta)
-        except cls.DoesNotExist:
-            o = cls(user=user, beta=beta)
-            o.save()
-            return o
-
-
diff --git a/apps/apiclient/settings.py b/apps/apiclient/settings.py
deleted file mode 100755 (executable)
index 51b4906..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-from django.conf import settings
-
-
-WL_CONSUMER_KEY = getattr(settings, 'APICLIENT_WL_CONSUMER_KEY', None)
-WL_CONSUMER_SECRET = getattr(settings, 'APICLIENT_WL_CONSUMER_SECRET', None)
-
-WL_API_URL = getattr(settings, 'APICLIENT_WL_API_URL', 'https://wolnelektury.pl/api/')
-
-BETA_API_URL = getattr(settings, 'APICLIENT_BETA_API_URL', 'http://dev.wolnelektury.pl/api/')
-
-WL_REQUEST_TOKEN_URL = getattr(settings, 'APICLIENT_WL_REQUEST_TOKEN_URL', 
-        WL_API_URL + 'oauth/request_token/')
-WL_ACCESS_TOKEN_URL = getattr(settings, 'APICLIENT_WL_ACCESS_TOKEN_URL', 
-        WL_API_URL + 'oauth/access_token/')
-WL_AUTHORIZE_URL = getattr(settings, 'APICLIENT_WL_AUTHORIZE_URL', 
-        WL_API_URL + 'oauth/authorize/')
-
-BETA_REQUEST_TOKEN_URL = BETA_API_URL + 'oauth/request_token/'
-BETA_ACCESS_TOKEN_URL = BETA_API_URL + 'oauth/access_token/'
-BETA_AUTHORIZE_URL = BETA_API_URL + 'oauth/authorize/'
diff --git a/apps/apiclient/urls.py b/apps/apiclient/urls.py
deleted file mode 100755 (executable)
index f623474..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-# -*- coding: utf-8 -*-
-from django.conf.urls import patterns, url
-
-urlpatterns = patterns('apiclient.views',
-    url(r'^oauth/$', 'oauth', name='apiclient_oauth'),
-    url(r'^oauth_callback/$', 'oauth_callback', name='apiclient_oauth_callback'),
-    url(r'^oauth-beta/$', 'oauth', kwargs={'beta': True}, name='apiclient_beta_oauth'),
-    url(r'^oauth_callback-beta/$', 'oauth_callback', kwargs={'beta': True}, name='apiclient_beta_callback'),
-)
diff --git a/apps/apiclient/views.py b/apps/apiclient/views.py
deleted file mode 100644 (file)
index 239682a..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-import cgi
-
-from django.contrib.auth.decorators import login_required
-from django.core.urlresolvers import reverse
-from django.http import HttpResponseRedirect, HttpResponse
-import oauth2
-
-from apiclient.models import OAuthConnection
-from apiclient import wl_consumer
-from apiclient.settings import WL_REQUEST_TOKEN_URL, WL_ACCESS_TOKEN_URL, WL_AUTHORIZE_URL
-from apiclient.settings import BETA_REQUEST_TOKEN_URL, BETA_ACCESS_TOKEN_URL, BETA_AUTHORIZE_URL
-
-
-@login_required
-def oauth(request, beta=False):
-    if wl_consumer is None:
-        return HttpResponse("OAuth consumer not configured.")
-
-    client = oauth2.Client(wl_consumer)
-    resp, content = client.request(WL_REQUEST_TOKEN_URL if not beta else BETA_REQUEST_TOKEN_URL)
-    if resp['status'] != '200':
-        raise Exception("Invalid response %s." % resp['status'])
-
-    request_token = dict(cgi.parse_qsl(content))
-    
-    conn = OAuthConnection.get(request.user, beta)
-    # this might reset existing auth!
-    conn.access = False
-    conn.token = request_token['oauth_token']
-    conn.token_secret = request_token['oauth_token_secret']
-    conn.save()
-
-    url = "%s?oauth_token=%s&oauth_callback=%s" % (
-            WL_AUTHORIZE_URL if not beta else BETA_AUTHORIZE_URL,
-            request_token['oauth_token'],
-            request.build_absolute_uri(reverse("apiclient_oauth_callback" if not beta else "apiclient_beta_callback")),
-            )
-
-    return HttpResponseRedirect(url)
-
-
-@login_required
-def oauth_callback(request, beta=False):
-    if wl_consumer is None:
-        return HttpResponse("OAuth consumer not configured.")
-
-    oauth_verifier = request.GET.get('oauth_verifier')
-    conn = OAuthConnection.get(request.user, beta)
-    token = oauth2.Token(conn.token, conn.token_secret)
-    token.set_verifier(oauth_verifier)
-    client = oauth2.Client(wl_consumer, token)
-    resp, content = client.request(WL_ACCESS_TOKEN_URL if not beta else BETA_ACCESS_TOKEN_URL, method="POST")
-    access_token = dict(cgi.parse_qsl(content))
-
-    conn.access = True
-    conn.token = access_token['oauth_token']
-    conn.token_secret = access_token['oauth_token_secret']
-    conn.save()
-
-    return HttpResponseRedirect('/')
diff --git a/apps/catalogue/__init__.py b/apps/catalogue/__init__.py
deleted file mode 100644 (file)
index c53f0e7..0000000
+++ /dev/null
@@ -1 +0,0 @@
-  # pragma: no cover
diff --git a/apps/catalogue/admin.py b/apps/catalogue/admin.py
deleted file mode 100644 (file)
index 53e8a25..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-from django.contrib import admin
-
-from catalogue import models
-
-class BookAdmin(admin.ModelAdmin):
-    list_display = ['title', 'public', '_published', '_new_publishable', 'project']
-    list_filter = ['public', '_published', '_new_publishable', 'project']
-    prepopulated_fields = {'slug': ['title']}
-    search_fields = ['title']
-
-
-admin.site.register(models.Project)
-admin.site.register(models.Book, BookAdmin)
-admin.site.register(models.Chunk)
-admin.site.register(models.Chunk.tag_model)
-
-admin.site.register(models.Image)
-admin.site.register(models.Image.tag_model)
diff --git a/apps/catalogue/constants.py b/apps/catalogue/constants.py
deleted file mode 100644 (file)
index 0c84232..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
-# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
-#
-TRIM_BEGIN = " TRIM_BEGIN "
-TRIM_END = " TRIM_END "
-
-MASTERS = ['powiesc',
-           'opowiadanie',
-           'liryka_l',
-           'liryka_lp',
-           'dramat_wierszowany_l',
-           'dramat_wierszowany_lp',
-           'dramat_wspolczesny',
-           ]
diff --git a/apps/catalogue/ebook_utils.py b/apps/catalogue/ebook_utils.py
deleted file mode 100644 (file)
index dae2e76..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-# -*- coding: utf-8 -*-
-from StringIO import StringIO
-from catalogue.models import Book
-from librarian import DocProvider
-from django.http import HttpResponse
-
-
-class RedakcjaDocProvider(DocProvider):
-    """Used for getting books' children."""
-
-    def __init__(self, publishable):
-        self.publishable = publishable
-
-    def by_slug(self, slug):
-        return StringIO(Book.objects.get(dc_slug=slug
-                    ).materialize(publishable=self.publishable
-                    ).encode('utf-8'))
-
-
-def serve_file(file_path, name, mime_type):
-    def read_chunks(f, size=8192):
-        chunk = f.read(size)
-        while chunk:
-            yield chunk
-            chunk = f.read(size)
-
-    response = HttpResponse(content_type=mime_type)
-    response['Content-Disposition'] = 'attachment; filename=%s' % name
-    with open(file_path) as f:
-        for chunk in read_chunks(f):
-            response.write(chunk)
-    return response
diff --git a/apps/catalogue/feeds.py b/apps/catalogue/feeds.py
deleted file mode 100644 (file)
index 4884a4c..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-# -*- coding: utf-8 -*-
-from django.contrib.syndication.views import Feed
-from django.shortcuts import get_object_or_404
-from catalogue.models import Book, Chunk
-
-class PublishTrackFeed(Feed):
-    title = u"Planowane publikacje"
-    link = "/"
-
-    def description(self, obj):
-        tag, published = obj
-        return u"Publikacje, które dotarły co najmniej do etapu: %s" % tag.name
-
-    def get_object(self, request, slug):
-        published = request.GET.get('published')
-        if published is not None:
-            published = published == 'true'
-        return get_object_or_404(Chunk.tag_model, slug=slug), published
-
-    def item_title(self, item):
-        return item.title
-
-    def items(self, obj):
-        tag, published = obj
-        books = Book.objects.filter(public=True, _on_track__gte=tag.ordering
-                ).order_by('-_on_track', 'title')
-        if published is not None:
-            books = books.filter(_published=published)
-        return books
diff --git a/apps/catalogue/fixtures/stages.json b/apps/catalogue/fixtures/stages.json
deleted file mode 100644 (file)
index 5a46ec0..0000000
+++ /dev/null
@@ -1,83 +0,0 @@
-[
-    {
-        "pk": 1, 
-        "model": "catalogue.chunktag", 
-        "fields": {
-            "ordering": 1, 
-            "name": "Autokorekta", 
-            "slug": "first_correction"
-        }
-    }, 
-    {
-        "pk": 2, 
-        "model": "catalogue.chunktag", 
-        "fields": {
-            "ordering": 2, 
-            "name": "Tagowanie", 
-            "slug": "tagging"
-        }
-    }, 
-    {
-        "pk": 3, 
-        "model": "catalogue.chunktag", 
-        "fields": {
-            "ordering": 3, 
-            "name": "Korekta", 
-            "slug": "proofreading"
-        }
-    }, 
-    {
-        "pk": 4, 
-        "model": "catalogue.chunktag", 
-        "fields": {
-            "ordering": 4, 
-            "name": "Sprawdzenie przypis\u00f3w \u017ar\u00f3d\u0142a", 
-            "slug": "annotation-proofreading"
-        }
-    }, 
-    {
-        "pk": 5, 
-        "model": "catalogue.chunktag", 
-        "fields": {
-            "ordering": 5, 
-            "name": "Uwsp\u00f3\u0142cze\u015bnienie", 
-            "slug": "modernisation"
-        }
-    }, 
-    {
-        "pk": 6, 
-        "model": "catalogue.chunktag", 
-        "fields": {
-            "ordering": 6, 
-            "name": "Przypisy", 
-            "slug": "annotations"
-        }
-    }, 
-    {
-        "pk": 7, 
-        "model": "catalogue.chunktag", 
-        "fields": {
-            "ordering": 7, 
-            "name": "Motywy", 
-            "slug": "themes"
-        }
-    }, 
-    {
-        "pk": 8, 
-        "model": "catalogue.chunktag", 
-        "fields": {
-            "ordering": 8, 
-            "name": "Ostateczna redakcja literacka", 
-            "slug": "editor-proofreading"
-        }
-    }, 
-    {
-        "pk": 9, 
-        "model": "catalogue.chunktag", 
-        "fields": {
-            "ordering": 9, 
-            "name": "Ostateczna redakcja techniczna", 
-            "slug": "technical-editor-proofreading"
-        }
-    }
-]
diff --git a/apps/catalogue/forms.py b/apps/catalogue/forms.py
deleted file mode 100644 (file)
index ea6a4ae..0000000
+++ /dev/null
@@ -1,234 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
-# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
-#
-from catalogue.models import User
-from django.db.models import Count
-from django import forms
-from django.utils.translation import ugettext_lazy as _
-from django.conf import settings
-
-from catalogue.constants import MASTERS
-from catalogue.models import Book, Chunk, Image
-
-class DocumentCreateForm(forms.ModelForm):
-    """
-        Form used for creating new documents.
-    """
-    file = forms.FileField(required=False)
-    text = forms.CharField(required=False, widget=forms.Textarea)
-
-    class Meta:
-        model = Book
-        exclude = ['parent', 'parent_number', 'project']
-
-    def __init__(self, *args, **kwargs):
-        super(DocumentCreateForm, self).__init__(*args, **kwargs)
-        self.fields['slug'].widget.attrs={'class': 'autoslug'}
-        self.fields['gallery'].widget.attrs={'class': 'autoslug'}
-        self.fields['title'].widget.attrs={'class': 'autoslug-source'}
-
-    def clean(self):
-        super(DocumentCreateForm, self).clean()
-        file = self.cleaned_data['file']
-
-        if file is not None:
-            try:
-                self.cleaned_data['text'] = file.read().decode('utf-8')
-            except UnicodeDecodeError:
-                raise forms.ValidationError(_("Text file must be UTF-8 encoded."))
-
-        if not self.cleaned_data["text"]:
-            self._errors["file"] = self.error_class([_("You must either enter text or upload a file")])
-
-        return self.cleaned_data
-
-
-class DocumentsUploadForm(forms.Form):
-    """
-        Form used for uploading new documents.
-    """
-    file = forms.FileField(required=True, label=_('ZIP file'))
-    dirs = forms.BooleanField(label=_('Directories are documents in chunks'),
-            widget = forms.CheckboxInput(attrs={'disabled':'disabled'}))
-
-    def clean(self):
-        file = self.cleaned_data['file']
-
-        import zipfile
-        try:
-            z = self.cleaned_data['zip'] = zipfile.ZipFile(file)
-        except zipfile.BadZipfile:
-            raise forms.ValidationError("Should be a ZIP file.")
-        if z.testzip():
-            raise forms.ValidationError("ZIP file corrupt.")
-
-        return self.cleaned_data
-
-
-class ChunkForm(forms.ModelForm):
-    """
-        Form used for editing a chunk.
-    """
-    user = forms.ModelChoiceField(queryset=
-        User.objects.annotate(count=Count('chunk')).
-        order_by('last_name', 'first_name'), required=False,
-        label=_('Assigned to')) 
-
-    class Meta:
-        model = Chunk
-        fields = ['title', 'slug', 'gallery_start', 'user', 'stage']
-        exclude = ['number']
-
-    def __init__(self, *args, **kwargs):
-        super(ChunkForm, self).__init__(*args, **kwargs)
-        self.fields['gallery_start'].widget.attrs={'class': 'number-input'}
-        self.fields['slug'].widget.attrs={'class': 'autoslug'}
-        self.fields['title'].widget.attrs={'class': 'autoslug-source'}
-
-    def clean_slug(self):
-        slug = self.cleaned_data['slug']
-        try:
-            chunk = Chunk.objects.get(book=self.instance.book, slug=slug)
-        except Chunk.DoesNotExist:
-            return slug
-        if chunk == self.instance:
-            return slug
-        raise forms.ValidationError(_('Chunk with this slug already exists'))
-
-
-class ChunkAddForm(ChunkForm):
-    """
-        Form used for adding a chunk to a document.
-    """
-
-    def clean_slug(self):
-        slug = self.cleaned_data['slug']
-        try:
-            user = Chunk.objects.get(book=self.instance.book, slug=slug)
-        except Chunk.DoesNotExist:
-            return slug
-        raise forms.ValidationError(_('Chunk with this slug already exists'))
-
-
-class BookAppendForm(forms.Form):
-    """
-        Form for appending a book to another book.
-        It means moving all chunks from book A to book B and deleting A.
-    """
-    append_to = forms.ModelChoiceField(queryset=Book.objects.all(),
-            label=_("Append to"))
-
-    def __init__(self, book, *args, **kwargs):
-        ret =  super(BookAppendForm, self).__init__(*args, **kwargs)
-        self.fields['append_to'].queryset = Book.objects.exclude(pk=book.pk)
-        return ret
-
-
-class BookForm(forms.ModelForm):
-    """Form used for editing a Book."""
-
-    class Meta:
-        model = Book
-        exclude = ['project']
-
-    def __init__(self, *args, **kwargs):
-        ret = super(BookForm, self).__init__(*args, **kwargs)
-        self.fields['slug'].widget.attrs.update({"class": "autoslug"})
-        self.fields['title'].widget.attrs.update({"class": "autoslug-source"})
-        return ret
-
-    def save(self, **kwargs):
-        orig_instance = Book.objects.get(pk=self.instance.pk)
-        old_gallery = orig_instance.gallery
-        new_gallery = self.cleaned_data['gallery']
-        if new_gallery != old_gallery:
-            import shutil
-            import os.path
-            from django.conf import settings
-            shutil.move(orig_instance.gallery_path(),
-                        os.path.join(settings.MEDIA_ROOT, settings.IMAGE_DIR, new_gallery))
-        super(BookForm, self).save(**kwargs)
-
-
-class ReadonlyBookForm(BookForm):
-    """Form used for not editing a Book."""
-
-    def __init__(self, *args, **kwargs):
-        ret = super(ReadonlyBookForm, self).__init__(*args, **kwargs)
-        for field in self.fields.values():
-            field.widget.attrs.update({"disabled": "disabled"})
-        return ret
-
-
-class ChooseMasterForm(forms.Form):
-    """
-        Form used for fixing the chunks in a book.
-    """
-
-    master = forms.ChoiceField(choices=((m, m) for m in MASTERS))
-
-
-class ImageForm(forms.ModelForm):
-    """Form used for editing an Image."""
-    user = forms.ModelChoiceField(queryset=
-        User.objects.annotate(count=Count('chunk')).
-        order_by('-count', 'last_name', 'first_name'), required=False,
-        label=_('Assigned to')) 
-
-    class Meta:
-        model = Image
-        fields = ['title', 'slug', 'user', 'stage']
-
-    def __init__(self, *args, **kwargs):
-        super(ImageForm, self).__init__(*args, **kwargs)
-        self.fields['slug'].widget.attrs={'class': 'autoslug'}
-        self.fields['title'].widget.attrs={'class': 'autoslug-source'}
-
-
-class ReadonlyImageForm(ImageForm):
-    """Form used for not editing an Image."""
-
-    def __init__(self, *args, **kwargs):
-        super(ReadonlyImageForm, self).__init__(*args, **kwargs)
-        for field in self.fields.values():
-            field.widget.attrs.update({"disabled": "disabled"})
-
-
-class MarkFinalForm(forms.Form):
-    username = forms.CharField(initial=settings.LITERARY_DIRECTOR_USERNAME)
-    comment = forms.CharField(initial=u'Ostateczna akceptacja merytoryczna przez kierownika literackiego.')
-    books = forms.CharField(widget=forms.Textarea, help_text=u'linki do książek w redakcji, po jednym na wiersz')
-
-    def clean_books(self):
-        books_value = self.cleaned_data['books']
-        slugs = [line.strip().strip('/').split('/')[-1] for line in books_value.split('\n') if line.strip()]
-        books = Book.objects.filter(slug__in=slugs)
-        if len(books) != len(slugs):
-            raise forms.ValidationError(
-                'Incorrect slug(s): %s' % ' '.join(slug for slug in slugs if not Book.objects.filter(slug=slug)))
-        return books
-
-    def clean_username(self):
-        username = self.cleaned_data['username']
-        if not User.objects.filter(username=username):
-            raise forms.ValidationError('Invalid username')
-        return username
-
-    def save(self):
-        for book in self.cleaned_data['books']:
-            for chunk in book.chunk_set.all():
-                src = chunk.head.materialize()
-                chunk.commit(
-                    text=src,
-                    author=User.objects.get(username=self.cleaned_data['username']),
-                    description=self.cleaned_data['comment'],
-                    tags=[Chunk.tag_model.objects.get(slug='editor-proofreading')],
-                    publishable=True
-                )
-
-
-class PublishOptionsForm(forms.Form):
-    days = forms.IntegerField(label=u'po ilu dniach udostępnienić (0 = od razu)', min_value=0, initial=0)
-    beta = forms.BooleanField(label=u'Opublikuj na wersji testowej', required=False)
diff --git a/apps/catalogue/helpers.py b/apps/catalogue/helpers.py
deleted file mode 100644 (file)
index d340b46..0000000
+++ /dev/null
@@ -1,148 +0,0 @@
-from datetime import date
-from functools import wraps
-from os.path import join
-from os import listdir, stat
-from shutil import move, rmtree
-from django.conf import settings
-import re
-import filecmp
-
-from django.db.models import Count
-
-
-def active_tab(tab):
-    """
-        View decorator, which puts tab info on a request.
-    """
-    def wrapper(f):
-        @wraps(f)
-        def wrapped(request, *args, **kwargs):
-            request.catalogue_active_tab = tab
-            return f(request, *args, **kwargs)
-        return wrapped
-    return wrapper
-
-
-def cached_in_field(field_name):
-    def decorator(f):
-        @property
-        @wraps(f)
-        def wrapped(self, *args, **kwargs):
-            value = getattr(self, field_name)
-            if value is None:
-                value = f(self, *args, **kwargs)
-                type(self)._default_manager.filter(pk=self.pk).update(**{field_name: value})
-            return value
-        return wrapped
-    return decorator
-
-
-def parse_isodate(isodate):
-    try:
-        return date(*[int(p) for p in isodate.split('-')])
-    except (AttributeError, TypeError, ValueError):
-        raise ValueError("Not a date in ISO format.")
-
-
-class GalleryMerger(object):
-    def __init__(self, dest_gallery, src_gallery):
-        self.dest = dest_gallery
-        self.src = src_gallery
-        self.dest_size = None
-        self.src_size = None
-        self.num_deleted = 0
-
-    @staticmethod
-    def path(gallery):
-        return join(settings.MEDIA_ROOT, settings.IMAGE_DIR, gallery)
-
-    @staticmethod
-    def get_prefix(name):
-        m = re.match(r"^([0-9])-", name)
-        if m:
-            return int(m.groups()[0])
-        return None
-
-    @staticmethod
-    def set_prefix(name, prefix, always=False):
-        m = not always and re.match(r"^([0-9])-", name)
-        return "%1d-%s" % (prefix, m and name[2:] or name)
-
-    @property
-    def was_merged(self):
-        "Check if we have gallery size recorded"
-        return self.dest_size is not None
-
-    def merge(self):
-        if not self.dest:
-            return self.src
-        if not self.src:
-            return self.dest
-
-        files = listdir(self.path(self.dest))
-        files.sort()
-        self.dest_size = len(files)
-        files_other = listdir(self.path(self.src))
-        files_other.sort()
-        self.src_size = len(files_other)
-
-        if files and files_other:
-            print "compare %s with %s" % (files[-1], files_other[0])
-            if filecmp.cmp(
-                    join(self.path(self.dest), files[-1]),
-                    join(self.path(self.src), files_other[0]),
-                    False
-                    ):
-                files_other.pop(0)
-                self.num_deleted = 1
-
-        prefixes = {}
-        renamed_files = {}
-        renamed_files_other = {}
-        last_pfx = -1
-
-        # check if all elements of my files have a prefix
-        files_prefixed = True
-        for f in files:
-            p = self.get_prefix(f)
-            if p:
-                if p > last_pfx: last_pfx = p
-            else:
-                files_prefixed = False
-                break
-
-        # if not, add a 0 prefix to them
-        if not files_prefixed:
-            prefixes[0] = 0
-            for f in files:
-                renamed_files[f] = self.set_prefix(f, 0, True)
-
-        # two cases here - either all are prefixed or not.
-        files_other_prefixed = True
-        for f in files_other:
-            pfx = self.get_prefix(f)
-            if pfx is not None:
-                if not pfx in prefixes:
-                    last_pfx += 1
-                    prefixes[pfx] = last_pfx
-                renamed_files_other[f] = self.set_prefix(f, prefixes[pfx])
-            else:
-                # ops, not all files here were prefixed.
-                files_other_prefixed = False
-                break
-
-        # just set a 1- prefix to all of them
-        if not files_other_prefixed:
-            for f in files_other:
-                renamed_files_other[f] = self.set_prefix(f, 1, True)
-
-        # finally, move / rename files.
-        for frm, to in renamed_files.items():
-            move(join(self.path(self.dest), frm),
-                        join(self.path(self.dest), to))
-        for frm, to in renamed_files_other.items():
-            move(join(self.path(self.src), frm),
-                        join(self.path(self.dest), to))            
-
-        rmtree(join(self.path(self.src)))
-        return self.dest
diff --git a/apps/catalogue/locale/pl/LC_MESSAGES/django.mo b/apps/catalogue/locale/pl/LC_MESSAGES/django.mo
deleted file mode 100644 (file)
index 87bdfbf..0000000
Binary files a/apps/catalogue/locale/pl/LC_MESSAGES/django.mo and /dev/null differ
diff --git a/apps/catalogue/locale/pl/LC_MESSAGES/django.po b/apps/catalogue/locale/pl/LC_MESSAGES/django.po
deleted file mode 100644 (file)
index 6387b5d..0000000
+++ /dev/null
@@ -1,804 +0,0 @@
-# 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: Platforma Redakcyjna\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2018-12-20 12:30+0100\n"
-"PO-Revision-Date: 2014-03-27 13:17+0100\n"
-"Last-Translator: Radek Czajka <radoslaw.czajka@nowoczesnapolska.org.pl>\n"
-"Language-Team: Fundacja Nowoczesna Polska <fundacja@nowoczesnapolska.org."
-"pl>\n"
-"Language: pl\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
-"|| n%100>=20) ? 1 : 2);\n"
-"X-Generator: Poedit 1.5.4\n"
-
-#: forms.py:40
-msgid "Text file must be UTF-8 encoded."
-msgstr "Plik powinien mieć kodowanie UTF-8."
-
-#: forms.py:43
-msgid "You must either enter text or upload a file"
-msgstr "Proszę wpisać tekst albo wybrać plik do załadowania"
-
-#: forms.py:52
-msgid "ZIP file"
-msgstr "Plik ZIP"
-
-#: forms.py:53
-msgid "Directories are documents in chunks"
-msgstr "Katalogi zawierają dokumenty w częściach"
-
-#: forms.py:77 forms.py:178
-msgid "Assigned to"
-msgstr "Przypisane do"
-
-#: forms.py:98 forms.py:112
-msgid "Chunk with this slug already exists"
-msgstr "Część z tym slugiem już istnieje"
-
-#: forms.py:121
-msgid "Append to"
-msgstr "Dołącz do"
-
-#: views.py:172
-#, python-format
-msgid "Slug already used for %s"
-msgstr "Slug taki sam jak dla pliku %s"
-
-#: views.py:174
-msgid "Slug already used in repository."
-msgstr "Dokument o tym slugu już istnieje w repozytorium."
-
-#: views.py:180
-msgid "File should be UTF-8 encoded."
-msgstr "Plik powinien mieć kodowanie UTF-8."
-
-#: views.py:619 models/book.py:56
-msgid "books"
-msgstr "książki"
-
-#: views.py:621
-msgid "scan gallery"
-msgstr "galeria skanów"
-
-#: models/book.py:28 models/chunk.py:23 models/image.py:22
-msgid "title"
-msgstr "tytuł"
-
-#: models/book.py:29 models/chunk.py:24 models/image.py:23
-msgid "slug"
-msgstr "slug"
-
-#: models/book.py:30 models/image.py:24
-msgid "public"
-msgstr "publiczna"
-
-#: models/book.py:31
-msgid "scan gallery name"
-msgstr "nazwa galerii skanów"
-
-#: models/book.py:35
-msgid "parent"
-msgstr "rodzic"
-
-#: models/book.py:36
-msgid "parent number"
-msgstr "numeracja rodzica"
-
-#: models/book.py:55 models/chunk.py:21 models/publish_log.py:17
-msgid "book"
-msgstr "książka"
-
-#: models/book.py:261
-msgid "No chunks in the book."
-msgstr "Książka nie ma części."
-
-#: models/book.py:265
-msgid "Not all chunks have publishable revisions."
-msgstr "Niektóre części nie są gotowe do publikacji."
-
-#: models/book.py:272 models/image.py:86
-msgid "Invalid XML"
-msgstr "Nieprawidłowy XML"
-
-#: models/book.py:274 models/image.py:88
-msgid "No Dublin Core found."
-msgstr "Brak sekcji Dublin Core."
-
-#: models/book.py:276 models/image.py:90
-msgid "Invalid Dublin Core"
-msgstr "Nieprawidłowy Dublin Core"
-
-#: models/book.py:279 models/image.py:94
-msgid "rdf:about is not"
-msgstr "rdf:about jest różny od"
-
-#: models/chunk.py:22
-msgid "number"
-msgstr "numer"
-
-#: models/chunk.py:25
-msgid "gallery start"
-msgstr "początek galerii"
-
-#: models/chunk.py:40
-msgid "chunk"
-msgstr "część"
-
-#: models/chunk.py:41
-msgid "chunks"
-msgstr "części"
-
-#: models/image.py:21 models/image.py:36 models/publish_log.py:45
-msgid "image"
-msgstr "obraz"
-
-#: models/image.py:37
-msgid "images"
-msgstr "obrazy"
-
-#: models/image.py:79
-msgid "There is no publishable revision"
-msgstr "Żadna wersja nie została oznaczona do publikacji."
-
-#: models/project.py:13
-msgid "name"
-msgstr "nazwa"
-
-#: models/project.py:14
-msgid "notes"
-msgstr "notatki"
-
-#: models/project.py:19 templates/catalogue/image_table.html:58
-#: templates/catalogue/book_list/book_list.html:65
-msgid "project"
-msgstr "projekt"
-
-#: models/project.py:20
-msgid "projects"
-msgstr "projekty"
-
-#: models/publish_log.py:18 models/publish_log.py:46
-msgid "time"
-msgstr "czas"
-
-#: models/publish_log.py:19 models/publish_log.py:47
-#: templates/catalogue/wall.html:20
-msgid "user"
-msgstr "użytkownik"
-
-#: models/publish_log.py:24 models/publish_log.py:33
-msgid "book publish record"
-msgstr "zapis publikacji książki"
-
-#: models/publish_log.py:25
-msgid "book publish records"
-msgstr "zapisy publikacji książek"
-
-#: models/publish_log.py:34 models/publish_log.py:48
-msgid "change"
-msgstr "zmiana"
-
-#: models/publish_log.py:38
-msgid "chunk publish record"
-msgstr "zapis publikacji części"
-
-#: models/publish_log.py:39
-msgid "chunk publish records"
-msgstr "zapisy publikacji części"
-
-#: models/publish_log.py:53
-msgid "image publish record"
-msgstr "zapis publikacji obrazu"
-
-#: models/publish_log.py:54
-msgid "image publish records"
-msgstr "zapisy publikacji obrazów"
-
-#: templates/catalogue/active_users_list.html:5
-msgid "Active users"
-msgstr "Aktywni użytkownicy"
-
-#: templates/catalogue/active_users_list.html:11
-msgid "Active users since"
-msgstr "Użytkownicy aktywni od"
-
-#: templates/catalogue/activity.html:6 templates/catalogue/activity.html:12
-#: templatetags/catalogue.py:29
-msgid "Activity"
-msgstr "Aktywność"
-
-#: templates/catalogue/base.html:10
-msgid "Platforma Redakcyjna"
-msgstr "Platforma Redakcyjna"
-
-#: templates/catalogue/book_append_to.html:4
-#: templates/catalogue/book_append_to.html:11
-msgid "Append book"
-msgstr "Dołącz książkę"
-
-#: templates/catalogue/book_detail.html:18
-#: templates/catalogue/book_edit.html:13 templates/catalogue/chunk_edit.html:16
-#: templates/catalogue/image_detail.html:18
-msgid "Save"
-msgstr "Zapisz"
-
-#: templates/catalogue/book_detail.html:25
-msgid "Edit gallery"
-msgstr "Edytuj galerię"
-
-#: templates/catalogue/book_detail.html:28
-msgid "Append to other book"
-msgstr "Dołącz do innej książki"
-
-#: templates/catalogue/book_detail.html:34
-msgid "Chunks"
-msgstr "Części"
-
-#: templates/catalogue/book_detail.html:49
-#: templates/catalogue/image_detail.html:36 templatetags/wall.py:108
-#: templatetags/wall.py:129
-msgid "Publication"
-msgstr "Publikacja"
-
-#: templates/catalogue/book_detail.html:58
-#: templates/catalogue/image_detail.html:38
-msgid "Last published"
-msgstr "Ostatnio opublikowano"
-
-#: templates/catalogue/book_detail.html:68
-msgid "Full XML"
-msgstr "Pełny XML"
-
-#: templates/catalogue/book_detail.html:69
-msgid "HTML version"
-msgstr "Wersja HTML"
-
-#: templates/catalogue/book_detail.html:70
-msgid "TXT version"
-msgstr "Wersja TXT"
-
-#: templates/catalogue/book_detail.html:71
-msgid "PDF version"
-msgstr "Wersja PDF"
-
-#: templates/catalogue/book_detail.html:72
-msgid "PDF version for mobiles"
-msgstr "Wersja PDF na telefony"
-
-#: templates/catalogue/book_detail.html:73
-msgid "EPUB version"
-msgstr "Wersja EPUB"
-
-#: templates/catalogue/book_detail.html:74
-msgid "MOBI version"
-msgstr "Wersja MOBI"
-
-#: templates/catalogue/book_detail.html:88
-#: templates/catalogue/image_detail.html:57
-msgid "Publish"
-msgstr "Opublikuj"
-
-#: templates/catalogue/book_detail.html:92
-#: templates/catalogue/image_detail.html:61
-msgid "Log in to publish."
-msgstr "Zaloguj się, aby opublikować."
-
-#: templates/catalogue/book_detail.html:95
-#: templates/catalogue/image_detail.html:64
-msgid "This book can't be published yet, because:"
-msgstr "Ta książka nie może jeszcze zostać opublikowana. Powód:"
-
-#: templates/catalogue/book_edit.html:5
-msgid "Edit book"
-msgstr "Edytuj książkę"
-
-#: templates/catalogue/book_html.html:12 templates/catalogue/book_text.html:15
-msgid "Table of contents"
-msgstr "Spis treści"
-
-#: templates/catalogue/book_html.html:13 templates/catalogue/book_text.html:17
-msgid "Edit. note"
-msgstr "Nota red."
-
-#: templates/catalogue/book_html.html:14
-msgid "Infobox"
-msgstr "Informacje"
-
-#: templates/catalogue/book_text.html:7
-msgid "Redakcja"
-msgstr ""
-
-#: templates/catalogue/chunk_add.html:5 templates/catalogue/chunk_add.html:9
-#: templates/catalogue/chunk_edit.html:22
-msgid "Split chunk"
-msgstr "Podziel część"
-
-#: templates/catalogue/chunk_add.html:14
-msgid "Insert empty chunk after"
-msgstr "Wstaw pustą część po"
-
-#: templates/catalogue/chunk_add.html:17
-msgid "Add chunk"
-msgstr "Dodaj część"
-
-#: templates/catalogue/chunk_edit.html:5 templates/catalogue/chunk_edit.html:9
-#: templates/catalogue/book_list/book.html:9
-#: templates/catalogue/book_list/chunk.html:7
-msgid "Chunk settings"
-msgstr "Ustawienia części"
-
-#: templates/catalogue/chunk_edit.html:14
-msgid "Book"
-msgstr "Książka"
-
-#: templates/catalogue/document_create_missing.html:5
-#: templates/catalogue/document_create_missing.html:9
-msgid "Create a new book"
-msgstr "Utwórz nową książkę"
-
-#: templates/catalogue/document_create_missing.html:15
-msgid "Create book"
-msgstr "Utwórz książkę"
-
-#: templates/catalogue/document_list.html:7
-msgid "Book list"
-msgstr "Lista książek"
-
-#: templates/catalogue/document_upload.html:5
-msgid "Bulk document upload"
-msgstr "Hurtowe dodawanie dokumentów"
-
-#: templates/catalogue/document_upload.html:11
-msgid "Bulk documents upload"
-msgstr "Hurtowe dodawanie dokumentów"
-
-#: templates/catalogue/document_upload.html:14
-msgid ""
-"Please submit a ZIP with UTF-8 encoded XML files. Files not ending with "
-"<code>.xml</code> will be ignored."
-msgstr ""
-"Proszę wskazać archiwum ZIP z plikami XML w kodowaniu UTF-8. Pliki nie "
-"kończące się na <code>.xml</code> zostaną zignorowane."
-
-#: templates/catalogue/document_upload.html:20
-#: templates/catalogue/upload_pdf.html:16 templatetags/catalogue.py:36
-msgid "Upload"
-msgstr "Załaduj"
-
-#: templates/catalogue/document_upload.html:27
-msgid ""
-"There have been some errors. No files have been added to the repository."
-msgstr "Wystąpiły błędy. Żadne pliki nie zostały dodane do repozytorium."
-
-#: templates/catalogue/document_upload.html:28
-msgid "Offending files"
-msgstr "Błędne pliki"
-
-#: templates/catalogue/document_upload.html:36
-msgid "Correct files"
-msgstr "Poprawne pliki"
-
-#: templates/catalogue/document_upload.html:47
-msgid "Files have been successfully uploaded to the repository."
-msgstr "Pliki zostały dodane do repozytorium."
-
-#: templates/catalogue/document_upload.html:48
-msgid "Uploaded files"
-msgstr "Dodane pliki"
-
-#: templates/catalogue/document_upload.html:58
-msgid "Skipped files"
-msgstr "Pominięte pliki"
-
-#: templates/catalogue/document_upload.html:59
-msgid "Files skipped due to no <code>.xml</code> extension"
-msgstr "Pliki pominięte z powodu braku rozszerzenia <code>.xml</code>."
-
-#: templates/catalogue/image_detail.html:26
-msgid "Editor"
-msgstr "Edytor"
-
-#: templates/catalogue/image_detail.html:28
-msgid "Proceed to the editor."
-msgstr "Przejdź do edytora."
-
-#: templates/catalogue/image_list.html:8
-msgid "Image list"
-msgstr "Lista obrazów"
-
-#: templates/catalogue/image_short.html:6
-msgid "Image settings"
-msgstr "Ustawienia obrazu"
-
-#: templates/catalogue/image_table.html:23
-#: templates/catalogue/book_list/book_list.html:28
-msgid "Search in book titles"
-msgstr "Szukaj w tytułach książek"
-
-#: templates/catalogue/image_table.html:28
-#: templates/catalogue/book_list/book_list.html:33
-msgid "stage"
-msgstr "etap"
-
-#: templates/catalogue/image_table.html:30
-#: templates/catalogue/image_table.html:41
-#: templates/catalogue/image_table.html:60
-#: templates/catalogue/book_list/book_list.html:35
-#: templates/catalogue/book_list/book_list.html:46
-#: templates/catalogue/book_list/book_list.html:67
-msgid "none"
-msgstr "brak"
-
-#: templates/catalogue/image_table.html:39
-#: templates/catalogue/book_list/book_list.html:44
-msgid "editor"
-msgstr "redaktor"
-
-#: templates/catalogue/image_table.html:50
-#: templates/catalogue/book_list/book_list.html:57
-msgid "status"
-msgstr "status"
-
-#: templates/catalogue/image_table.html:77
-#, python-format
-msgid "%(c)s image"
-msgid_plural "%(c)s images"
-msgstr[0] "%(c)s obraz"
-msgstr[1] "%(c)s obrazy"
-msgstr[2] "%(c)s obrazów"
-
-#: templates/catalogue/image_table.html:82
-msgid "No images found."
-msgstr "Nie znaleziono obrazów."
-
-#: templates/catalogue/image_table.html:88
-#: templates/catalogue/book_list/book_list.html:102
-msgid "Set stage"
-msgstr "Ustaw etap"
-
-#: templates/catalogue/image_table.html:89
-#: templates/catalogue/book_list/book_list.html:103
-msgid "Set user"
-msgstr "Przypisz redaktora"
-
-#: templates/catalogue/image_table.html:91
-#: templates/catalogue/book_list/book_list.html:105
-msgid "Project"
-msgstr "Projekt"
-
-#: templates/catalogue/image_table.html:92
-#: templates/catalogue/book_list/book_list.html:106
-msgid "More users"
-msgstr "Więcej użytkowników"
-
-#: templates/catalogue/my_page.html:15 templatetags/catalogue.py:27
-msgid "My page"
-msgstr "Moja strona"
-
-#: templates/catalogue/my_page.html:24
-msgid "Your last edited documents"
-msgstr "Twoje ostatnie edycje"
-
-#: templates/catalogue/my_page.html:39 templates/catalogue/user_page.html:16
-msgid "Recent activity for"
-msgstr "Ostatnia aktywność dla:"
-
-#: templates/catalogue/upload_pdf.html:5 templates/catalogue/upload_pdf.html:11
-msgid "PDF file upload"
-msgstr "Ładowanie pliku PDF"
-
-#: templates/catalogue/user_list.html:7 templates/catalogue/user_list.html:12
-#: templatetags/catalogue.py:32
-msgid "Users"
-msgstr "Użytkownicy"
-
-#: templates/catalogue/wall.html:30
-msgid "not logged in"
-msgstr "nie zalogowany"
-
-#: templates/catalogue/wall.html:35
-msgid "No activity recorded."
-msgstr "Nie zanotowano aktywności."
-
-#: templates/catalogue/book_list/book.html:8
-#: templates/catalogue/book_list/book.html:29
-msgid "Book settings"
-msgstr "Ustawienia książki"
-
-#: templates/catalogue/book_list/book_list.html:23
-msgid "Show hidden books"
-msgstr "Pokaż ukryte książki"
-
-#: templates/catalogue/book_list/book_list.html:91
-#, python-format
-msgid "%(c)s book"
-msgid_plural "%(c)s books"
-msgstr[0] "%(c)s książka"
-msgstr[1] "%(c)s książki"
-msgstr[2] "%(c)s książek"
-
-#: templates/catalogue/book_list/book_list.html:96
-msgid "No books found."
-msgstr "Nie znaleziono książek."
-
-#: templatetags/book_list.py:84 templatetags/book_list.py:152
-msgid "publishable"
-msgstr "do publikacji"
-
-#: templatetags/book_list.py:85 templatetags/book_list.py:153
-msgid "changed"
-msgstr "zmienione"
-
-#: templatetags/book_list.py:86 templatetags/book_list.py:154
-msgid "published"
-msgstr "opublikowane"
-
-#: templatetags/book_list.py:87 templatetags/book_list.py:155
-msgid "unpublished"
-msgstr "nie opublikowane"
-
-#: templatetags/book_list.py:88 templatetags/book_list.py:156
-msgid "empty"
-msgstr "puste"
-
-#: templatetags/catalogue.py:30
-msgid "All"
-msgstr "Wszystkie"
-
-#: templatetags/catalogue.py:31
-msgid "Images"
-msgstr "Obrazy"
-
-#: templatetags/catalogue.py:35
-msgid "Add"
-msgstr "Dodaj"
-
-#: templatetags/catalogue.py:38
-msgid "Covers"
-msgstr "Okładki"
-
-#: templatetags/wall.py:49 templatetags/wall.py:78
-msgid "Related edit"
-msgstr "Powiązana zmiana"
-
-#: templatetags/wall.py:51 templatetags/wall.py:80
-msgid "Edit"
-msgstr "Zmiana"
-
-#: templatetags/wall.py:150
-msgid "Comment"
-msgstr "Komentarz"
-
-#~ msgid "Comments"
-#~ msgstr "Komentarze"
-
-#~ msgid "Mark publishable"
-#~ msgstr "Oznacz do publikacji"
-
-#~ msgid "Mark not publishable"
-#~ msgstr "Odznacz do publikacji"
-
-#~ msgid "Other user"
-#~ msgstr "Inny użytkownik"
-
-#~ msgid "Admin"
-#~ msgstr "Administracja"
-
-#~ msgid "edit"
-#~ msgstr "edytuj"
-
-#~ msgid "add basic document structure"
-#~ msgstr "dodaj podstawową strukturę dokumentu"
-
-#~ msgid "change master tag to"
-#~ msgstr "zmień tak master na"
-
-#~ msgid "add begin trimming tag"
-#~ msgstr "dodaj początkowy ogranicznik"
-
-#~ msgid "add end trimming tag"
-#~ msgstr "dodaj końcowy ogranicznik"
-
-#~ msgid "unstructured text"
-#~ msgstr "tekst bez struktury"
-
-#~ msgid "unknown XML"
-#~ msgstr "nieznany XML"
-
-#~ msgid "broken document"
-#~ msgstr "uszkodzony dokument"
-
-#~ msgid "Apply fixes"
-#~ msgstr "Wykonaj zmiany"
-
-#~ msgid "Can mark for publishing"
-#~ msgstr "Oznacza do publikacji"
-
-#~ msgid "Author"
-#~ msgstr "Autor"
-
-#~ msgid "Your name"
-#~ msgstr "Imię i nazwisko"
-
-#~ msgid "Author's email"
-#~ msgstr "E-mail autora"
-
-#~ msgid "Your email address, so we can show a gravatar :)"
-#~ msgstr "Adres e-mail, żebyśmy mogli pokazać gravatar :)"
-
-#~ msgid "Describe changes you made."
-#~ msgstr "Opisz swoje zmiany"
-
-#~ msgid "Completed"
-#~ msgstr "Ukończono"
-
-#~ msgid "If you completed a life cycle stage, select it."
-#~ msgstr "Jeśli został ukończony etap prac, wskaż go."
-
-#~ msgid "Describe the reason for reverting."
-#~ msgstr "Opisz powód przywrócenia."
-
-#~ msgid "theme"
-#~ msgstr "motyw"
-
-#~ msgid "themes"
-#~ msgstr "motywy"
-
-#~ msgid "Tag added"
-#~ msgstr "Dodano tag"
-
-#~ msgid "Revision marked"
-#~ msgstr "Wersja oznaczona"
-
-#~ msgid "New version"
-#~ msgstr "Nowa wersja"
-
-#~ msgid "Click to open/close gallery"
-#~ msgstr "Kliknij, aby (ro)zwinąć galerię"
-
-#~ msgid "Help"
-#~ msgstr "Pomoc"
-
-#~ msgid "Version"
-#~ msgstr "Wersja"
-
-#~ msgid "Unknown"
-#~ msgstr "nieznana"
-
-#~ msgid "Save attempt in progress"
-#~ msgstr "Trwa zapisywanie"
-
-#~ msgid "There is a newer version of this document!"
-#~ msgstr "Istnieje nowsza wersja tego dokumentu!"
-
-#~ msgid "Clear filter"
-#~ msgstr "Wyczyść filtr"
-
-#~ msgid "Cancel"
-#~ msgstr "Anuluj"
-
-#~ msgid "Revert"
-#~ msgstr "Przywróć"
-
-#~ msgid "all"
-#~ msgstr "wszystkie"
-
-#~ msgid "Annotations"
-#~ msgstr "Przypisy"
-
-#~ msgid "Previous"
-#~ msgstr "Poprzednie"
-
-#~ msgid "Next"
-#~ msgstr "Następne"
-
-#~ msgid "Zoom in"
-#~ msgstr "Powiększ"
-
-#~ msgid "Zoom out"
-#~ msgstr "Zmniejsz"
-
-#~ msgid "Gallery"
-#~ msgstr "Galeria"
-
-#~ msgid "Compare versions"
-#~ msgstr "Porównaj wersje"
-
-#~ msgid "Revert document"
-#~ msgstr "Przywróć wersję"
-
-#~ msgid "View version"
-#~ msgstr "Zobacz wersję"
-
-#~ msgid "History"
-#~ msgstr "Historia"
-
-#~ msgid "Search"
-#~ msgstr "Szukaj"
-
-#~ msgid "Replace with"
-#~ msgstr "Zamień na"
-
-#~ msgid "Replace"
-#~ msgstr "Zamień"
-
-#~ msgid "Options"
-#~ msgstr "Opcje"
-
-#~ msgid "Case sensitive"
-#~ msgstr "Rozróżniaj wielkość liter"
-
-#~ msgid "From cursor"
-#~ msgstr "Zacznij od kursora"
-
-#~ msgid "Search and replace"
-#~ msgstr "Znajdź i zamień"
-
-#~ msgid "Source code"
-#~ msgstr "Kod źródłowy"
-
-#~ msgid "Title"
-#~ msgstr "Tytuł"
-
-#~ msgid "Document ID"
-#~ msgstr "ID dokumentu"
-
-#~ msgid "Current version"
-#~ msgstr "Aktualna wersja"
-
-#~ msgid "Last edited by"
-#~ msgstr "Ostatnio edytowane przez"
-
-#~ msgid "Summary"
-#~ msgstr "Podsumowanie"
-
-#~ msgid "Insert theme"
-#~ msgstr "Wstaw motyw"
-
-#~ msgid "Insert annotation"
-#~ msgstr "Wstaw przypis"
-
-#~ msgid "Visual editor"
-#~ msgstr "Edytor wizualny"
-
-#~ msgid "Unassigned"
-#~ msgstr "Nie przypisane"
-
-#~ msgid "First correction"
-#~ msgstr "Autokorekta"
-
-#~ msgid "Tagging"
-#~ msgstr "Tagowanie"
-
-#~ msgid "Initial Proofreading"
-#~ msgstr "Korekta"
-
-#~ msgid "Annotation Proofreading"
-#~ msgstr "Sprawdzenie przypisów źródła"
-
-#~ msgid "Modernisation"
-#~ msgstr "Uwspółcześnienie"
-
-#~ msgid "Themes"
-#~ msgstr "Motywy"
-
-#~ msgid "Editor's Proofreading"
-#~ msgstr "Ostateczna redakcja literacka"
-
-#~ msgid "Technical Editor's Proofreading"
-#~ msgstr "Ostateczna redakcja techniczna"
-
-#~ msgid "Finished stage: %s"
-#~ msgstr "Ukończony etap: %s"
-
-#~ msgid "Refresh"
-#~ msgstr "Odśwież"
diff --git a/apps/catalogue/management/__init__.py b/apps/catalogue/management/__init__.py
deleted file mode 100644 (file)
index bc3d6c0..0000000
+++ /dev/null
@@ -1,120 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
-# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
-#
-from collections import defaultdict
-from django.db import transaction
-from lxml import etree
-
-
-class XmlUpdater(object):
-    """A base class for massive XML updates.
-
-    In a subclass, override `fix_tree` and/or use `fixes_field` decorator.
-    Attributes:
-    * commit_desc: commits description
-    * retain_publishable: set publishable if head is (default: True)
-    * only_first_chunk: process only first chunks of books (default: False)
-    """
-    commit_desc = "auto-update"
-    retain_publishable = True
-    only_first_chunk = False
-
-    _element_fixers = defaultdict(list)
-
-    def __init__(self):
-        self.counters = defaultdict(lambda: 0)
-
-    @classmethod
-    def fixes_elements(cls, xpath):
-        """Decorator, registering a function as a fixer for given field type.
-
-        Any decorated function will be called like
-            f(element, change=..., verbose=...)
-        providing changeset as context.
-
-        :param xpath: element lookup, e.g. ".//{namespace-uri}tag-name"
-        :returns: True if anything changed
-        """
-        def wrapper(fixer):
-            cls._element_fixers[xpath].append(fixer)
-            return fixer
-        return wrapper
-
-    def fix_tree(self, tree, verbose):
-        """Override to provide general tree-fixing mechanism.
-
-        :param tree: the parsed XML tree
-        :param verbose: verbosity level
-        :returns: True if anythig changed
-        """
-        return False
-
-    def fix_chunk(self, chunk, user, verbose=0, dry_run=False):
-        """Runs the update for a single chunk."""
-        if verbose >= 2:
-            print chunk.get_absolute_url()
-        old_head = chunk.head
-        src = old_head.materialize()
-        try:
-            tree = etree.fromstring(src)
-        except:
-            if verbose:
-                print "%s: invalid XML" % chunk.get_absolute_url()
-            self.counters['Bad XML'] += 1
-            return
-
-        dirty = False
-        # Call the general fixing function.
-        if self.fix_tree(tree, verbose=verbose):
-            dirty = True
-        # Call the registered fixers.
-        for xpath, fixers in self._element_fixers.items():
-            for elem in tree.findall(xpath):
-                for fixer in fixers:
-                    if fixer(elem, change=old_head, verbose=verbose):
-                        dirty = True
-
-        if not dirty:
-            self.counters['Clean'] += 1
-            return
-
-        if not dry_run:
-            new_head = chunk.commit(
-                etree.tostring(tree, encoding=unicode),
-                author=user,
-                description=self.commit_desc
-            )
-            if self.retain_publishable:
-                if old_head.publishable:
-                    new_head.set_publishable(True)
-        if verbose >= 2:
-            print "done"
-        self.counters['Updated chunks'] += 1
-
-    def run(self, user, verbose=0, dry_run=False, books=None):
-        """Runs the actual update."""
-        if books is None:
-            from catalogue.models import Book
-            books = Book.objects.all()
-
-        # Start transaction management.
-        transaction.enter_transaction_management()
-
-        for book in books:
-            self.counters['All books'] += 1
-            chunks = book.chunk_set.all()
-            if self.only_first_chunk:
-                chunks = chunks[:1]
-            for chunk in chunks:
-                self.counters['All chunks'] += 1
-                self.fix_chunk(chunk, user, verbose, dry_run)
-
-        transaction.commit()
-        transaction.leave_transaction_management()
-
-    def print_results(self):
-        """Prints the counters."""
-        for item in sorted(self.counters.items()):
-            print "%s: %d" % item
diff --git a/apps/catalogue/management/commands/__init__.py b/apps/catalogue/management/commands/__init__.py
deleted file mode 100644 (file)
index e6f146f..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
-# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
-#
-import sys
-from optparse import make_option
-from django.contrib.auth.models import User
-from django.core.management.base import BaseCommand
-from catalogue.models import Book
-
-
-class XmlUpdaterCommand(BaseCommand):
-    """Base class for creating massive XML-updating commands.
-
-    In a subclass, provide an XmlUpdater class in the `updater' attribute.
-    """
-    option_list = BaseCommand.option_list + (
-        make_option('-q', '--quiet', action='store_false', dest='verbose',
-            default=True, help='Less output'),
-        make_option('-d', '--dry-run', action='store_true', dest='dry_run',
-            default=False, help="Don't actually touch anything"),
-        make_option('-u', '--username', dest='username', metavar='USER',
-            help='Assign commits to this user (required, preferably yourself).'),
-    )
-    args = "[slug]..."
-
-    def handle(self, *args, **options):
-        verbose = options.get('verbose')
-        dry_run = options.get('dry_run')
-        username = options.get('username')
-
-        if username:
-            user = User.objects.get(username=username)
-        else:
-            print 'Please provide a username.'
-            sys.exit(1)
-
-        books = Book.objects.filter(slug__in=args) if args else None
-
-        updater = self.updater()
-        updater.run(user, verbose=verbose, dry_run=dry_run, books=books)
-        updater.print_results()
diff --git a/apps/catalogue/management/commands/add_parent.py b/apps/catalogue/management/commands/add_parent.py
deleted file mode 100644 (file)
index 2ab0510..0000000
+++ /dev/null
@@ -1,101 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
-# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
-#
-import sys
-
-from datetime import date
-from lxml import etree
-
-from django.core.management import BaseCommand
-
-from catalogue.models import Book
-from librarian import RDFNS, DCNS
-
-TEMPLATE = '''<utwor>
-<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
-<rdf:Description rdf:about="http://redakcja.wolnelektury.pl/documents/book/%(slug)s/">
-%(dc)s
-</rdf:Description>
-</rdf:RDF>
-
-</utwor>
-'''
-
-DC_TEMPLATE = '<dc:%(tag)s xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">%(value)s</dc:%(tag)s>'
-
-DC_TAGS = (
-    'creator',
-    'title',
-    'relation.hasPart',
-    'contributor.translator',
-    'contributor.editor',
-    'contributor.technical_editor',
-    'contributor.funding',
-    'contributor.thanks',
-    'publisher',
-    'subject.period',
-    'subject.type',
-    'subject.genre',
-    'description',
-    'identifier.url',
-    'source',
-    'source.URL',
-    'rights.license',
-    'rights',
-    'date.pd',
-    'format',
-    'type',
-    'date',
-    'audience',
-    'language',
-)
-
-IDENTIFIER_PREFIX = 'http://wolnelektury.pl/katalog/lektura/'
-
-
-def dc_desc_element(book):
-    xml = book.materialize()
-    tree = etree.fromstring(xml)
-    return tree.find(".//" + RDFNS("Description"))
-
-
-def distinct_dc_values(tag, desc_elements):
-    values = set()
-    for desc in desc_elements:
-        values.update(elem.text for elem in desc.findall(DCNS(tag)))
-    return values
-
-
-class Command(BaseCommand):
-    args = 'slug'
-
-    def handle(self, slug, **options):
-        children_slugs = [line.strip() for line in sys.stdin]
-        children = Book.objects.filter(dc_slug__in=children_slugs)
-        desc_elements = [dc_desc_element(child) for child in children]
-        title = u'Utwory wybrane'
-        own_attributes = {
-            'title': title,
-            'relation.hasPart': [IDENTIFIER_PREFIX + child_slug for child_slug in children_slugs],
-            'identifier.url': IDENTIFIER_PREFIX + slug,
-            'date': date.today().isoformat(),
-        }
-        dc_tags = []
-        for tag in DC_TAGS:
-            if tag in own_attributes:
-                values = own_attributes[tag]
-                if not isinstance(values, list):
-                    values = [values]
-            else:
-                values = distinct_dc_values(tag, desc_elements)
-            for value in values:
-                dc_tags.append(DC_TEMPLATE % {'tag': tag, 'value': value})
-        xml = TEMPLATE % {'slug': slug, 'dc': '\n'.join(dc_tags)}
-        Book.create(
-            text=xml,
-            creator=None,
-            slug=slug,
-            title=title,
-            gallery=slug)
diff --git a/apps/catalogue/management/commands/assign_from_redmine.py b/apps/catalogue/management/commands/assign_from_redmine.py
deleted file mode 100644 (file)
index 491fd83..0000000
+++ /dev/null
@@ -1,151 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import csv
-from optparse import make_option
-import re
-import sys
-import urllib
-import urllib2
-
-from django.contrib.auth.models import User
-from django.core.management.base import BaseCommand
-from django.core.management.color import color_style
-from django.db import transaction
-
-from slugify import slugify
-from catalogue.models import Chunk
-
-
-REDMINE_CSV = 'http://redmine.nowoczesnapolska.org.pl/projects/wl-publikacje/issues.csv'
-REDAKCJA_URL = 'http://redakcja.wolnelektury.pl/documents/'
-
-
-class Command(BaseCommand):
-    option_list = BaseCommand.option_list + (
-        make_option('-r', '--redakcja', dest='redakcja', metavar='URL',
-            help='Base URL of Redakcja documents',
-            default=REDAKCJA_URL),
-        make_option('-q', '--quiet', action='store_false', dest='verbose', default=True,
-            help='Less output'),
-        make_option('-f', '--force', action='store_true', dest='force', default=False,
-            help='Force assignment overwrite'),
-    )
-    help = 'Imports ticket assignments from Redmine.'
-    args = '[redmine-csv-url]'
-
-    def handle(self, *redmine_csv, **options):
-
-        self.style = color_style()
-
-        redakcja = options.get('redakcja')
-        verbose = options.get('verbose')
-        force = options.get('force')
-
-        if not redmine_csv:
-            if verbose:
-                print "Using default Redmine CSV URL:", REDMINE_CSV
-            redmine_csv = REDMINE_CSV
-
-        # Start transaction management.
-        transaction.enter_transaction_management()
-
-        redakcja_link = re.compile(re.escape(redakcja) + r'([-_.:?&%/a-zA-Z0-9]*)')
-
-        all_tickets = 0
-        all_chunks = 0
-        done_tickets = 0
-        done_chunks = 0
-        empty_users = 0
-        unknown_users = {}
-        unknown_books = []
-        forced = []
-
-        if verbose:
-            print 'Downloading CSV file'
-        for r in csv.reader(urllib2.urlopen(redmine_csv)):
-            if r[0] == '#':
-                continue
-            all_tickets += 1
-
-            username = r[6]
-            if not username:
-                if verbose:
-                    print "Empty user, skipping"
-                empty_users += 1
-                continue
-
-            first_name, last_name = unicode(username, 'utf-8').rsplit(u' ', 1)
-            try:
-                user = User.objects.get(first_name=first_name, last_name=last_name)
-            except User.DoesNotExist:
-                print self.style.ERROR('Unknown user: ' + username)
-                unknown_users.setdefault(username, 0)
-                unknown_users[username] += 1
-                continue
-
-            ticket_done = False
-            for fname in redakcja_link.findall(r[-1]):
-                fname = unicode(urllib.unquote(fname), 'utf-8', 'ignore')
-                if fname.endswith('.xml'):
-                    fname = fname[:-4]
-                fname = fname.replace(' ', '_')
-                fname = slugify(fname)
-
-                chunks = Chunk.objects.filter(book__slug=fname)
-                if not chunks:
-                    print self.style.ERROR('Unknown book: ' + fname)
-                    unknown_books.append(fname)
-                    continue
-                all_chunks += chunks.count()
-
-                for chunk in chunks:
-                    if chunk.user:
-                        if chunk.user == user:
-                            continue
-                        else:
-                            forced.append((chunk, chunk.user, user))
-                            if force:
-                                print self.style.WARNING(
-                                    '%s assigned to %s, forcing change to %s.' %
-                                    (chunk.pretty_name(), chunk.user, user))
-                            else:
-                                print self.style.WARNING(
-                                    '%s assigned to %s not to %s, skipping.' %
-                                    (chunk.pretty_name(), chunk.user, user))
-                                continue
-                    chunk.user = user
-                    chunk.save()
-                    ticket_done = True
-                    done_chunks += 1
-
-            if ticket_done:
-                done_tickets += 1
-
-
-        # Print results
-        print
-        print "Results:"
-        print "Assignments imported from %d/%d tickets to %d/%d relevalt chunks." % (
-                done_tickets, all_tickets, done_chunks, all_chunks)
-        if empty_users:
-            print "%d tickets were unassigned." % empty_users
-        if forced:
-            print "%d assignments conficts (%s):" % (
-                len(forced), "changed" if force else "left")
-            for chunk, orig, user in forced:
-                print "  %s: \t%s \t->  %s" % (
-                    chunk.pretty_name(), orig.username, user.username)
-        if unknown_books:
-            print "%d unknown books:" % len(unknown_books)
-            for fname in unknown_books:
-                print "  %s" % fname
-        if unknown_users:
-            print "%d unknown users:" % len(unknown_users)
-            for name in unknown_users:
-                print "  %s (%d tickets)" % (name, unknown_users[name])
-        print
-
-
-        transaction.commit()
-        transaction.leave_transaction_management()
-
diff --git a/apps/catalogue/management/commands/fixdc.py b/apps/catalogue/management/commands/fixdc.py
deleted file mode 100644 (file)
index 3f997d0..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
-# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
-#
-from librarian import RDFNS, WLURI, ValidationError
-from librarian.dcparser import BookInfo
-from catalogue.management import XmlUpdater
-from catalogue.management.commands import XmlUpdaterCommand
-
-
-class FixDC(XmlUpdater):
-    commit_desc = "auto-fixing DC"
-    retain_publishable = True
-    only_first_chunk = True
-
-    def fix_wluri(elem, change, verbose):
-        try:
-            WLURI.strict(elem.text)
-        except ValidationError:
-            correct_field = unicode(WLURI.from_slug(
-                                WLURI(elem.text.strip()).slug))
-            try:
-                WLURI.strict(correct_field)
-            except ValidationError:
-                # Can't make a valid WLURI out of it, leave as is.
-                return False
-            if verbose:
-                print "Changing %s from %s to %s" % (
-                        elem.tag, elem.text, correct_field
-                    )
-            elem.text = correct_field
-            return True
-    for field in BookInfo.FIELDS:
-        if field.validator == WLURI:
-            XmlUpdater.fixes_elements('.//' + field.uri)(fix_wluri)
-
-    @XmlUpdater.fixes_elements(".//" + RDFNS("Description"))
-    def fix_rdfabout(elem, change, verbose):
-        correct_about = change.tree.book.correct_about()
-        attr_name = RDFNS("about")
-        current_about = elem.get(attr_name)
-        if current_about != correct_about:
-            if verbose:
-                print "Changing rdf:about from %s to %s" % (
-                        current_about, correct_about
-                    )
-            elem.set(attr_name, correct_about)
-            return True
-
-
-class Command(XmlUpdaterCommand):
-    updater = FixDC
-    help = 'Fixes obvious errors in DC: rdf:about and WLURI format.'
diff --git a/apps/catalogue/management/commands/import_wl.py b/apps/catalogue/management/commands/import_wl.py
deleted file mode 100644 (file)
index 45c9e33..0000000
+++ /dev/null
@@ -1,89 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from collections import defaultdict
-import json
-from optparse import make_option
-import urllib2
-
-from django.core.management.base import BaseCommand
-from django.core.management.color import color_style
-from django.db import transaction
-from librarian.dcparser import BookInfo
-from librarian import ParseError, ValidationError
-
-from catalogue.models import Book
-
-
-WL_API = 'http://www.wolnelektury.pl/api/books/'
-
-
-class Command(BaseCommand):
-    option_list = BaseCommand.option_list + (
-        make_option('-q', '--quiet', action='store_false', dest='verbose', default=True,
-            help='Less output'),
-    )
-    help = 'Imports XML files from WL.'
-
-    def handle(self, *args, **options):
-
-        self.style = color_style()
-
-        verbose = options.get('verbose')
-
-        # Start transaction management.
-        transaction.enter_transaction_management()
-
-        if verbose:
-            print 'Reading currently managed files (skipping hidden ones).'
-        slugs = defaultdict(list)
-        for b in Book.objects.exclude(slug__startswith='.').all():
-            if verbose:
-                print b.slug
-            text = b.materialize().encode('utf-8')
-            try:
-                info = BookInfo.from_bytes(text)
-            except (ParseError, ValidationError):
-                pass
-            else:
-                slugs[info.slug].append(b)
-
-        book_count = 0
-        commit_args = {
-            "author_name": 'Platforma',
-            "description": 'Automatycznie zaimportowane z Wolnych Lektur',
-            "publishable": True,
-        }
-
-        if verbose:
-            print 'Opening books list'
-        for book in json.load(urllib2.urlopen(WL_API)):
-            book_detail = json.load(urllib2.urlopen(book['href']))
-            xml_text = urllib2.urlopen(book_detail['xml']).read()
-            info = BookInfo.from_bytes(xml_text)
-            previous_books = slugs.get(info.slug)
-            if previous_books:
-                if len(previous_books) > 1:
-                    print self.style.ERROR("There is more than one book "
-                        "with slug %s:"), 
-                previous_book = previous_books[0]
-                comm = previous_book.slug
-            else:
-                previous_book = None
-                comm = '*'
-            print book_count, info.slug , '-->', comm
-            Book.import_xml_text(xml_text, title=info.title[:255],
-                slug=info.slug[:128], previous_book=previous_book,
-                commit_args=commit_args)
-            book_count += 1
-
-        # Print results
-        print
-        print "Results:"
-        print "Imported %d books from WL:" % (
-                book_count, )
-        print
-
-
-        transaction.commit()
-        transaction.leave_transaction_management()
-
diff --git a/apps/catalogue/management/commands/insert_isbn.py b/apps/catalogue/management/commands/insert_isbn.py
deleted file mode 100644 (file)
index 7548cb1..0000000
+++ /dev/null
@@ -1,103 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
-# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
-#
-import csv
-
-import sys
-from django.contrib.auth.models import User
-from lxml import etree
-from optparse import make_option
-
-from collections import defaultdict
-from django.core.management import BaseCommand
-
-from catalogue.models import Book
-from librarian import RDFNS, DCNS
-
-CONTENT_TYPES = {
-    'pdf':  'application/pdf',
-    'epub': 'application/epub+zip',
-    'mobi': 'application/x-mobipocket-ebook',
-    'txt':  'text/plain',
-    'html': 'text/html',
-}
-
-
-ISBN_TEMPLATES = (
-    r'<dc:relation.hasFormat id="%(format)s" xmlns:dc="http://purl.org/dc/elements/1.1/">%(url)s'
-    r'</dc:relation.hasFormat>',
-    r'<meta refines="#%(format)s" id="%(format)s-id" property="dcterms:identifier">ISBN-%(isbn)s</meta>',
-    r'<meta refines="#%(format)s-id" property="identifier-type">ISBN</meta>',
-    r'<meta refines="#%(format)s" property="dcterms:format">%(content_type)s</meta>',
-)
-
-
-def url_for_format(slug, format):
-    if format == 'html':
-        return 'https://wolnelektury.pl/katalog/lektura/%s.html' % slug
-    else:
-        return 'http://wolnelektury.pl/media/book/%(format)s/%(slug)s.%(format)s' % {'slug': slug, 'format': format}
-
-
-class Command(BaseCommand):
-    option_list = BaseCommand.option_list + (
-        # make_option('-q', '--quiet', action='store_false', dest='verbose',
-        #     default=True, help='Less output'),
-        # make_option('-d', '--dry-run', action='store_true', dest='dry_run',
-        #     default=False, help="Don't actually touch anything"),
-        make_option(
-            '-u', '--username', dest='username', metavar='USER',
-            help='Assign commits to this user (required, preferably yourself).'),
-    )
-    args = 'csv_file'
-
-    def handle(self, csv_file, **options):
-        username = options.get('username')
-
-        if username:
-            user = User.objects.get(username=username)
-        else:
-            print 'Please provide a username.'
-            sys.exit(1)
-
-        csvfile = open(csv_file, 'rb')
-        isbn_lists = defaultdict(list)
-        for slug, format, isbn in csv.reader(csvfile, delimiter=','):
-            isbn_lists[slug].append((format, isbn))
-        csvfile.close()
-
-        for slug, isbn_list in isbn_lists.iteritems():
-            print 'processing %s' % slug
-            book = Book.objects.get(dc_slug=slug)
-            chunk = book.chunk_set.first()
-            old_head = chunk.head
-            src = old_head.materialize()
-            tree = etree.fromstring(src)
-            isbn_node = tree.find('.//' + DCNS("relation.hasFormat"))
-            if isbn_node is not None:
-                print '%s already contains ISBN metadata, skipping' % slug
-                continue
-            desc = tree.find(".//" + RDFNS("Description"))
-            for format, isbn in isbn_list:
-                for template in ISBN_TEMPLATES:
-                    isbn_xml = template % {
-                        'format': format,
-                        'isbn': isbn,
-                        'content_type': CONTENT_TYPES[format],
-                        'url': url_for_format(slug, format),
-                    }
-                    element = etree.XML(isbn_xml)
-                    element.tail = '\n'
-                    desc.append(element)
-            new_head = chunk.commit(
-                etree.tostring(tree, encoding=unicode),
-                author=user,
-                description='automatyczne dodanie isbn'
-            )
-            print 'committed %s' % slug
-            if old_head.publishable:
-                new_head.set_publishable(True)
-            else:
-                print 'Warning: %s not publishable' % slug
diff --git a/apps/catalogue/management/commands/mark_final.py b/apps/catalogue/management/commands/mark_final.py
deleted file mode 100644 (file)
index cdfaab9..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
-# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
-#
-import sys
-from django.contrib.auth.models import User
-from optparse import make_option
-
-from django.core.management import BaseCommand
-
-from catalogue.models import Book, Chunk
-
-
-class Command(BaseCommand):
-    option_list = BaseCommand.option_list + (
-        # make_option('-q', '--quiet', action='store_false', dest='verbose',
-        #     default=True, help='Less output'),
-        # make_option('-d', '--dry-run', action='store_true', dest='dry_run',
-        #     default=False, help="Don't actually touch anything"),
-        make_option(
-            '-u', '--username', dest='username', metavar='USER',
-            help='Assign commits to this user (required).'),
-    )
-    args = 'slug_file'
-
-    def handle(self, slug_file, **options):
-        username = options.get('username')
-
-        if username:
-            user = User.objects.get(username=username)
-        else:
-            print 'Please provide a username.'
-            sys.exit(1)
-
-        slugs = [line.strip() for line in open(slug_file)]
-        books = Book.objects.filter(slug__in=slugs)
-
-        for book in books:
-            print 'processing %s' % book.slug
-            for chunk in book.chunk_set.all():
-                src = chunk.head.materialize()
-                chunk.commit(
-                    text=src,
-                    author=user,
-                    description=u'Ostateczna akceptacja merytoryczna przez kierownika literackiego.',
-                    tags=[Chunk.tag_model.objects.get(slug='editor-proofreading')],
-                    publishable=True
-                )
-            print 'committed %s' % book.slug
diff --git a/apps/catalogue/management/commands/merge_books.py b/apps/catalogue/management/commands/merge_books.py
deleted file mode 100644 (file)
index 82bd622..0000000
+++ /dev/null
@@ -1,215 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from optparse import make_option
-import sys
-
-from django.contrib.auth.models import User
-from django.core.management.base import BaseCommand
-from django.core.management.color import color_style
-from django.db import transaction
-
-from catalogue.models import Book
-
-
-def common_prefix(texts):
-    common = []
-
-    min_len = min(len(text) for text in texts)
-    for i in range(min_len):
-        chars = list(set([text[i] for text in texts]))
-        if len(chars) > 1:
-            break
-        common.append(chars[0])
-    return "".join(common)
-
-
-class Command(BaseCommand):
-    option_list = BaseCommand.option_list + (
-        make_option('-s', '--slug', dest='new_slug', metavar='SLUG',
-            help='New slug of the merged book (defaults to common part of all slugs).'),
-        make_option('-t', '--title', dest='new_title', metavar='TITLE',
-            help='New title of the merged book (defaults to common part of all titles).'),
-        make_option('-q', '--quiet', action='store_false', dest='verbose', default=True,
-            help='Less output'),
-        make_option('-g', '--guess', action='store_true', dest='guess', default=False,
-            help='Try to guess what merges are needed (but do not apply them).'),
-        make_option('-d', '--dry-run', action='store_true', dest='dry_run', default=False,
-            help='Dry run: do not actually change anything.'),
-        make_option('-f', '--force', action='store_true', dest='force', default=False,
-            help='On slug conflict, hide the original book to archive.'),
-    )
-    help = 'Merges multiple books into one.'
-    args = '[slug]...'
-
-
-    def print_guess(self, dry_run=True, force=False):
-        from collections import defaultdict
-        from pipes import quote
-        import re
-    
-        def read_slug(slug):
-            res = []
-            res.append((re.compile(ur'__?(przedmowa)$'), -1))
-            res.append((re.compile(ur'__?(cz(esc)?|ksiega|rozdzial)__?(?P<n>\d*)$'), None))
-            res.append((re.compile(ur'__?(rozdzialy__?)?(?P<n>\d*)-'), None))
-        
-            for r, default in res:
-                m = r.search(slug)
-                if m:
-                    start = m.start()
-                    try:
-                        return int(m.group('n')), slug[:start]
-                    except IndexError:
-                        return default, slug[:start]
-            return None, slug
-    
-        def file_to_title(fname):
-            """ Returns a title-like version of a filename. """
-            parts = (p.replace('_', ' ').title() for p in fname.split('__'))
-            return ' / '.join(parts)
-    
-        merges = defaultdict(list)
-        slugs = []
-        for b in Book.objects.all():
-            slugs.append(b.slug)
-            n, ns = read_slug(b.slug)
-            if n is not None:
-                merges[ns].append((n, b))
-    
-        conflicting_slugs = []
-        for slug in sorted(merges.keys()):
-            merge_list = sorted(merges[slug])
-            if len(merge_list) < 2:
-                continue
-    
-            merge_slugs = [b.slug for i, b in merge_list]
-            if slug in slugs and slug not in merge_slugs:
-                conflicting_slugs.append(slug)
-    
-            title = file_to_title(slug)
-            print "./manage.py merge_books %s%s--title=%s --slug=%s \\\n    %s\n" % (
-                '--dry-run ' if dry_run else '',
-                '--force ' if force else '',
-                quote(title), slug,
-                " \\\n    ".join(merge_slugs)
-                )
-    
-        if conflicting_slugs:
-            if force:
-                print self.style.NOTICE('# These books will be archived:')
-            else:
-                print self.style.ERROR('# ERROR: Conflicting slugs:')
-            for slug in conflicting_slugs:
-                print '#', slug
-
-
-    def handle(self, *slugs, **options):
-
-        self.style = color_style()
-
-        force = options.get('force')
-        guess = options.get('guess')
-        dry_run = options.get('dry_run')
-        new_slug = options.get('new_slug').decode('utf-8')
-        new_title = options.get('new_title').decode('utf-8')
-        verbose = options.get('verbose')
-
-        if guess:
-            if slugs:
-                print "Please specify either slugs, or --guess."
-                return
-            else:
-                self.print_guess(dry_run, force)
-                return
-        if not slugs:
-            print "Please specify some book slugs"
-            return
-
-        # Start transaction management.
-        transaction.enter_transaction_management()
-
-        books = [Book.objects.get(slug=slug) for slug in slugs]
-        common_slug = common_prefix(slugs)
-        common_title = common_prefix([b.title for b in books])
-
-        if not new_title:
-            new_title = common_title
-        elif common_title.startswith(new_title):
-            common_title = new_title
-
-        if not new_slug:
-            new_slug = common_slug
-        elif common_slug.startswith(new_slug):
-            common_slug = new_slug
-
-        if slugs[0] != new_slug and Book.objects.filter(slug=new_slug).exists():
-            self.style.ERROR('Book already exists, skipping!')
-
-
-        if dry_run and verbose:
-            print self.style.NOTICE('DRY RUN: nothing will be changed.')
-            print
-
-        if verbose:
-            print "New title:", self.style.NOTICE(new_title)
-            print "New slug:", self.style.NOTICE(new_slug)
-            print
-
-        for i, book in enumerate(books):
-            chunk_titles = []
-            chunk_slugs = []
-
-            book_title = book.title[len(common_title):].replace(' / ', ' ').lstrip()
-            book_slug = book.slug[len(common_slug):].replace('__', '_').lstrip('-_')
-            for j, chunk in enumerate(book):
-                if j:
-                    new_chunk_title = book_title + '_%d' % j
-                    new_chunk_slug = book_slug + '_%d' % j
-                else:
-                    new_chunk_title, new_chunk_slug = book_title, book_slug
-
-                chunk_titles.append(new_chunk_title)
-                chunk_slugs.append(new_chunk_slug)
-
-                if verbose:
-                    print "title: %s // %s  -->\n       %s // %s\nslug: %s / %s  -->\n      %s / %s" % (
-                        book.title, chunk.title,
-                        new_title, new_chunk_title,
-                        book.slug, chunk.slug,
-                        new_slug, new_chunk_slug)
-                    print
-
-            if not dry_run:
-                try:
-                    conflict = Book.objects.get(slug=new_slug)
-                except Book.DoesNotExist:
-                    conflict = None
-                else:
-                    if conflict == books[0]:
-                        conflict = None
-
-                if conflict:
-                    if force:
-                        # FIXME: there still may be a conflict
-                        conflict.slug = '.' + conflict.slug
-                        conflict.save()
-                        print self.style.NOTICE('Book with slug "%s" moved to "%s".' % (new_slug, conflict.slug))
-                    else:
-                        print self.style.ERROR('ERROR: Book with slug "%s" exists.' % new_slug)
-                        return
-
-                if i:
-                    books[0].append(books[i], slugs=chunk_slugs, titles=chunk_titles)
-                else:
-                    book.title = new_title
-                    book.slug = new_slug
-                    book.save()
-                    for j, chunk in enumerate(book):
-                        chunk.title = chunk_titles[j]
-                        chunk.slug = chunk_slugs[j]
-                        chunk.save()
-
-
-        transaction.commit()
-        transaction.leave_transaction_management()
-
diff --git a/apps/catalogue/management/commands/prune_audience.py b/apps/catalogue/management/commands/prune_audience.py
deleted file mode 100644 (file)
index 114a26f..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
-# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
-#
-
-import sys
-from django.contrib.auth.models import User
-from lxml import etree
-from optparse import make_option
-
-from django.core.management import BaseCommand
-
-from catalogue.models import Book
-from librarian import DCNS
-
-
-class Command(BaseCommand):
-    option_list = BaseCommand.option_list + (
-        # make_option('-q', '--quiet', action='store_false', dest='verbose',
-        #     default=True, help='Less output'),
-        # make_option('-d', '--dry-run', action='store_true', dest='dry_run',
-        #     default=False, help="Don't actually touch anything"),
-        make_option(
-            '-u', '--username', dest='username', metavar='USER',
-            help='Assign commits to this user (required, preferably yourself).'),
-    )
-    args = 'exclude_file'
-
-    def handle(self, exclude_file, **options):
-        username = options.get('username')
-
-        if username:
-            user = User.objects.get(username=username)
-        else:
-            print 'Please provide a username.'
-            sys.exit(1)
-
-        excluded_slugs = [line.strip() for line in open(exclude_file, 'rb') if line.strip()]
-        books = Book.objects.exclude(slug__in=excluded_slugs)
-
-        for book in books:
-            if not book.is_published():
-                continue
-            print 'processing %s' % book.slug
-            chunk = book.chunk_set.first()
-            old_head = chunk.head
-            src = old_head.materialize()
-            tree = etree.fromstring(src)
-            audience_nodes = tree.findall('.//' + DCNS("audience"))
-            if not audience_nodes:
-                print '%s has no audience, skipping' % book.slug
-                continue
-
-            for node in audience_nodes:
-                node.getparent().remove(node)
-
-            chunk.commit(
-                etree.tostring(tree, encoding=unicode),
-                author=user,
-                description='automatyczne skasowanie audience',
-                publishable=old_head.publishable
-            )
-            print 'committed %s' % book.slug
-            if not old_head.publishable:
-                print 'Warning: %s not publishable, last head: %s, %s' % (
-                    book.slug, old_head.author.username, old_head.description[:40].replace('\n', ' '))
diff --git a/apps/catalogue/managers.py b/apps/catalogue/managers.py
deleted file mode 100644 (file)
index a131ce9..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-from django.db import models
-
-class VisibleManager(models.Manager):
-    def get_queryset(self):
-        return super(VisibleManager, self).get_queryset().exclude(_hidden=True)
diff --git a/apps/catalogue/migrations/0001_initial.py b/apps/catalogue/migrations/0001_initial.py
deleted file mode 100644 (file)
index dccd9b7..0000000
+++ /dev/null
@@ -1,240 +0,0 @@
-# encoding: utf-8
-import datetime
-from south.db import db
-from south.v2 import SchemaMigration
-from django.db import models
-
-class Migration(SchemaMigration):
-
-    def forwards(self, orm):
-        
-        # Adding model 'Book'
-        db.create_table('catalogue_book', (
-            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
-            ('title', self.gf('django.db.models.fields.CharField')(max_length=255, db_index=True)),
-            ('slug', self.gf('django.db.models.fields.SlugField')(unique=True, max_length=128, db_index=True)),
-            ('gallery', self.gf('django.db.models.fields.CharField')(max_length=255, blank=True)),
-            ('parent', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='children', null=True, to=orm['catalogue.Book'])),
-            ('parent_number', self.gf('django.db.models.fields.IntegerField')(db_index=True, null=True, blank=True)),
-            ('_short_html', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
-            ('_single', self.gf('django.db.models.fields.NullBooleanField')(db_index=True, null=True, blank=True)),
-            ('_new_publishable', self.gf('django.db.models.fields.NullBooleanField')(null=True, blank=True)),
-            ('_published', self.gf('django.db.models.fields.NullBooleanField')(null=True, blank=True)),
-        ))
-        db.send_create_signal('catalogue', ['Book'])
-
-        # Adding model 'Chunk'
-        db.create_table('catalogue_chunk', (
-            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
-            ('creator', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='created_documents', null=True, to=orm['auth.User'])),
-            ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], null=True, blank=True)),
-            ('book', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['catalogue.Book'])),
-            ('number', self.gf('django.db.models.fields.IntegerField')()),
-            ('slug', self.gf('django.db.models.fields.SlugField')(max_length=50, db_index=True)),
-            ('title', self.gf('django.db.models.fields.CharField')(max_length=255, blank=True)),
-            ('_short_html', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
-            ('_hidden', self.gf('django.db.models.fields.NullBooleanField')(null=True, blank=True)),
-            ('_changed', self.gf('django.db.models.fields.NullBooleanField')(null=True, blank=True)),
-            ('stage', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['catalogue.ChunkTag'], null=True, blank=True)),
-            ('head', self.gf('django.db.models.fields.related.ForeignKey')(default=None, to=orm['catalogue.ChunkChange'], null=True, blank=True)),
-        ))
-        db.send_create_signal('catalogue', ['Chunk'])
-
-        # Adding unique constraint on 'Chunk', fields ['book', 'number']
-        db.create_unique('catalogue_chunk', ['book_id', 'number'])
-
-        # Adding unique constraint on 'Chunk', fields ['book', 'slug']
-        db.create_unique('catalogue_chunk', ['book_id', 'slug'])
-
-        # Adding model 'ChunkTag'
-        db.create_table('catalogue_chunktag', (
-            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
-            ('name', self.gf('django.db.models.fields.CharField')(max_length=64)),
-            ('slug', self.gf('django.db.models.fields.SlugField')(db_index=True, max_length=64, unique=True, null=True, blank=True)),
-            ('ordering', self.gf('django.db.models.fields.IntegerField')()),
-        ))
-        db.send_create_signal('catalogue', ['ChunkTag'])
-
-        # Adding model 'ChunkChange'
-        db.create_table('catalogue_chunkchange', (
-            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
-            ('author', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], null=True, blank=True)),
-            ('author_name', self.gf('django.db.models.fields.CharField')(max_length=128, null=True, blank=True)),
-            ('author_email', self.gf('django.db.models.fields.CharField')(max_length=128, null=True, blank=True)),
-            ('revision', self.gf('django.db.models.fields.IntegerField')(db_index=True)),
-            ('parent', self.gf('django.db.models.fields.related.ForeignKey')(default=None, related_name='children', null=True, blank=True, to=orm['catalogue.ChunkChange'])),
-            ('merge_parent', self.gf('django.db.models.fields.related.ForeignKey')(default=None, related_name='merge_children', null=True, blank=True, to=orm['catalogue.ChunkChange'])),
-            ('description', self.gf('django.db.models.fields.TextField')(default='', blank=True)),
-            ('created_at', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.now, db_index=True)),
-            ('publishable', self.gf('django.db.models.fields.BooleanField')(default=False)),
-            ('tree', self.gf('django.db.models.fields.related.ForeignKey')(related_name='change_set', to=orm['catalogue.Chunk'])),
-            ('data', self.gf('django.db.models.fields.files.FileField')(max_length=100)),
-        ))
-        db.send_create_signal('catalogue', ['ChunkChange'])
-
-        # Adding unique constraint on 'ChunkChange', fields ['tree', 'revision']
-        db.create_unique('catalogue_chunkchange', ['tree_id', 'revision'])
-
-        # Adding M2M table for field tags on 'ChunkChange'
-        db.create_table('catalogue_chunkchange_tags', (
-            ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
-            ('chunkchange', models.ForeignKey(orm['catalogue.chunkchange'], null=False)),
-            ('chunktag', models.ForeignKey(orm['catalogue.chunktag'], null=False))
-        ))
-        db.create_unique('catalogue_chunkchange_tags', ['chunkchange_id', 'chunktag_id'])
-
-        # Adding model 'BookPublishRecord'
-        db.create_table('catalogue_bookpublishrecord', (
-            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
-            ('book', self.gf('django.db.models.fields.related.ForeignKey')(related_name='publish_log', to=orm['catalogue.Book'])),
-            ('timestamp', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
-            ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'])),
-        ))
-        db.send_create_signal('catalogue', ['BookPublishRecord'])
-
-        # Adding model 'ChunkPublishRecord'
-        db.create_table('catalogue_chunkpublishrecord', (
-            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
-            ('book_record', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['catalogue.BookPublishRecord'])),
-            ('change', self.gf('django.db.models.fields.related.ForeignKey')(related_name='publish_log', to=orm['catalogue.ChunkChange'])),
-        ))
-        db.send_create_signal('catalogue', ['ChunkPublishRecord'])
-
-
-    def backwards(self, orm):
-        
-        # Removing unique constraint on 'ChunkChange', fields ['tree', 'revision']
-        db.delete_unique('catalogue_chunkchange', ['tree_id', 'revision'])
-
-        # Removing unique constraint on 'Chunk', fields ['book', 'slug']
-        db.delete_unique('catalogue_chunk', ['book_id', 'slug'])
-
-        # Removing unique constraint on 'Chunk', fields ['book', 'number']
-        db.delete_unique('catalogue_chunk', ['book_id', 'number'])
-
-        # Deleting model 'Book'
-        db.delete_table('catalogue_book')
-
-        # Deleting model 'Chunk'
-        db.delete_table('catalogue_chunk')
-
-        # Deleting model 'ChunkTag'
-        db.delete_table('catalogue_chunktag')
-
-        # Deleting model 'ChunkChange'
-        db.delete_table('catalogue_chunkchange')
-
-        # Removing M2M table for field tags on 'ChunkChange'
-        db.delete_table('catalogue_chunkchange_tags')
-
-        # Deleting model 'BookPublishRecord'
-        db.delete_table('catalogue_bookpublishrecord')
-
-        # Deleting model 'ChunkPublishRecord'
-        db.delete_table('catalogue_chunkpublishrecord')
-
-
-    models = {
-        'auth.group': {
-            'Meta': {'object_name': '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']", 'symmetrical': 'False', 'blank': 'True'})
-        },
-        'auth.permission': {
-            'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
-            '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': {
-            'Meta': {'object_name': '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']", 'symmetrical': 'False', 'blank': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
-            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            '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']", 'symmetrical': 'False', 'blank': 'True'}),
-            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
-        },
-        'catalogue.book': {
-            'Meta': {'ordering': "['parent_number', 'title']", 'object_name': 'Book'},
-            '_new_publishable': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
-            '_published': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
-            '_short_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            '_single': ('django.db.models.fields.NullBooleanField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
-            'gallery': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': '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', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
-            'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '128', 'db_index': 'True'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'})
-        },
-        'catalogue.bookpublishrecord': {
-            'Meta': {'ordering': "['-timestamp']", 'object_name': 'BookPublishRecord'},
-            'book': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publish_log'", 'to': "orm['catalogue.Book']"}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'timestamp': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
-        },
-        'catalogue.chunk': {
-            'Meta': {'ordering': "['number']", 'unique_together': "[['book', 'number'], ['book', 'slug']]", 'object_name': 'Chunk'},
-            '_changed': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
-            '_hidden': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
-            '_short_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'book': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.Book']"}),
-            'creator': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'created_documents'", 'null': 'True', 'to': "orm['auth.User']"}),
-            'head': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['catalogue.ChunkChange']", 'null': 'True', 'blank': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'number': ('django.db.models.fields.IntegerField', [], {}),
-            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'}),
-            'stage': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.ChunkTag']", 'null': 'True', 'blank': 'True'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'})
-        },
-        'catalogue.chunkchange': {
-            'Meta': {'ordering': "('created_at',)", 'unique_together': "(['tree', 'revision'],)", 'object_name': 'ChunkChange'},
-            'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
-            'author_email': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
-            'author_name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
-            'created_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}),
-            'data': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
-            'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'merge_parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'merge_children'", 'null': 'True', 'blank': 'True', 'to': "orm['catalogue.ChunkChange']"}),
-            'parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'children'", 'null': 'True', 'blank': 'True', 'to': "orm['catalogue.ChunkChange']"}),
-            'publishable': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'revision': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
-            'tags': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'change_set'", 'symmetrical': 'False', 'to': "orm['catalogue.ChunkTag']"}),
-            'tree': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'change_set'", 'to': "orm['catalogue.Chunk']"})
-        },
-        'catalogue.chunkpublishrecord': {
-            'Meta': {'object_name': 'ChunkPublishRecord'},
-            'book_record': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.BookPublishRecord']"}),
-            'change': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publish_log'", 'to': "orm['catalogue.ChunkChange']"}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
-        },
-        'catalogue.chunktag': {
-            'Meta': {'ordering': "['ordering']", 'object_name': 'ChunkTag'},
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
-            'ordering': ('django.db.models.fields.IntegerField', [], {}),
-            'slug': ('django.db.models.fields.SlugField', [], {'db_index': 'True', 'max_length': '64', 'unique': 'True', 'null': 'True', 'blank': 'True'})
-        },
-        'contenttypes.contenttype': {
-            'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', '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/0002_stages.py b/apps/catalogue/migrations/0002_stages.py
deleted file mode 100644 (file)
index 7155457..0000000
+++ /dev/null
@@ -1,122 +0,0 @@
-# encoding: utf-8
-import datetime
-from south.db import db
-from south.v2 import DataMigration
-from django.db import models
-
-class Migration(DataMigration):
-
-    def forwards(self, orm):
-
-        from django.core.management import call_command
-        call_command("loaddata", "stages.json")
-
-
-    def backwards(self, orm):
-        "Write your backwards methods here."
-
-
-    models = {
-        'auth.group': {
-            'Meta': {'object_name': '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']", 'symmetrical': 'False', 'blank': 'True'})
-        },
-        'auth.permission': {
-            'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
-            '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': {
-            'Meta': {'object_name': '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']", 'symmetrical': 'False', 'blank': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
-            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            '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']", 'symmetrical': 'False', 'blank': 'True'}),
-            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
-        },
-        'catalogue.book': {
-            'Meta': {'ordering': "['parent_number', 'title']", 'object_name': 'Book'},
-            '_new_publishable': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
-            '_published': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
-            '_short_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            '_single': ('django.db.models.fields.NullBooleanField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
-            'gallery': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': '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', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
-            'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '128', 'db_index': 'True'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'})
-        },
-        'catalogue.bookpublishrecord': {
-            'Meta': {'ordering': "['-timestamp']", 'object_name': 'BookPublishRecord'},
-            'book': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publish_log'", 'to': "orm['catalogue.Book']"}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'timestamp': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
-        },
-        'catalogue.chunk': {
-            'Meta': {'ordering': "['number']", 'unique_together': "[['book', 'number'], ['book', 'slug']]", 'object_name': 'Chunk'},
-            '_changed': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
-            '_hidden': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
-            '_short_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'book': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.Book']"}),
-            'creator': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'created_documents'", 'null': 'True', 'to': "orm['auth.User']"}),
-            'head': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['catalogue.ChunkChange']", 'null': 'True', 'blank': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'number': ('django.db.models.fields.IntegerField', [], {}),
-            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'}),
-            'stage': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.ChunkTag']", 'null': 'True', 'blank': 'True'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'})
-        },
-        'catalogue.chunkchange': {
-            'Meta': {'ordering': "('created_at',)", 'unique_together': "(['tree', 'revision'],)", 'object_name': 'ChunkChange'},
-            'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
-            'author_email': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
-            'author_name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
-            'created_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}),
-            'data': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
-            'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'merge_parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'merge_children'", 'null': 'True', 'blank': 'True', 'to': "orm['catalogue.ChunkChange']"}),
-            'parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'children'", 'null': 'True', 'blank': 'True', 'to': "orm['catalogue.ChunkChange']"}),
-            'publishable': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'revision': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
-            'tags': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'change_set'", 'symmetrical': 'False', 'to': "orm['catalogue.ChunkTag']"}),
-            'tree': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'change_set'", 'to': "orm['catalogue.Chunk']"})
-        },
-        'catalogue.chunkpublishrecord': {
-            'Meta': {'object_name': 'ChunkPublishRecord'},
-            'book_record': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.BookPublishRecord']"}),
-            'change': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publish_log'", 'to': "orm['catalogue.ChunkChange']"}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
-        },
-        'catalogue.chunktag': {
-            'Meta': {'ordering': "['ordering']", 'object_name': 'ChunkTag'},
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
-            'ordering': ('django.db.models.fields.IntegerField', [], {}),
-            'slug': ('django.db.models.fields.SlugField', [], {'db_index': 'True', 'max_length': '64', 'unique': 'True', 'null': 'True', 'blank': 'True'})
-        },
-        'contenttypes.contenttype': {
-            'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', '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/0003_from_hg.py b/apps/catalogue/migrations/0003_from_hg.py
deleted file mode 100644 (file)
index e542d50..0000000
+++ /dev/null
@@ -1,281 +0,0 @@
-# encoding: utf-8
-import datetime
-from zlib import compress
-import os
-import os.path
-import re
-import urllib
-
-from django.db import models
-from south.db import db
-from south.v2 import DataMigration
-
-from django.conf import settings
-from slugify import slugify
-
-META_REGEX = re.compile(r'\s*<!--\s(.*?)-->', re.DOTALL | re.MULTILINE)
-STAGE_TAGS_RE = re.compile(r'^#stage-finished: (.*)$', re.MULTILINE)
-AUTHOR_RE = re.compile(r'\s*(.*?)\s*<(.*)>\s*')
-
-
-def urlunquote(url):
-    """Unqotes URL
-
-    # >>> urlunquote('Za%C5%BC%C3%B3%C5%82%C4%87_g%C4%99%C5%9Bl%C4%85_ja%C5%BA%C5%84')
-    # u'Za\u017c\xf3\u0142\u0107_g\u0119\u015bl\u0105 ja\u017a\u0144'
-    """
-    return unicode(urllib.unquote(url), 'utf-8', 'ignore')
-
-
-def split_name(name):
-    parts = name.split('__')
-    return parts
-
-
-def file_to_title(fname):
-    """ Returns a title-like version of a filename. """
-    parts = (p.replace('_', ' ').title() for p in fname.split('__'))
-    return ' / '.join(parts)
-
-
-def plain_text(text):
-    return re.sub(META_REGEX, '', text, 1)
-
-
-def gallery(slug, text):
-    result = {}
-
-    m = re.match(META_REGEX, text)
-    if m:
-        for line in m.group(1).split('\n'):
-            try:
-                k, v = line.split(':', 1)
-                result[k.strip()] = v.strip()
-            except ValueError:
-                continue
-
-    gallery = result.get('gallery', slugify(slug))
-
-    if gallery.startswith('/'):
-        gallery = os.path.basename(gallery)
-
-    return gallery
-
-
-def migrate_file_from_hg(orm, fname, entry):
-    fname = urlunquote(fname)
-    print fname
-    if fname.endswith('.xml'):
-        fname = fname[:-4]
-    title = file_to_title(fname)
-    fname = slugify(fname)
-
-    # create all the needed objects
-    # what if it already exists?
-    book = orm.Book.objects.create(
-        title=title,
-        slug=fname)
-    chunk = orm.Chunk.objects.create(
-        book=book,
-        number=1,
-        slug='1')
-    try:
-        chunk.stage = orm.ChunkTag.objects.order_by('ordering')[0]
-    except IndexError:
-        chunk.stage = None
-
-    maxrev = entry.filerev()
-    gallery_link = None
-
-    # this will fail if directory exists
-    os.makedirs(os.path.join(settings.CATALOGUE_REPO_PATH, str(chunk.pk)))
-
-    for rev in xrange(maxrev + 1):
-        fctx = entry.filectx(rev)
-        data = fctx.data()
-        gallery_link = gallery(fname, data)
-        data = plain_text(data)
-
-        # get tags from description
-        description = fctx.description().decode("utf-8", 'replace')
-        tags = STAGE_TAGS_RE.findall(description)
-        tags = [orm.ChunkTag.objects.get(slug=slug.strip()) for slug in tags]
-
-        if tags:
-            max_ordering = max(tags, key=lambda x: x.ordering).ordering
-            try:
-                chunk.stage = orm.ChunkTag.objects.filter(ordering__gt=max_ordering).order_by('ordering')[0]
-            except IndexError:
-                chunk.stage = None
-
-        description = STAGE_TAGS_RE.sub('', description)
-
-        author = author_name = author_email = None
-        author_desc = fctx.user().decode("utf-8", 'replace')
-        m = AUTHOR_RE.match(author_desc)
-        if m:
-            try:
-                author = orm['auth.User'].objects.get(username=m.group(1), email=m.group(2))
-            except orm['auth.User'].DoesNotExist:
-                author_name = m.group(1)
-                author_email = m.group(2)
-        else:
-            author_name = author_desc
-
-        head = orm.ChunkChange.objects.create(
-            tree=chunk,
-            revision=rev + 1,
-            created_at=datetime.datetime.fromtimestamp(fctx.date()[0]),
-            description=description,
-            author=author,
-            author_name=author_name,
-            author_email=author_email,
-            parent=chunk.head
-            )
-
-        path = "%d/%d" % (chunk.pk, head.pk)
-        abs_path = os.path.join(settings.CATALOGUE_REPO_PATH, path)
-        f = open(abs_path, 'wb')
-        f.write(compress(data))
-        f.close()
-        head.data = path
-
-        head.tags = tags
-        head.save()
-
-        chunk.head = head
-
-    chunk.save()
-    if gallery_link:
-        book.gallery = gallery_link
-        book.save()
-
-
-class Migration(DataMigration):
-
-    def forwards(self, orm):
-        try:
-            hg_path = settings.WIKI_REPOSITORY_PATH
-        except:
-            print 'repository not configured, skipping'
-        else:
-            from mercurial import hg, ui
-
-            print 'migrate from', hg_path
-            repo = hg.repository(ui.ui(), hg_path)
-            tip = repo['tip']
-            for fname in tip:
-                if fname.startswith('.'):
-                    continue
-                migrate_file_from_hg(orm, fname, tip[fname])
-
-
-    def backwards(self, orm):
-        "Write your backwards methods here."
-        pass
-
-
-    models = {
-        'auth.group': {
-            'Meta': {'object_name': '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']", 'symmetrical': 'False', 'blank': 'True'})
-        },
-        'auth.permission': {
-            'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
-            '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': {
-            'Meta': {'object_name': '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']", 'symmetrical': 'False', 'blank': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
-            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            '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']", 'symmetrical': 'False', 'blank': 'True'}),
-            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
-        },
-        'catalogue.book': {
-            'Meta': {'ordering': "['parent_number', 'title']", 'object_name': 'Book'},
-            '_new_publishable': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
-            '_published': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
-            '_short_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            '_single': ('django.db.models.fields.NullBooleanField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
-            'gallery': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': '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', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
-            'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '128', 'db_index': 'True'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'})
-        },
-        'catalogue.bookpublishrecord': {
-            'Meta': {'ordering': "['-timestamp']", 'object_name': 'BookPublishRecord'},
-            'book': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publish_log'", 'to': "orm['catalogue.Book']"}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'timestamp': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
-        },
-        'catalogue.chunk': {
-            'Meta': {'ordering': "['number']", 'unique_together': "[['book', 'number'], ['book', 'slug']]", 'object_name': 'Chunk'},
-            '_changed': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
-            '_hidden': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
-            '_short_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'book': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.Book']"}),
-            'creator': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'created_documents'", 'null': 'True', 'to': "orm['auth.User']"}),
-            'head': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['catalogue.ChunkChange']", 'null': 'True', 'blank': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'number': ('django.db.models.fields.IntegerField', [], {}),
-            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'}),
-            'stage': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.ChunkTag']", 'null': 'True', 'blank': 'True'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'})
-        },
-        'catalogue.chunkchange': {
-            'Meta': {'ordering': "('created_at',)", 'unique_together': "(['tree', 'revision'],)", 'object_name': 'ChunkChange'},
-            'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
-            'author_email': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
-            'author_name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
-            'created_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}),
-            'data': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
-            'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'merge_parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'merge_children'", 'null': 'True', 'blank': 'True', 'to': "orm['catalogue.ChunkChange']"}),
-            'parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'children'", 'null': 'True', 'blank': 'True', 'to': "orm['catalogue.ChunkChange']"}),
-            'publishable': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'revision': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
-            'tags': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'change_set'", 'symmetrical': 'False', 'to': "orm['catalogue.ChunkTag']"}),
-            'tree': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'change_set'", 'to': "orm['catalogue.Chunk']"})
-        },
-        'catalogue.chunkpublishrecord': {
-            'Meta': {'object_name': 'ChunkPublishRecord'},
-            'book_record': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.BookPublishRecord']"}),
-            'change': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publish_log'", 'to': "orm['catalogue.ChunkChange']"}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
-        },
-        'catalogue.chunktag': {
-            'Meta': {'ordering': "['ordering']", 'object_name': 'ChunkTag'},
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
-            'ordering': ('django.db.models.fields.IntegerField', [], {}),
-            'slug': ('django.db.models.fields.SlugField', [], {'db_index': 'True', 'max_length': '64', 'unique': 'True', 'null': 'True', 'blank': 'True'})
-        },
-        'contenttypes.contenttype': {
-            'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', '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/0004_fix_revisions.py b/apps/catalogue/migrations/0004_fix_revisions.py
deleted file mode 100644 (file)
index fe5c86b..0000000
+++ /dev/null
@@ -1,125 +0,0 @@
-# encoding: utf-8
-import datetime
-from south.db import db
-from south.v2 import DataMigration
-from django.db import models
-
-class Migration(DataMigration):
-
-    def forwards(self, orm):
-        "Make sure all revisions start with 1, not 0."
-        for zero_commit in orm.ChunkChange.objects.filter(revision=0):
-            for change in zero_commit.tree.change_set.all().order_by('-revision'):
-                change.revision=models.F('revision') + 1
-                change.save()
-
-
-    def backwards(self, orm):
-        "Write your backwards methods here."
-        pass
-
-
-    models = {
-        'auth.group': {
-            'Meta': {'object_name': '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']", 'symmetrical': 'False', 'blank': 'True'})
-        },
-        'auth.permission': {
-            'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
-            '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': {
-            'Meta': {'object_name': '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']", 'symmetrical': 'False', 'blank': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
-            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            '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']", 'symmetrical': 'False', 'blank': 'True'}),
-            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
-        },
-        'catalogue.book': {
-            'Meta': {'ordering': "['parent_number', 'title']", 'object_name': 'Book'},
-            '_new_publishable': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
-            '_published': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
-            '_short_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            '_single': ('django.db.models.fields.NullBooleanField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
-            'gallery': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': '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', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
-            'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '128', 'db_index': 'True'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'})
-        },
-        'catalogue.bookpublishrecord': {
-            'Meta': {'ordering': "['-timestamp']", 'object_name': 'BookPublishRecord'},
-            'book': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publish_log'", 'to': "orm['catalogue.Book']"}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'timestamp': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
-        },
-        'catalogue.chunk': {
-            'Meta': {'ordering': "['number']", 'unique_together': "[['book', 'number'], ['book', 'slug']]", 'object_name': 'Chunk'},
-            '_changed': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
-            '_hidden': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
-            '_short_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'book': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.Book']"}),
-            'creator': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'created_chunk'", 'null': 'True', 'to': "orm['auth.User']"}),
-            'head': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['catalogue.ChunkChange']", 'null': 'True', 'blank': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'number': ('django.db.models.fields.IntegerField', [], {}),
-            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'}),
-            'stage': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.ChunkTag']", 'null': 'True', 'blank': 'True'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'})
-        },
-        'catalogue.chunkchange': {
-            'Meta': {'ordering': "('created_at',)", 'unique_together': "(['tree', 'revision'],)", 'object_name': 'ChunkChange'},
-            'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
-            'author_email': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
-            'author_name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
-            'created_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}),
-            'data': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
-            'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'merge_parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'merge_children'", 'null': 'True', 'blank': 'True', 'to': "orm['catalogue.ChunkChange']"}),
-            'parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'children'", 'null': 'True', 'blank': 'True', 'to': "orm['catalogue.ChunkChange']"}),
-            'publishable': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'revision': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
-            'tags': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'change_set'", 'symmetrical': 'False', 'to': "orm['catalogue.ChunkTag']"}),
-            'tree': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'change_set'", 'to': "orm['catalogue.Chunk']"})
-        },
-        'catalogue.chunkpublishrecord': {
-            'Meta': {'object_name': 'ChunkPublishRecord'},
-            'book_record': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.BookPublishRecord']"}),
-            'change': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publish_log'", 'to': "orm['catalogue.ChunkChange']"}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
-        },
-        'catalogue.chunktag': {
-            'Meta': {'ordering': "['ordering']", 'object_name': 'ChunkTag'},
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
-            'ordering': ('django.db.models.fields.IntegerField', [], {}),
-            'slug': ('django.db.models.fields.SlugField', [], {'db_index': 'True', 'max_length': '64', 'unique': 'True', 'null': 'True', 'blank': 'True'})
-        },
-        'contenttypes.contenttype': {
-            'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', '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/0005_auto__add_field_chunk_gallery_start.py b/apps/catalogue/migrations/0005_auto__add_field_chunk_gallery_start.py
deleted file mode 100644 (file)
index 71af5f6..0000000
+++ /dev/null
@@ -1,125 +0,0 @@
-# encoding: utf-8
-import datetime
-from south.db import db
-from south.v2 import SchemaMigration
-from django.db import models
-
-class Migration(SchemaMigration):
-
-    def forwards(self, orm):
-        
-        # Adding field 'Chunk.gallery_start'
-        db.add_column('catalogue_chunk', 'gallery_start', self.gf('django.db.models.fields.IntegerField')(null=True, blank=True), keep_default=False)
-
-
-    def backwards(self, orm):
-        
-        # Deleting field 'Chunk.gallery_start'
-        db.delete_column('catalogue_chunk', 'gallery_start')
-
-
-    models = {
-        'auth.group': {
-            'Meta': {'object_name': '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']", 'symmetrical': 'False', 'blank': 'True'})
-        },
-        'auth.permission': {
-            'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
-            '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': {
-            'Meta': {'object_name': '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']", 'symmetrical': 'False', 'blank': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
-            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            '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']", 'symmetrical': 'False', 'blank': 'True'}),
-            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
-        },
-        'catalogue.book': {
-            'Meta': {'ordering': "['parent_number', 'title']", 'object_name': 'Book'},
-            '_new_publishable': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
-            '_published': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
-            '_short_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            '_single': ('django.db.models.fields.NullBooleanField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
-            'gallery': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': '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', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
-            'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '128', 'db_index': 'True'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'})
-        },
-        'catalogue.bookpublishrecord': {
-            'Meta': {'ordering': "['-timestamp']", 'object_name': 'BookPublishRecord'},
-            'book': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publish_log'", 'to': "orm['catalogue.Book']"}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'timestamp': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
-        },
-        'catalogue.chunk': {
-            'Meta': {'ordering': "['number']", 'unique_together': "[['book', 'number'], ['book', 'slug']]", 'object_name': 'Chunk'},
-            '_changed': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
-            '_hidden': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
-            '_short_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'book': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.Book']"}),
-            'creator': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'created_chunk'", 'null': 'True', 'to': "orm['auth.User']"}),
-            'gallery_start': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
-            'head': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['catalogue.ChunkChange']", 'null': 'True', 'blank': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'number': ('django.db.models.fields.IntegerField', [], {}),
-            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'}),
-            'stage': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.ChunkTag']", 'null': 'True', 'blank': 'True'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'})
-        },
-        'catalogue.chunkchange': {
-            'Meta': {'ordering': "('created_at',)", 'unique_together': "(['tree', 'revision'],)", 'object_name': 'ChunkChange'},
-            'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
-            'author_email': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
-            'author_name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
-            'created_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}),
-            'data': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
-            'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'merge_parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'merge_children'", 'null': 'True', 'blank': 'True', 'to': "orm['catalogue.ChunkChange']"}),
-            'parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'children'", 'null': 'True', 'blank': 'True', 'to': "orm['catalogue.ChunkChange']"}),
-            'publishable': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'revision': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
-            'tags': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'change_set'", 'symmetrical': 'False', 'to': "orm['catalogue.ChunkTag']"}),
-            'tree': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'change_set'", 'to': "orm['catalogue.Chunk']"})
-        },
-        'catalogue.chunkpublishrecord': {
-            'Meta': {'object_name': 'ChunkPublishRecord'},
-            'book_record': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.BookPublishRecord']"}),
-            'change': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publish_log'", 'to': "orm['catalogue.ChunkChange']"}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
-        },
-        'catalogue.chunktag': {
-            'Meta': {'ordering': "['ordering']", 'object_name': 'ChunkTag'},
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
-            'ordering': ('django.db.models.fields.IntegerField', [], {}),
-            'slug': ('django.db.models.fields.SlugField', [], {'db_index': 'True', 'max_length': '64', 'unique': 'True', 'null': 'True', 'blank': 'True'})
-        },
-        'contenttypes.contenttype': {
-            'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', '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/0006_auto__add_field_book_public.py b/apps/catalogue/migrations/0006_auto__add_field_book_public.py
deleted file mode 100644 (file)
index fd1cea5..0000000
+++ /dev/null
@@ -1,126 +0,0 @@
-# encoding: utf-8
-import datetime
-from south.db import db
-from south.v2 import SchemaMigration
-from django.db import models
-
-class Migration(SchemaMigration):
-
-    def forwards(self, orm):
-        
-        # Adding field 'Book.public'
-        db.add_column('catalogue_book', 'public', self.gf('django.db.models.fields.BooleanField')(default=True, db_index=True), keep_default=False)
-
-
-    def backwards(self, orm):
-        
-        # Deleting field 'Book.public'
-        db.delete_column('catalogue_book', 'public')
-
-
-    models = {
-        'auth.group': {
-            'Meta': {'object_name': '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']", 'symmetrical': 'False', 'blank': 'True'})
-        },
-        'auth.permission': {
-            'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
-            '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': {
-            'Meta': {'object_name': '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']", 'symmetrical': 'False', 'blank': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
-            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            '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']", 'symmetrical': 'False', 'blank': 'True'}),
-            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
-        },
-        'catalogue.book': {
-            'Meta': {'ordering': "['title', 'slug']", 'object_name': 'Book'},
-            '_new_publishable': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
-            '_published': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
-            '_short_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            '_single': ('django.db.models.fields.NullBooleanField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
-            'gallery': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': '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', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
-            'public': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}),
-            'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '128', 'db_index': 'True'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'})
-        },
-        'catalogue.bookpublishrecord': {
-            'Meta': {'ordering': "['-timestamp']", 'object_name': 'BookPublishRecord'},
-            'book': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publish_log'", 'to': "orm['catalogue.Book']"}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'timestamp': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
-        },
-        'catalogue.chunk': {
-            'Meta': {'ordering': "['number']", 'unique_together': "[['book', 'number'], ['book', 'slug']]", 'object_name': 'Chunk'},
-            '_changed': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
-            '_hidden': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
-            '_short_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'book': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.Book']"}),
-            'creator': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'created_chunk'", 'null': 'True', 'to': "orm['auth.User']"}),
-            'gallery_start': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
-            'head': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['catalogue.ChunkChange']", 'null': 'True', 'blank': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'number': ('django.db.models.fields.IntegerField', [], {}),
-            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'}),
-            'stage': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.ChunkTag']", 'null': 'True', 'blank': 'True'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'})
-        },
-        'catalogue.chunkchange': {
-            'Meta': {'ordering': "('created_at',)", 'unique_together': "(['tree', 'revision'],)", 'object_name': 'ChunkChange'},
-            'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
-            'author_email': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
-            'author_name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
-            'created_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}),
-            'data': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
-            'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'merge_parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'merge_children'", 'null': 'True', 'blank': 'True', 'to': "orm['catalogue.ChunkChange']"}),
-            'parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'children'", 'null': 'True', 'blank': 'True', 'to': "orm['catalogue.ChunkChange']"}),
-            'publishable': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'revision': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
-            'tags': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'change_set'", 'symmetrical': 'False', 'to': "orm['catalogue.ChunkTag']"}),
-            'tree': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'change_set'", 'to': "orm['catalogue.Chunk']"})
-        },
-        'catalogue.chunkpublishrecord': {
-            'Meta': {'object_name': 'ChunkPublishRecord'},
-            'book_record': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.BookPublishRecord']"}),
-            'change': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publish_log'", 'to': "orm['catalogue.ChunkChange']"}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
-        },
-        'catalogue.chunktag': {
-            'Meta': {'ordering': "['ordering']", 'object_name': 'ChunkTag'},
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
-            'ordering': ('django.db.models.fields.IntegerField', [], {}),
-            'slug': ('django.db.models.fields.SlugField', [], {'db_index': 'True', 'max_length': '64', 'unique': 'True', 'null': 'True', 'blank': 'True'})
-        },
-        'contenttypes.contenttype': {
-            'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', '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/0007_auto__add_field_book_dc_slug.py b/apps/catalogue/migrations/0007_auto__add_field_book_dc_slug.py
deleted file mode 100644 (file)
index 5ae20ea..0000000
+++ /dev/null
@@ -1,127 +0,0 @@
-# encoding: utf-8
-import datetime
-from south.db import db
-from south.v2 import SchemaMigration
-from django.db import models
-
-class Migration(SchemaMigration):
-
-    def forwards(self, orm):
-        
-        # Adding field 'Book.dc_slug'
-        db.add_column('catalogue_book', 'dc_slug', self.gf('django.db.models.fields.CharField')(max_length=128, null=True, blank=True), keep_default=False)
-
-
-    def backwards(self, orm):
-        
-        # Deleting field 'Book.dc_slug'
-        db.delete_column('catalogue_book', 'dc_slug')
-
-
-    models = {
-        'auth.group': {
-            'Meta': {'object_name': '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']", 'symmetrical': 'False', 'blank': 'True'})
-        },
-        'auth.permission': {
-            'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
-            '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': {
-            'Meta': {'object_name': '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']", 'symmetrical': 'False', 'blank': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
-            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            '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']", 'symmetrical': 'False', 'blank': 'True'}),
-            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
-        },
-        'catalogue.book': {
-            'Meta': {'ordering': "['title', 'slug']", 'object_name': 'Book'},
-            '_new_publishable': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
-            '_published': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
-            '_short_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            '_single': ('django.db.models.fields.NullBooleanField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
-            'dc_slug': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
-            'gallery': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': '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', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
-            'public': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}),
-            'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '128', 'db_index': 'True'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'})
-        },
-        'catalogue.bookpublishrecord': {
-            'Meta': {'ordering': "['-timestamp']", 'object_name': 'BookPublishRecord'},
-            'book': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publish_log'", 'to': "orm['catalogue.Book']"}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'timestamp': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
-        },
-        'catalogue.chunk': {
-            'Meta': {'ordering': "['number']", 'unique_together': "[['book', 'number'], ['book', 'slug']]", 'object_name': 'Chunk'},
-            '_changed': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
-            '_hidden': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
-            '_short_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'book': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.Book']"}),
-            'creator': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'created_chunk'", 'null': 'True', 'to': "orm['auth.User']"}),
-            'gallery_start': ('django.db.models.fields.IntegerField', [], {'default': '1', 'null': 'True', 'blank': 'True'}),
-            'head': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['catalogue.ChunkChange']", 'null': 'True', 'blank': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'number': ('django.db.models.fields.IntegerField', [], {}),
-            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'}),
-            'stage': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.ChunkTag']", 'null': 'True', 'blank': 'True'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'})
-        },
-        'catalogue.chunkchange': {
-            'Meta': {'ordering': "('created_at',)", 'unique_together': "(['tree', 'revision'],)", 'object_name': 'ChunkChange'},
-            'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
-            'author_email': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
-            'author_name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
-            'created_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}),
-            'data': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
-            'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'merge_parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'merge_children'", 'null': 'True', 'blank': 'True', 'to': "orm['catalogue.ChunkChange']"}),
-            'parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'children'", 'null': 'True', 'blank': 'True', 'to': "orm['catalogue.ChunkChange']"}),
-            'publishable': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'revision': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
-            'tags': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'change_set'", 'symmetrical': 'False', 'to': "orm['catalogue.ChunkTag']"}),
-            'tree': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'change_set'", 'to': "orm['catalogue.Chunk']"})
-        },
-        'catalogue.chunkpublishrecord': {
-            'Meta': {'object_name': 'ChunkPublishRecord'},
-            'book_record': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.BookPublishRecord']"}),
-            'change': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publish_log'", 'to': "orm['catalogue.ChunkChange']"}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
-        },
-        'catalogue.chunktag': {
-            'Meta': {'ordering': "['ordering']", 'object_name': 'ChunkTag'},
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
-            'ordering': ('django.db.models.fields.IntegerField', [], {}),
-            'slug': ('django.db.models.fields.SlugField', [], {'db_index': 'True', 'max_length': '64', 'unique': 'True', 'null': 'True', 'blank': 'True'})
-        },
-        'contenttypes.contenttype': {
-            'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', '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_auto.py b/apps/catalogue/migrations/0008_auto.py
deleted file mode 100644 (file)
index 5276b27..0000000
+++ /dev/null
@@ -1,127 +0,0 @@
-# encoding: utf-8
-import datetime
-from south.db import db
-from south.v2 import SchemaMigration
-from django.db import models
-
-class Migration(SchemaMigration):
-
-    def forwards(self, orm):
-        
-        # Adding index on 'Book', fields ['dc_slug']
-        db.create_index('catalogue_book', ['dc_slug'])
-
-
-    def backwards(self, orm):
-        
-        # Removing index on 'Book', fields ['dc_slug']
-        db.delete_index('catalogue_book', ['dc_slug'])
-
-
-    models = {
-        'auth.group': {
-            'Meta': {'object_name': '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']", 'symmetrical': 'False', 'blank': 'True'})
-        },
-        'auth.permission': {
-            'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
-            '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': {
-            'Meta': {'object_name': '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']", 'symmetrical': 'False', 'blank': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
-            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            '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']", 'symmetrical': 'False', 'blank': 'True'}),
-            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
-        },
-        'catalogue.book': {
-            'Meta': {'ordering': "['title', 'slug']", 'object_name': 'Book'},
-            '_new_publishable': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
-            '_published': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
-            '_short_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            '_single': ('django.db.models.fields.NullBooleanField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
-            'dc_slug': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '128', 'null': 'True', 'blank': 'True'}),
-            'gallery': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': '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', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
-            'public': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}),
-            'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '128', 'db_index': 'True'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'})
-        },
-        'catalogue.bookpublishrecord': {
-            'Meta': {'ordering': "['-timestamp']", 'object_name': 'BookPublishRecord'},
-            'book': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publish_log'", 'to': "orm['catalogue.Book']"}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'timestamp': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
-        },
-        'catalogue.chunk': {
-            'Meta': {'ordering': "['number']", 'unique_together': "[['book', 'number'], ['book', 'slug']]", 'object_name': 'Chunk'},
-            '_changed': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
-            '_hidden': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
-            '_short_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'book': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.Book']"}),
-            'creator': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'created_chunk'", 'null': 'True', 'to': "orm['auth.User']"}),
-            'gallery_start': ('django.db.models.fields.IntegerField', [], {'default': '1', 'null': 'True', 'blank': 'True'}),
-            'head': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['catalogue.ChunkChange']", 'null': 'True', 'blank': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'number': ('django.db.models.fields.IntegerField', [], {}),
-            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'}),
-            'stage': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.ChunkTag']", 'null': 'True', 'blank': 'True'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'})
-        },
-        'catalogue.chunkchange': {
-            'Meta': {'ordering': "('created_at',)", 'unique_together': "(['tree', 'revision'],)", 'object_name': 'ChunkChange'},
-            'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
-            'author_email': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
-            'author_name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
-            'created_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}),
-            'data': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
-            'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'merge_parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'merge_children'", 'null': 'True', 'blank': 'True', 'to': "orm['catalogue.ChunkChange']"}),
-            'parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'children'", 'null': 'True', 'blank': 'True', 'to': "orm['catalogue.ChunkChange']"}),
-            'publishable': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'revision': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
-            'tags': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'change_set'", 'symmetrical': 'False', 'to': "orm['catalogue.ChunkTag']"}),
-            'tree': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'change_set'", 'to': "orm['catalogue.Chunk']"})
-        },
-        'catalogue.chunkpublishrecord': {
-            'Meta': {'object_name': 'ChunkPublishRecord'},
-            'book_record': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.BookPublishRecord']"}),
-            'change': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publish_log'", 'to': "orm['catalogue.ChunkChange']"}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
-        },
-        'catalogue.chunktag': {
-            'Meta': {'ordering': "['ordering']", 'object_name': 'ChunkTag'},
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
-            'ordering': ('django.db.models.fields.IntegerField', [], {}),
-            'slug': ('django.db.models.fields.SlugField', [], {'db_index': 'True', 'max_length': '64', 'unique': 'True', 'null': 'True', 'blank': 'True'})
-        },
-        'contenttypes.contenttype': {
-            'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', '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/0009_auto__add_field_book__on_track.py b/apps/catalogue/migrations/0009_auto__add_field_book__on_track.py
deleted file mode 100644 (file)
index f0509c4..0000000
+++ /dev/null
@@ -1,128 +0,0 @@
-# encoding: utf-8
-import datetime
-from south.db import db
-from south.v2 import SchemaMigration
-from django.db import models
-
-class Migration(SchemaMigration):
-
-    def forwards(self, orm):
-        
-        # Adding field 'Book._on_track'
-        db.add_column('catalogue_book', '_on_track', self.gf('django.db.models.fields.IntegerField')(db_index=True, null=True, blank=True), keep_default=False)
-
-
-    def backwards(self, orm):
-        
-        # Deleting field 'Book._on_track'
-        db.delete_column('catalogue_book', '_on_track')
-
-
-    models = {
-        'auth.group': {
-            'Meta': {'object_name': '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']", 'symmetrical': 'False', 'blank': 'True'})
-        },
-        'auth.permission': {
-            'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
-            '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': {
-            'Meta': {'object_name': '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']", 'symmetrical': 'False', 'blank': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
-            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            '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']", 'symmetrical': 'False', 'blank': 'True'}),
-            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
-        },
-        'catalogue.book': {
-            'Meta': {'ordering': "['title', 'slug']", 'object_name': 'Book'},
-            '_new_publishable': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
-            '_on_track': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
-            '_published': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
-            '_short_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            '_single': ('django.db.models.fields.NullBooleanField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
-            'dc_slug': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '128', 'null': 'True', 'blank': 'True'}),
-            'gallery': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': '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', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
-            'public': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}),
-            'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '128', 'db_index': 'True'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'})
-        },
-        'catalogue.bookpublishrecord': {
-            'Meta': {'ordering': "['-timestamp']", 'object_name': 'BookPublishRecord'},
-            'book': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publish_log'", 'to': "orm['catalogue.Book']"}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'timestamp': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
-        },
-        'catalogue.chunk': {
-            'Meta': {'ordering': "['number']", 'unique_together': "[['book', 'number'], ['book', 'slug']]", 'object_name': 'Chunk'},
-            '_changed': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
-            '_hidden': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
-            '_short_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'book': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.Book']"}),
-            'creator': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'created_chunk'", 'null': 'True', 'to': "orm['auth.User']"}),
-            'gallery_start': ('django.db.models.fields.IntegerField', [], {'default': '1', 'null': 'True', 'blank': 'True'}),
-            'head': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['catalogue.ChunkChange']", 'null': 'True', 'blank': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'number': ('django.db.models.fields.IntegerField', [], {}),
-            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'}),
-            'stage': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.ChunkTag']", 'null': 'True', 'blank': 'True'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'})
-        },
-        'catalogue.chunkchange': {
-            'Meta': {'ordering': "('created_at',)", 'unique_together': "(['tree', 'revision'],)", 'object_name': 'ChunkChange'},
-            'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
-            'author_email': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
-            'author_name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
-            'created_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}),
-            'data': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
-            'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'merge_parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'merge_children'", 'null': 'True', 'blank': 'True', 'to': "orm['catalogue.ChunkChange']"}),
-            'parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'children'", 'null': 'True', 'blank': 'True', 'to': "orm['catalogue.ChunkChange']"}),
-            'publishable': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'revision': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
-            'tags': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'change_set'", 'symmetrical': 'False', 'to': "orm['catalogue.ChunkTag']"}),
-            'tree': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'change_set'", 'to': "orm['catalogue.Chunk']"})
-        },
-        'catalogue.chunkpublishrecord': {
-            'Meta': {'object_name': 'ChunkPublishRecord'},
-            'book_record': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.BookPublishRecord']"}),
-            'change': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publish_log'", 'to': "orm['catalogue.ChunkChange']"}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
-        },
-        'catalogue.chunktag': {
-            'Meta': {'ordering': "['ordering']", 'object_name': 'ChunkTag'},
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
-            'ordering': ('django.db.models.fields.IntegerField', [], {}),
-            'slug': ('django.db.models.fields.SlugField', [], {'db_index': 'True', 'max_length': '64', 'unique': 'True', 'null': 'True', 'blank': 'True'})
-        },
-        'contenttypes.contenttype': {
-            'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', '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/0010_auto__add_field_book_dc_cover_image.py b/apps/catalogue/migrations/0010_auto__add_field_book_dc_cover_image.py
deleted file mode 100644 (file)
index aebbed9..0000000
+++ /dev/null
@@ -1,144 +0,0 @@
-# -*- coding: utf-8 -*-
-import datetime
-from south.db import db
-from south.v2 import SchemaMigration
-from django.db import models
-
-
-class Migration(SchemaMigration):
-    depends_on = (
-        ("cover", "0001_initial"),
-    )
-
-    def forwards(self, orm):
-        # Adding field 'Book.dc_cover_image'
-        db.add_column('catalogue_book', 'dc_cover_image',
-                      self.gf('django.db.models.fields.related.ForeignKey')(to=orm['cover.Image'], null=True, on_delete=models.SET_NULL, blank=True),
-                      keep_default=False)
-
-
-    def backwards(self, orm):
-        # Deleting field 'Book.dc_cover_image'
-        db.delete_column('catalogue_book', 'dc_cover_image_id')
-
-
-    models = {
-        'auth.group': {
-            'Meta': {'object_name': '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']", 'symmetrical': 'False', 'blank': 'True'})
-        },
-        'auth.permission': {
-            'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
-            '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': {
-            'Meta': {'object_name': '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']", 'symmetrical': 'False', 'blank': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
-            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            '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']", 'symmetrical': 'False', 'blank': 'True'}),
-            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
-        },
-        'catalogue.book': {
-            'Meta': {'ordering': "['title', 'slug']", 'object_name': 'Book'},
-            '_new_publishable': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
-            '_on_track': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
-            '_published': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
-            '_short_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            '_single': ('django.db.models.fields.NullBooleanField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
-            'dc_cover_image': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cover.Image']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'dc_slug': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '128', 'null': 'True', 'blank': 'True'}),
-            'gallery': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': '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', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
-            'public': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}),
-            'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '128'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'})
-        },
-        'catalogue.bookpublishrecord': {
-            'Meta': {'ordering': "['-timestamp']", 'object_name': 'BookPublishRecord'},
-            'book': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publish_log'", 'to': "orm['catalogue.Book']"}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'timestamp': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
-        },
-        'catalogue.chunk': {
-            'Meta': {'ordering': "['number']", 'unique_together': "[['book', 'number'], ['book', 'slug']]", 'object_name': 'Chunk'},
-            '_changed': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
-            '_hidden': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
-            '_short_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'book': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.Book']"}),
-            'creator': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'created_chunk'", 'null': 'True', 'to': "orm['auth.User']"}),
-            'gallery_start': ('django.db.models.fields.IntegerField', [], {'default': '1', 'null': 'True', 'blank': 'True'}),
-            'head': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['catalogue.ChunkChange']", 'null': 'True', 'blank': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'number': ('django.db.models.fields.IntegerField', [], {}),
-            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50'}),
-            'stage': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.ChunkTag']", 'null': 'True', 'blank': 'True'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'})
-        },
-        'catalogue.chunkchange': {
-            'Meta': {'ordering': "('created_at',)", 'unique_together': "(['tree', 'revision'],)", 'object_name': 'ChunkChange'},
-            'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
-            'author_email': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
-            'author_name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
-            'created_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}),
-            'data': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
-            'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'merge_parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'merge_children'", 'null': 'True', 'blank': 'True', 'to': "orm['catalogue.ChunkChange']"}),
-            'parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'children'", 'null': 'True', 'blank': 'True', 'to': "orm['catalogue.ChunkChange']"}),
-            'publishable': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'revision': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
-            'tags': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'change_set'", 'symmetrical': 'False', 'to': "orm['catalogue.ChunkTag']"}),
-            'tree': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'change_set'", 'to': "orm['catalogue.Chunk']"})
-        },
-        'catalogue.chunkpublishrecord': {
-            'Meta': {'object_name': 'ChunkPublishRecord'},
-            'book_record': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.BookPublishRecord']"}),
-            'change': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publish_log'", 'to': "orm['catalogue.ChunkChange']"}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
-        },
-        'catalogue.chunktag': {
-            'Meta': {'ordering': "['ordering']", 'object_name': 'ChunkTag'},
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
-            'ordering': ('django.db.models.fields.IntegerField', [], {}),
-            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '64', 'unique': 'True', 'null': 'True', 'blank': 'True'})
-        },
-        'contenttypes.contenttype': {
-            'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', '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'})
-        },
-        'cover.image': {
-            'Meta': {'object_name': 'Image'},
-            'author': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'download_url': ('django.db.models.fields.URLField', [], {'unique': 'True', 'max_length': '200'}),
-            'file': ('django.db.models.fields.files.ImageField', [], {'max_length': '100'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'license_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'license_url': ('django.db.models.fields.URLField', [], {'max_length': '255', 'blank': 'True'}),
-            'source_url': ('django.db.models.fields.URLField', [], {'max_length': '200'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        }
-    }
-
-    complete_apps = ['catalogue']
\ No newline at end of file
diff --git a/apps/catalogue/migrations/0011_auto__add_project__add_field_book_project.py b/apps/catalogue/migrations/0011_auto__add_project__add_field_book_project.py
deleted file mode 100644 (file)
index 6f30cb4..0000000
+++ /dev/null
@@ -1,159 +0,0 @@
-# -*- coding: utf-8 -*-
-import datetime
-from south.db import db
-from south.v2 import SchemaMigration
-from django.db import models
-
-
-class Migration(SchemaMigration):
-
-    def forwards(self, orm):
-        # Adding model 'Project'
-        db.create_table(u'catalogue_project', (
-            (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
-            ('name', self.gf('django.db.models.fields.CharField')(unique=True, max_length=255)),
-            ('notes', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
-        ))
-        db.send_create_signal('catalogue', ['Project'])
-
-        # Adding field 'Book.project'
-        db.add_column(u'catalogue_book', 'project',
-                      self.gf('django.db.models.fields.related.ForeignKey')(to=orm['catalogue.Project'], null=True),
-                      keep_default=False)
-
-
-    def backwards(self, orm):
-        # Deleting model 'Project'
-        db.delete_table(u'catalogue_project')
-
-        # Deleting field 'Book.project'
-        db.delete_column(u'catalogue_book', 'project_id')
-
-
-    models = {
-        u'auth.group': {
-            'Meta': {'object_name': 'Group'},
-            u'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': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
-        },
-        u'auth.permission': {
-            'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
-            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
-        },
-        u'auth.user': {
-            'Meta': {'object_name': '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': u"orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
-            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            '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': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
-            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
-        },
-        'catalogue.book': {
-            'Meta': {'ordering': "['title', 'slug']", 'object_name': 'Book'},
-            '_new_publishable': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
-            '_on_track': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
-            '_published': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
-            '_short_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            '_single': ('django.db.models.fields.NullBooleanField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
-            'dc_cover_image': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['cover.Image']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'dc_slug': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '128', 'null': 'True', 'blank': 'True'}),
-            'gallery': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': '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', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
-            'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.Project']", 'null': 'True'}),
-            'public': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}),
-            'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '128'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'})
-        },
-        'catalogue.bookpublishrecord': {
-            'Meta': {'ordering': "['-timestamp']", 'object_name': 'BookPublishRecord'},
-            'book': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publish_log'", 'to': "orm['catalogue.Book']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'timestamp': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"})
-        },
-        'catalogue.chunk': {
-            'Meta': {'ordering': "['number']", 'unique_together': "[['book', 'number'], ['book', 'slug']]", 'object_name': 'Chunk'},
-            '_changed': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
-            '_hidden': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
-            '_short_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'book': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.Book']"}),
-            'creator': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'created_chunk'", 'null': 'True', 'to': u"orm['auth.User']"}),
-            'gallery_start': ('django.db.models.fields.IntegerField', [], {'default': '1', 'null': 'True', 'blank': 'True'}),
-            'head': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['catalogue.ChunkChange']", 'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'number': ('django.db.models.fields.IntegerField', [], {}),
-            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50'}),
-            'stage': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.ChunkTag']", 'null': 'True', 'blank': 'True'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'})
-        },
-        'catalogue.chunkchange': {
-            'Meta': {'ordering': "('created_at',)", 'unique_together': "(['tree', 'revision'],)", 'object_name': 'ChunkChange'},
-            'author': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}),
-            'author_email': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
-            'author_name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
-            'created_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}),
-            'data': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
-            'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'merge_parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'merge_children'", 'null': 'True', 'blank': 'True', 'to': "orm['catalogue.ChunkChange']"}),
-            'parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'children'", 'null': 'True', 'blank': 'True', 'to': "orm['catalogue.ChunkChange']"}),
-            'publishable': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'revision': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
-            'tags': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'change_set'", 'symmetrical': 'False', 'to': "orm['catalogue.ChunkTag']"}),
-            'tree': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'change_set'", 'to': "orm['catalogue.Chunk']"})
-        },
-        'catalogue.chunkpublishrecord': {
-            'Meta': {'object_name': 'ChunkPublishRecord'},
-            'book_record': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.BookPublishRecord']"}),
-            'change': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publish_log'", 'to': "orm['catalogue.ChunkChange']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
-        },
-        'catalogue.chunktag': {
-            'Meta': {'ordering': "['ordering']", 'object_name': 'ChunkTag'},
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
-            'ordering': ('django.db.models.fields.IntegerField', [], {}),
-            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '64', 'unique': 'True', 'null': 'True', 'blank': 'True'})
-        },
-        'catalogue.project': {
-            'Meta': {'ordering': "['name']", 'object_name': 'Project'},
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
-            'notes': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
-        },
-        u'contenttypes.contenttype': {
-            'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
-            'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            u'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'})
-        },
-        u'cover.image': {
-            'Meta': {'object_name': 'Image'},
-            'author': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'download_url': ('django.db.models.fields.URLField', [], {'unique': 'True', 'max_length': '200'}),
-            'file': ('django.db.models.fields.files.ImageField', [], {'max_length': '100'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'license_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'license_url': ('django.db.models.fields.URLField', [], {'max_length': '255', 'blank': 'True'}),
-            'source_url': ('django.db.models.fields.URLField', [], {'max_length': '200'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        }
-    }
-
-    complete_apps = ['catalogue']
\ No newline at end of file
diff --git a/apps/catalogue/migrations/0012_auto__add_imagepublishrecord__add_imagechange__add_unique_imagechange_.py b/apps/catalogue/migrations/0012_auto__add_imagepublishrecord__add_imagechange__add_unique_imagechange_.py
deleted file mode 100644 (file)
index 599e103..0000000
+++ /dev/null
@@ -1,270 +0,0 @@
-# -*- coding: utf-8 -*-
-import datetime
-from south.db import db
-from south.v2 import SchemaMigration
-from django.db import models
-
-
-class Migration(SchemaMigration):
-
-    def forwards(self, orm):
-        # Adding model 'ImagePublishRecord'
-        db.create_table(u'catalogue_imagepublishrecord', (
-            (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
-            ('image', self.gf('django.db.models.fields.related.ForeignKey')(related_name='publish_log', to=orm['catalogue.Image'])),
-            ('timestamp', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
-            ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'])),
-            ('change', self.gf('django.db.models.fields.related.ForeignKey')(related_name='publish_log', to=orm['catalogue.ImageChange'])),
-        ))
-        db.send_create_signal('catalogue', ['ImagePublishRecord'])
-
-        # Adding model 'ImageChange'
-        db.create_table(u'catalogue_imagechange', (
-            (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
-            ('author', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], null=True, blank=True)),
-            ('author_name', self.gf('django.db.models.fields.CharField')(max_length=128, null=True, blank=True)),
-            ('author_email', self.gf('django.db.models.fields.CharField')(max_length=128, null=True, blank=True)),
-            ('revision', self.gf('django.db.models.fields.IntegerField')(db_index=True)),
-            ('parent', self.gf('django.db.models.fields.related.ForeignKey')(default=None, related_name='children', null=True, blank=True, to=orm['catalogue.ImageChange'])),
-            ('merge_parent', self.gf('django.db.models.fields.related.ForeignKey')(default=None, related_name='merge_children', null=True, blank=True, to=orm['catalogue.ImageChange'])),
-            ('description', self.gf('django.db.models.fields.TextField')(default='', blank=True)),
-            ('created_at', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.now, db_index=True)),
-            ('publishable', self.gf('django.db.models.fields.BooleanField')(default=False)),
-            ('tree', self.gf('django.db.models.fields.related.ForeignKey')(related_name='change_set', to=orm['catalogue.Image'])),
-            ('data', self.gf('django.db.models.fields.files.FileField')(max_length=100)),
-        ))
-        db.send_create_signal('catalogue', ['ImageChange'])
-
-        # Adding M2M table for field tags on 'ImageChange'
-        db.create_table(u'catalogue_imagechange_tags', (
-            ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
-            ('imagechange', models.ForeignKey(orm['catalogue.imagechange'], null=False)),
-            ('imagetag', models.ForeignKey(orm['catalogue.imagetag'], null=False))
-        ))
-        db.create_unique(u'catalogue_imagechange_tags', ['imagechange_id', 'imagetag_id'])
-
-        # Adding unique constraint on 'ImageChange', fields ['tree', 'revision']
-        db.create_unique(u'catalogue_imagechange', ['tree_id', 'revision'])
-
-        # Adding model 'ImageTag'
-        db.create_table(u'catalogue_imagetag', (
-            (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
-            ('name', self.gf('django.db.models.fields.CharField')(max_length=64)),
-            ('slug', self.gf('django.db.models.fields.SlugField')(max_length=64, unique=True, null=True, blank=True)),
-            ('ordering', self.gf('django.db.models.fields.IntegerField')()),
-        ))
-        db.send_create_signal('catalogue', ['ImageTag'])
-
-        # Adding model 'Image'
-        db.create_table(u'catalogue_image', (
-            (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
-            ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], null=True, blank=True)),
-            ('image', self.gf('django.db.models.fields.files.FileField')(max_length=100)),
-            ('title', self.gf('django.db.models.fields.CharField')(max_length=255, blank=True)),
-            ('slug', self.gf('django.db.models.fields.SlugField')(unique=True, max_length=50)),
-            ('public', self.gf('django.db.models.fields.BooleanField')(default=True, db_index=True)),
-            ('_short_html', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
-            ('_new_publishable', self.gf('django.db.models.fields.NullBooleanField')(null=True, blank=True)),
-            ('_published', self.gf('django.db.models.fields.NullBooleanField')(null=True, blank=True)),
-            ('_changed', self.gf('django.db.models.fields.NullBooleanField')(null=True, blank=True)),
-            ('stage', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['catalogue.ImageTag'], null=True, blank=True)),
-            ('head', self.gf('django.db.models.fields.related.ForeignKey')(default=None, to=orm['catalogue.ImageChange'], null=True, blank=True)),
-            ('creator', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='created_image', null=True, to=orm['auth.User'])),
-        ))
-        db.send_create_signal('catalogue', ['Image'])
-
-
-    def backwards(self, orm):
-        # Removing unique constraint on 'ImageChange', fields ['tree', 'revision']
-        db.delete_unique(u'catalogue_imagechange', ['tree_id', 'revision'])
-
-        # Deleting model 'ImagePublishRecord'
-        db.delete_table(u'catalogue_imagepublishrecord')
-
-        # Deleting model 'ImageChange'
-        db.delete_table(u'catalogue_imagechange')
-
-        # Removing M2M table for field tags on 'ImageChange'
-        db.delete_table('catalogue_imagechange_tags')
-
-        # Deleting model 'ImageTag'
-        db.delete_table(u'catalogue_imagetag')
-
-        # Deleting model 'Image'
-        db.delete_table(u'catalogue_image')
-
-
-    models = {
-        u'auth.group': {
-            'Meta': {'object_name': 'Group'},
-            u'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': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
-        },
-        u'auth.permission': {
-            'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
-            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
-        },
-        u'auth.user': {
-            'Meta': {'object_name': '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': u"orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
-            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            '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': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
-            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
-        },
-        'catalogue.book': {
-            'Meta': {'ordering': "['title', 'slug']", 'object_name': 'Book'},
-            '_new_publishable': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
-            '_on_track': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
-            '_published': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
-            '_short_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            '_single': ('django.db.models.fields.NullBooleanField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
-            'dc_cover_image': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['cover.Image']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'dc_slug': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '128', 'null': 'True', 'blank': 'True'}),
-            'gallery': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': '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', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
-            'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.Project']", 'null': 'True', 'blank': 'True'}),
-            'public': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}),
-            'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '128'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'})
-        },
-        'catalogue.bookpublishrecord': {
-            'Meta': {'ordering': "['-timestamp']", 'object_name': 'BookPublishRecord'},
-            'book': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publish_log'", 'to': "orm['catalogue.Book']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'timestamp': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"})
-        },
-        'catalogue.chunk': {
-            'Meta': {'ordering': "['number']", 'unique_together': "[['book', 'number'], ['book', 'slug']]", 'object_name': 'Chunk'},
-            '_changed': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
-            '_hidden': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
-            '_short_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'book': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.Book']"}),
-            'creator': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'created_chunk'", 'null': 'True', 'to': u"orm['auth.User']"}),
-            'gallery_start': ('django.db.models.fields.IntegerField', [], {'default': '1', 'null': 'True', 'blank': 'True'}),
-            'head': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['catalogue.ChunkChange']", 'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'number': ('django.db.models.fields.IntegerField', [], {}),
-            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50'}),
-            'stage': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.ChunkTag']", 'null': 'True', 'blank': 'True'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'})
-        },
-        'catalogue.chunkchange': {
-            'Meta': {'ordering': "('created_at',)", 'unique_together': "(['tree', 'revision'],)", 'object_name': 'ChunkChange'},
-            'author': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}),
-            'author_email': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
-            'author_name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
-            'created_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}),
-            'data': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
-            'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'merge_parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'merge_children'", 'null': 'True', 'blank': 'True', 'to': "orm['catalogue.ChunkChange']"}),
-            'parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'children'", 'null': 'True', 'blank': 'True', 'to': "orm['catalogue.ChunkChange']"}),
-            'publishable': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'revision': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
-            'tags': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'change_set'", 'symmetrical': 'False', 'to': "orm['catalogue.ChunkTag']"}),
-            'tree': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'change_set'", 'to': "orm['catalogue.Chunk']"})
-        },
-        'catalogue.chunkpublishrecord': {
-            'Meta': {'object_name': 'ChunkPublishRecord'},
-            'book_record': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.BookPublishRecord']"}),
-            'change': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publish_log'", 'to': "orm['catalogue.ChunkChange']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
-        },
-        'catalogue.chunktag': {
-            'Meta': {'ordering': "['ordering']", 'object_name': 'ChunkTag'},
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
-            'ordering': ('django.db.models.fields.IntegerField', [], {}),
-            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '64', 'unique': 'True', 'null': 'True', 'blank': 'True'})
-        },
-        'catalogue.image': {
-            'Meta': {'ordering': "['title']", 'object_name': 'Image'},
-            '_changed': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
-            '_new_publishable': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
-            '_published': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
-            '_short_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'creator': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'created_image'", 'null': 'True', 'to': u"orm['auth.User']"}),
-            'head': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['catalogue.ImageChange']", 'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'image': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
-            'public': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}),
-            'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50'}),
-            'stage': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.ImageTag']", 'null': 'True', 'blank': 'True'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'})
-        },
-        'catalogue.imagechange': {
-            'Meta': {'ordering': "('created_at',)", 'unique_together': "(['tree', 'revision'],)", 'object_name': 'ImageChange'},
-            'author': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}),
-            'author_email': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
-            'author_name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
-            'created_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}),
-            'data': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
-            'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'merge_parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'merge_children'", 'null': 'True', 'blank': 'True', 'to': "orm['catalogue.ImageChange']"}),
-            'parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'children'", 'null': 'True', 'blank': 'True', 'to': "orm['catalogue.ImageChange']"}),
-            'publishable': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'revision': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
-            'tags': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'change_set'", 'symmetrical': 'False', 'to': "orm['catalogue.ImageTag']"}),
-            'tree': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'change_set'", 'to': "orm['catalogue.Image']"})
-        },
-        'catalogue.imagepublishrecord': {
-            'Meta': {'ordering': "['-timestamp']", 'object_name': 'ImagePublishRecord'},
-            'change': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publish_log'", 'to': "orm['catalogue.ImageChange']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'image': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publish_log'", 'to': "orm['catalogue.Image']"}),
-            'timestamp': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"})
-        },
-        'catalogue.imagetag': {
-            'Meta': {'ordering': "['ordering']", 'object_name': 'ImageTag'},
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
-            'ordering': ('django.db.models.fields.IntegerField', [], {}),
-            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '64', 'unique': 'True', 'null': 'True', 'blank': 'True'})
-        },
-        'catalogue.project': {
-            'Meta': {'ordering': "['name']", 'object_name': 'Project'},
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
-            'notes': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
-        },
-        u'contenttypes.contenttype': {
-            'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
-            'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            u'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'})
-        },
-        u'cover.image': {
-            'Meta': {'object_name': 'Image'},
-            'author': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'download_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'unique': 'True', 'null': 'True', 'blank': 'True'}),
-            'file': ('django.db.models.fields.files.ImageField', [], {'max_length': '100'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'license_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'license_url': ('django.db.models.fields.URLField', [], {'max_length': '255', 'blank': 'True'}),
-            'source_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        }
-    }
-
-    complete_apps = ['catalogue']
\ No newline at end of file
diff --git a/apps/catalogue/migrations/0013_auto__add_field_image_project.py b/apps/catalogue/migrations/0013_auto__add_field_image_project.py
deleted file mode 100644 (file)
index 6ae3564..0000000
+++ /dev/null
@@ -1,196 +0,0 @@
-# -*- coding: utf-8 -*-
-import datetime
-from south.db import db
-from south.v2 import SchemaMigration
-from django.db import models
-
-
-class Migration(SchemaMigration):
-
-    def forwards(self, orm):
-        # Adding field 'Image.project'
-        db.add_column(u'catalogue_image', 'project',
-                      self.gf('django.db.models.fields.related.ForeignKey')(to=orm['catalogue.Project'], null=True, blank=True),
-                      keep_default=False)
-
-
-    def backwards(self, orm):
-        # Deleting field 'Image.project'
-        db.delete_column(u'catalogue_image', 'project_id')
-
-
-    models = {
-        u'auth.group': {
-            'Meta': {'object_name': 'Group'},
-            u'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': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
-        },
-        u'auth.permission': {
-            'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
-            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
-        },
-        u'auth.user': {
-            'Meta': {'object_name': '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': u"orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
-            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            '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': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
-            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
-        },
-        'catalogue.book': {
-            'Meta': {'ordering': "['title', 'slug']", 'object_name': 'Book'},
-            '_new_publishable': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
-            '_on_track': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
-            '_published': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
-            '_short_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            '_single': ('django.db.models.fields.NullBooleanField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
-            'dc_cover_image': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['cover.Image']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
-            'dc_slug': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '128', 'null': 'True', 'blank': 'True'}),
-            'gallery': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': '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', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
-            'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.Project']", 'null': 'True', 'blank': 'True'}),
-            'public': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}),
-            'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '128'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'})
-        },
-        'catalogue.bookpublishrecord': {
-            'Meta': {'ordering': "['-timestamp']", 'object_name': 'BookPublishRecord'},
-            'book': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publish_log'", 'to': "orm['catalogue.Book']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'timestamp': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"})
-        },
-        'catalogue.chunk': {
-            'Meta': {'ordering': "['number']", 'unique_together': "[['book', 'number'], ['book', 'slug']]", 'object_name': 'Chunk'},
-            '_changed': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
-            '_hidden': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
-            '_short_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'book': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.Book']"}),
-            'creator': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'created_chunk'", 'null': 'True', 'to': u"orm['auth.User']"}),
-            'gallery_start': ('django.db.models.fields.IntegerField', [], {'default': '1', 'null': 'True', 'blank': 'True'}),
-            'head': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['catalogue.ChunkChange']", 'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'number': ('django.db.models.fields.IntegerField', [], {}),
-            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50'}),
-            'stage': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.ChunkTag']", 'null': 'True', 'blank': 'True'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'})
-        },
-        'catalogue.chunkchange': {
-            'Meta': {'ordering': "('created_at',)", 'unique_together': "(['tree', 'revision'],)", 'object_name': 'ChunkChange'},
-            'author': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}),
-            'author_email': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
-            'author_name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
-            'created_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}),
-            'data': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
-            'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'merge_parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'merge_children'", 'null': 'True', 'blank': 'True', 'to': "orm['catalogue.ChunkChange']"}),
-            'parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'children'", 'null': 'True', 'blank': 'True', 'to': "orm['catalogue.ChunkChange']"}),
-            'publishable': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'revision': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
-            'tags': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'change_set'", 'symmetrical': 'False', 'to': "orm['catalogue.ChunkTag']"}),
-            'tree': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'change_set'", 'to': "orm['catalogue.Chunk']"})
-        },
-        'catalogue.chunkpublishrecord': {
-            'Meta': {'object_name': 'ChunkPublishRecord'},
-            'book_record': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.BookPublishRecord']"}),
-            'change': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publish_log'", 'to': "orm['catalogue.ChunkChange']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
-        },
-        'catalogue.chunktag': {
-            'Meta': {'ordering': "['ordering']", 'object_name': 'ChunkTag'},
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
-            'ordering': ('django.db.models.fields.IntegerField', [], {}),
-            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '64', 'unique': 'True', 'null': 'True', 'blank': 'True'})
-        },
-        'catalogue.image': {
-            'Meta': {'ordering': "['title']", 'object_name': 'Image'},
-            '_changed': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
-            '_new_publishable': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
-            '_published': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
-            '_short_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
-            'creator': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'created_image'", 'null': 'True', 'to': u"orm['auth.User']"}),
-            'head': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['catalogue.ImageChange']", 'null': 'True', 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'image': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
-            'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.Project']", 'null': 'True', 'blank': 'True'}),
-            'public': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}),
-            'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50'}),
-            'stage': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.ImageTag']", 'null': 'True', 'blank': 'True'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'})
-        },
-        'catalogue.imagechange': {
-            'Meta': {'ordering': "('created_at',)", 'unique_together': "(['tree', 'revision'],)", 'object_name': 'ImageChange'},
-            'author': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}),
-            'author_email': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
-            'author_name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
-            'created_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}),
-            'data': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
-            'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'merge_parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'merge_children'", 'null': 'True', 'blank': 'True', 'to': "orm['catalogue.ImageChange']"}),
-            'parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'children'", 'null': 'True', 'blank': 'True', 'to': "orm['catalogue.ImageChange']"}),
-            'publishable': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'revision': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
-            'tags': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'change_set'", 'symmetrical': 'False', 'to': "orm['catalogue.ImageTag']"}),
-            'tree': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'change_set'", 'to': "orm['catalogue.Image']"})
-        },
-        'catalogue.imagepublishrecord': {
-            'Meta': {'ordering': "['-timestamp']", 'object_name': 'ImagePublishRecord'},
-            'change': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publish_log'", 'to': "orm['catalogue.ImageChange']"}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'image': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publish_log'", 'to': "orm['catalogue.Image']"}),
-            'timestamp': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"})
-        },
-        'catalogue.imagetag': {
-            'Meta': {'ordering': "['ordering']", 'object_name': 'ImageTag'},
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
-            'ordering': ('django.db.models.fields.IntegerField', [], {}),
-            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '64', 'unique': 'True', 'null': 'True', 'blank': 'True'})
-        },
-        'catalogue.project': {
-            'Meta': {'ordering': "['name']", 'object_name': 'Project'},
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
-            'notes': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
-        },
-        u'contenttypes.contenttype': {
-            'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
-            'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            u'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'})
-        },
-        u'cover.image': {
-            'Meta': {'object_name': 'Image'},
-            'author': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'download_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'unique': 'True', 'null': 'True', 'blank': 'True'}),
-            'file': ('django.db.models.fields.files.ImageField', [], {'max_length': '100'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'license_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'license_url': ('django.db.models.fields.URLField', [], {'max_length': '255', 'blank': 'True'}),
-            'source_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        }
-    }
-
-    complete_apps = ['catalogue']
\ No newline at end of file
diff --git a/apps/catalogue/migrations/__init__.py b/apps/catalogue/migrations/__init__.py
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/apps/catalogue/models/__init__.py b/apps/catalogue/models/__init__.py
deleted file mode 100755 (executable)
index d0015c7..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
-# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
-#
-from catalogue.models.project import Project
-from catalogue.models.chunk import Chunk
-from catalogue.models.image import Image
-from catalogue.models.publish_log import (BookPublishRecord,
-    ChunkPublishRecord, ImagePublishRecord)
-from catalogue.models.book import Book
-from catalogue.models.listeners import *
-
-from django.contrib.auth.models import User as AuthUser
-
-class User(AuthUser):
-    class Meta:
-        proxy = True
-
-    def __unicode__(self):
-        return "%s %s" % (self.first_name, self.last_name)
diff --git a/apps/catalogue/models/book.py b/apps/catalogue/models/book.py
deleted file mode 100755 (executable)
index 1fcd05a..0000000
+++ /dev/null
@@ -1,452 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
-# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
-#
-from django.contrib.sites.models import Site
-from django.db import models, transaction
-from django.template.loader import render_to_string
-from django.utils.translation import ugettext_lazy as _
-from django.conf import settings
-from slugify import slugify
-
-
-import apiclient
-from catalogue.helpers import cached_in_field, GalleryMerger
-from catalogue.models import BookPublishRecord, ChunkPublishRecord, Project
-from catalogue.signals import post_publish
-from catalogue.tasks import refresh_instance, book_content_updated
-from catalogue.xml_tools import compile_text, split_xml
-from cover.models import Image
-import os
-import shutil
-import re
-
-class Book(models.Model):
-    """ A document edited on the wiki """
-
-    title = models.CharField(_('title'), max_length=255, db_index=True)
-    slug = models.SlugField(_('slug'), max_length=128, unique=True, db_index=True)
-    public = models.BooleanField(_('public'), default=True, db_index=True)
-    gallery = models.CharField(_('scan gallery name'), max_length=255, blank=True)
-    project = models.ForeignKey(Project, null=True, blank=True)
-
-    #wl_slug = models.CharField(_('title'), max_length=255, null=True, db_index=True, editable=False)
-    parent = models.ForeignKey('self', null=True, blank=True, verbose_name=_('parent'), related_name="children", editable=False)
-    parent_number = models.IntegerField(_('parent number'), null=True, blank=True, db_index=True, editable=False)
-
-    # Cache
-    _short_html = models.TextField(null=True, blank=True, editable=False)
-    _single = models.NullBooleanField(editable=False, db_index=True)
-    _new_publishable = models.NullBooleanField(editable=False)
-    _published = models.NullBooleanField(editable=False)
-    _on_track = models.IntegerField(null=True, blank=True, db_index=True, editable=False)
-    dc_cover_image = models.ForeignKey(Image, blank=True, null=True,
-        db_index=True, on_delete=models.SET_NULL, editable=False)
-    dc_slug = models.CharField(max_length=128, null=True, blank=True,
-            editable=False, db_index=True)
-
-    class NoTextError(BaseException):
-        pass
-
-    class Meta:
-        app_label = 'catalogue'
-        ordering = ['title', 'slug']
-        verbose_name = _('book')
-        verbose_name_plural = _('books')
-
-
-    # Representing
-    # ============
-
-    def __iter__(self):
-        return iter(self.chunk_set.all())
-
-    def __getitem__(self, chunk):
-        return self.chunk_set.all()[chunk]
-
-    def __len__(self):
-        return self.chunk_set.count()
-
-    def __nonzero__(self):
-        """
-            Necessary so that __len__ isn't used for bool evaluation.
-        """
-        return True
-
-    def __unicode__(self):
-        return self.title
-
-    @models.permalink
-    def get_absolute_url(self):
-        return ("catalogue_book", [self.slug])
-
-    def correct_about(self):
-        return "http://%s%s" % (
-            Site.objects.get_current().domain,
-            self.get_absolute_url()
-        )
-
-    def gallery_path(self):
-        return os.path.join(settings.MEDIA_ROOT, settings.IMAGE_DIR, self.gallery)
-
-    def gallery_url(self):
-        return '%s%s%s/' % (settings.MEDIA_URL, settings.IMAGE_DIR, self.gallery)
-
-    # Creating & manipulating
-    # =======================
-
-    def accessible(self, request):
-        return self.public or request.user.is_authenticated()
-
-    @classmethod
-    @transaction.atomic
-    def create(cls, creator, text, *args, **kwargs):
-        b = cls.objects.create(*args, **kwargs)
-        b.chunk_set.all().update(creator=creator)
-        b[0].commit(text, author=creator)
-        return b
-
-    def add(self, *args, **kwargs):
-        """Add a new chunk at the end."""
-        return self.chunk_set.reverse()[0].split(*args, **kwargs)
-
-    @classmethod
-    @transaction.atomic
-    def import_xml_text(cls, text=u'', previous_book=None,
-                commit_args=None, **kwargs):
-        """Imports a book from XML, splitting it into chunks as necessary."""
-        texts = split_xml(text)
-        if previous_book:
-            instance = previous_book
-        else:
-            instance = cls(**kwargs)
-            instance.save()
-
-        # if there are more parts, set the rest to empty strings
-        book_len = len(instance)
-        for i in range(book_len - len(texts)):
-            texts.append((u'pusta część %d' % (i + 1), u''))
-
-        i = 0
-        for i, (title, text) in enumerate(texts):
-            if not title:
-                title = u'część %d' % (i + 1)
-
-            slug = slugify(title)
-
-            if i < book_len:
-                chunk = instance[i]
-                chunk.slug = slug[:50]
-                chunk.title = title[:255]
-                chunk.save()
-            else:
-                chunk = instance.add(slug, title)
-
-            chunk.commit(text, **commit_args)
-
-        return instance
-
-    def make_chunk_slug(self, proposed):
-        """ 
-            Finds a chunk slug not yet used in the book.
-        """
-        slugs = set(c.slug for c in self)
-        i = 1
-        new_slug = proposed[:50]
-        while new_slug in slugs:
-            new_slug = "%s_%d" % (proposed[:45], i)
-            i += 1
-        return new_slug
-
-    @transaction.atomic
-    def append(self, other, slugs=None, titles=None):
-        """Add all chunks of another book to self."""
-        assert self != other
-
-        number = self[len(self) - 1].number + 1
-        len_other = len(other)
-        single = len_other == 1
-
-        if slugs is not None:
-            assert len(slugs) == len_other
-        if titles is not None:
-            assert len(titles) == len_other
-            if slugs is None:
-                slugs = [slugify(t) for t in titles]
-
-        for i, chunk in enumerate(other):
-            # move chunk to new book
-            chunk.book = self
-            chunk.number = number
-
-            if titles is None:
-                # try some title guessing
-                if other.title.startswith(self.title):
-                    other_title_part = other.title[len(self.title):].lstrip(' /')
-                else:
-                    other_title_part = other.title
-
-                if single:
-                    # special treatment for appending one-parters:
-                    # just use the guessed title and original book slug
-                    chunk.title = other_title_part
-                    if other.slug.startswith(self.slug):
-                        chunk.slug = other.slug[len(self.slug):].lstrip('-_')
-                    else:
-                        chunk.slug = other.slug
-                else:
-                    chunk.title = ("%s, %s" % (other_title_part, chunk.title))[:255]
-            else:
-                chunk.slug = slugs[i]
-                chunk.title = titles[i]
-
-            chunk.slug = self.make_chunk_slug(chunk.slug)
-            chunk.save()
-            number += 1
-        assert not other.chunk_set.exists()
-
-        gm = GalleryMerger(self.gallery, other.gallery)
-        self.gallery = gm.merge()
-
-        # and move the gallery starts
-        if gm.was_merged:
-                for chunk in self[len(self) - len_other:]:
-                        old_start = chunk.gallery_start or 1
-                        chunk.gallery_start = old_start + gm.dest_size - gm.num_deleted
-                        chunk.save()
-
-        other.delete()
-
-
-    @transaction.atomic
-    def prepend_history(self, other):
-        """Prepend history from all the other book's chunks to own."""
-        assert self != other
-
-        for i in range(len(self), len(other)):
-            title = u"pusta część %d" % i
-            chunk = self.add(slugify(title), title)
-            chunk.commit('')
-
-        for i in range(len(other)):
-            self[i].prepend_history(other[0])
-
-        assert not other.chunk_set.exists()
-        other.delete()
-
-    def split(self):
-        """Splits all the chunks into separate books."""
-        self.title
-        for chunk in self:
-            book = Book.objects.create(title=chunk.title, slug=chunk.slug,
-                    public=self.public, gallery=self.gallery)
-            book[0].delete()
-            chunk.book = book
-            chunk.number = 1
-            chunk.save()
-        assert not self.chunk_set.exists()
-        self.delete()
-
-    # State & cache
-    # =============
-
-    def last_published(self):
-        try:
-            return self.publish_log.all()[0].timestamp
-        except IndexError:
-            return None
-
-    def assert_publishable(self):
-        assert self.chunk_set.exists(), _('No chunks in the book.')
-        try:
-            changes = self.get_current_changes(publishable=True)
-        except self.NoTextError:
-            raise AssertionError(_('Not all chunks have publishable revisions.'))
-
-        from librarian import NoDublinCore, ParseError, ValidationError
-
-        try:
-            bi = self.wldocument(changes=changes, strict=True).book_info
-        except ParseError, e:
-            raise AssertionError(_('Invalid XML') + ': ' + unicode(e))
-        except NoDublinCore:
-            raise AssertionError(_('No Dublin Core found.'))
-        except ValidationError, e:
-            raise AssertionError(_('Invalid Dublin Core') + ': ' + unicode(e))
-
-        valid_about = self.correct_about()
-        assert bi.about == valid_about, _("rdf:about is not") + " " + valid_about
-
-    def publishable_error(self):
-        try:
-            return self.assert_publishable()
-        except AssertionError, e:
-            return e
-        else:
-            return None
-
-    def hidden(self):
-        return self.slug.startswith('.')
-
-    def is_new_publishable(self):
-        """Checks if book is ready for publishing.
-
-        Returns True if there is a publishable version newer than the one
-        already published.
-
-        """
-        new_publishable = False
-        if not self.chunk_set.exists():
-            return False
-        for chunk in self:
-            change = chunk.publishable()
-            if not change:
-                return False
-            if not new_publishable and not change.publish_log.exists():
-                new_publishable = True
-        return new_publishable
-    new_publishable = cached_in_field('_new_publishable')(is_new_publishable)
-
-    def is_published(self):
-        return self.publish_log.exists()
-    published = cached_in_field('_published')(is_published)
-
-    def get_on_track(self):
-        if self.published:
-            return -1
-        stages = [ch.stage.ordering if ch.stage is not None else 0
-                    for ch in self]
-        if not len(stages):
-            return 0
-        return min(stages)
-    on_track = cached_in_field('_on_track')(get_on_track)
-
-    def is_single(self):
-        return len(self) == 1
-    single = cached_in_field('_single')(is_single)
-
-    @cached_in_field('_short_html')
-    def short_html(self):
-        return render_to_string('catalogue/book_list/book.html', {'book': self})
-
-    def book_info(self, publishable=True):
-        try:
-            book_xml = self.materialize(publishable=publishable)
-        except self.NoTextError:
-            pass
-        else:
-            from librarian.dcparser import BookInfo
-            from librarian import NoDublinCore, ParseError, ValidationError
-            try:
-                return BookInfo.from_bytes(book_xml.encode('utf-8'))
-            except (self.NoTextError, ParseError, NoDublinCore, ValidationError):
-                return None
-
-    def refresh_dc_cache(self):
-        update = {
-            'dc_slug': None,
-            'dc_cover_image': None,
-        }
-
-        info = self.book_info()
-        if info is not None:
-            update['dc_slug'] = info.url.slug
-            if info.cover_source:
-                try:
-                    image = Image.objects.get(pk=int(info.cover_source.rstrip('/').rsplit('/', 1)[-1]))
-                except:
-                    pass
-                else:
-                    if info.cover_source == image.get_full_url():
-                        update['dc_cover_image'] = image
-        Book.objects.filter(pk=self.pk).update(**update)
-
-    def touch(self):
-        # this should only really be done when text or publishable status changes
-        book_content_updated.delay(self)
-
-        update = {
-            "_new_publishable": self.is_new_publishable(),
-            "_published": self.is_published(),
-            "_single": self.is_single(),
-            "_on_track": self.get_on_track(),
-            "_short_html": None,
-        }
-        Book.objects.filter(pk=self.pk).update(**update)
-        refresh_instance(self)
-
-    def refresh(self):
-        """This should be done offline."""
-        self.short_html
-        self.single
-        self.new_publishable
-        self.published
-
-    # Materializing & publishing
-    # ==========================
-
-    def get_current_changes(self, publishable=True):
-        """
-            Returns a list containing one Change for every Chunk in the Book.
-            Takes the most recent revision (publishable, if set).
-            Throws an error, if a proper revision is unavailable for a Chunk.
-        """
-        if publishable:
-            changes = [chunk.publishable() for chunk in self]
-        else:
-            changes = [chunk.head for chunk in self if chunk.head is not None]
-        if None in changes:
-            raise self.NoTextError('Some chunks have no available text.')
-        return changes
-
-    def materialize(self, publishable=False, changes=None):
-        """ 
-            Get full text of the document compiled from chunks.
-            Takes the current versions of all texts
-            or versions most recently tagged for publishing,
-            or a specified iterable changes.
-        """
-        if changes is None:
-            changes = self.get_current_changes(publishable)
-        return compile_text(change.materialize() for change in changes)
-
-    def wldocument(self, publishable=True, changes=None, 
-            parse_dublincore=True, strict=False):
-        from catalogue.ebook_utils import RedakcjaDocProvider
-        from librarian.parser import WLDocument
-
-        return WLDocument.from_bytes(
-                self.materialize(publishable=publishable, changes=changes).encode('utf-8'),
-                provider=RedakcjaDocProvider(publishable=publishable),
-                parse_dublincore=parse_dublincore,
-                strict=strict)
-
-    def publish(self, user, fake=False, host=None, days=0, beta=False):
-        """
-            Publishes a book on behalf of a (local) user.
-        """
-        self.assert_publishable()
-        changes = self.get_current_changes(publishable=True)
-        if not fake:
-            book_xml = self.materialize(changes=changes)
-            data = {"book_xml": book_xml, "days": days}
-            if host:
-                data['gallery_url'] = host + self.gallery_url()
-            apiclient.api_call(user, "books/", data, beta=beta)
-        if not beta:
-            # record the publish
-            br = BookPublishRecord.objects.create(book=self, user=user)
-            for c in changes:
-                ChunkPublishRecord.objects.create(book_record=br, change=c)
-            if not self.public and days == 0:
-                self.public = True
-                self.save()
-            if self.public and days > 0:
-                self.public = False
-                self.save()
-            post_publish.send(sender=br)
-
-    def latex_dir(self):
-        doc = self.wldocument()
-        return doc.latex_dir(cover=True, ilustr_path=self.gallery_path())
diff --git a/apps/catalogue/models/chunk.py b/apps/catalogue/models/chunk.py
deleted file mode 100755 (executable)
index fc3a9ea..0000000
+++ /dev/null
@@ -1,124 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
-# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
-#
-from django.conf import settings
-from django.db import models
-from django.db.utils import IntegrityError
-from django.template.loader import render_to_string
-from django.utils.translation import ugettext_lazy as _
-from catalogue.helpers import cached_in_field
-from catalogue.managers import VisibleManager
-from catalogue.tasks import refresh_instance
-from dvcs import models as dvcs_models
-
-
-class Chunk(dvcs_models.Document):
-    """ An editable chunk of text. Every Book text is divided into chunks. """
-    REPO_PATH = settings.CATALOGUE_REPO_PATH
-
-    book = models.ForeignKey('Book', editable=False, verbose_name=_('book'))
-    number = models.IntegerField(_('number'))
-    title = models.CharField(_('title'), max_length=255, blank=True)
-    slug = models.SlugField(_('slug'))
-    gallery_start = models.IntegerField(_('gallery start'), null=True, blank=True, default=1)
-
-    # cache
-    _short_html = models.TextField(null=True, blank=True, editable=False)
-    _hidden = models.NullBooleanField(editable=False)
-    _changed = models.NullBooleanField(editable=False)
-
-    # managers
-    objects = models.Manager()
-    visible_objects = VisibleManager()
-
-    class Meta:
-        app_label = 'catalogue'
-        unique_together = [['book', 'number'], ['book', 'slug']]
-        ordering = ['number']
-        verbose_name = _('chunk')
-        verbose_name_plural = _('chunks')
-        permissions = [('can_pubmark', 'Can mark for publishing')]
-
-    # Representing
-    # ============
-
-    def __unicode__(self):
-        return "%d:%d: %s" % (self.book_id, self.number, self.title)
-
-    @models.permalink
-    def get_absolute_url(self):
-        return "wiki_editor", [self.book.slug, self.slug]
-
-    def pretty_name(self, book_length=None):
-        title = self.book.title
-        if self.title:
-            title += ", %s" % self.title
-        if book_length > 1:
-            title += " (%d/%d)" % (self.number, book_length)
-        return title
-
-    # Creating and manipulation
-    # =========================
-
-    def split(self, slug, title='', **kwargs):
-        """ Create an empty chunk after this one """
-        self.book.chunk_set.filter(number__gt=self.number).update(
-                number=models.F('number')+1)
-        new_chunk = None
-        while not new_chunk:
-            new_slug = self.book.make_chunk_slug(slug)
-            try:
-                new_chunk = self.book.chunk_set.create(
-                    number=self.number+1,
-                    slug=new_slug[:50], title=title[:255], **kwargs)
-            except IntegrityError:
-                pass
-        return new_chunk
-
-    @classmethod
-    def get(cls, book_slug, chunk_slug=None):
-        if chunk_slug is None:
-            return cls.objects.get(book__slug=book_slug, number=1)
-        else:
-            return cls.objects.get(book__slug=book_slug, slug=chunk_slug)
-
-    # State & cache
-    # =============
-
-    def new_publishable(self):
-        change = self.publishable()
-        if not change:
-            return False
-        return not change.publish_log.exists()
-
-    def is_changed(self):
-        if self.head is None:
-            return False
-        return not self.head.publishable
-    changed = cached_in_field('_changed')(is_changed)
-
-    def is_hidden(self):
-        return self.book.hidden()
-    hidden = cached_in_field('_hidden')(is_hidden)
-
-    @cached_in_field('_short_html')
-    def short_html(self):
-        return render_to_string(
-                    'catalogue/book_list/chunk.html', {'chunk': self})
-
-    def touch(self):
-        update = {
-            "_changed": self.is_changed(),
-            "_hidden": self.is_hidden(),
-            "_short_html": None,
-        }
-        Chunk.objects.filter(pk=self.pk).update(**update)
-        refresh_instance(self)
-
-    def refresh(self):
-        """This should be done offline."""
-        self.changed
-        self.hidden
-        self.short_html
diff --git a/apps/catalogue/models/image.py b/apps/catalogue/models/image.py
deleted file mode 100755 (executable)
index 646dd0a..0000000
+++ /dev/null
@@ -1,172 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
-# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
-#
-from django.conf import settings
-from django.contrib.sites.models import Site
-from django.db import models
-from django.template.loader import render_to_string
-from django.utils.translation import ugettext_lazy as _
-from catalogue.helpers import cached_in_field
-from catalogue.models import Project
-from catalogue.tasks import refresh_instance
-from dvcs import models as dvcs_models
-
-
-class Image(dvcs_models.Document):
-    """ An editable chunk of text. Every Book text is divided into chunks. """
-    REPO_PATH = settings.CATALOGUE_IMAGE_REPO_PATH
-
-    image = models.FileField(_('image'), upload_to='catalogue/images')
-    title = models.CharField(_('title'), max_length=255, blank=True)
-    slug = models.SlugField(_('slug'), unique=True)
-    public = models.BooleanField(_('public'), default=True, db_index=True)
-    project = models.ForeignKey(Project, null=True, blank=True)
-
-    # cache
-    _short_html = models.TextField(null=True, blank=True, editable=False)
-    _new_publishable = models.NullBooleanField(editable=False)
-    _published = models.NullBooleanField(editable=False)
-    _changed = models.NullBooleanField(editable=False)
-
-    class Meta:
-        app_label = 'catalogue'
-        ordering = ['title']
-        verbose_name = _('image')
-        verbose_name_plural = _('images')
-        permissions = [('can_pubmark_image', 'Can mark images for publishing')]
-
-    # Representing
-    # ============
-
-    def __unicode__(self):
-        return self.title
-
-    @models.permalink
-    def get_absolute_url(self):
-        return ("catalogue_image", [self.slug])
-
-    def correct_about(self):
-        return ["http://%s%s" % (
-            Site.objects.get_current().domain,
-            self.get_absolute_url()
-            ),
-            "http://%s%s" % (
-                'obrazy.redakcja.wolnelektury.pl',
-                self.get_absolute_url()
-            )]
-
-    # State & cache
-    # =============
-
-    def last_published(self):
-        try:
-            return self.publish_log.all()[0].timestamp
-        except IndexError:
-            return None
-
-    def assert_publishable(self):
-        from librarian.picture import WLPicture
-        from librarian import NoDublinCore, ParseError, ValidationError
-
-        class SelfImageStore(object):
-            def path(self_, slug, mime_type):
-                """Returns own file object. Ignores slug ad mime_type."""
-                return open(self.image.path)
-
-        publishable = self.publishable()
-        assert publishable, _("There is no publishable revision")
-        picture_xml = publishable.materialize()
-
-        try:
-            picture = WLPicture.from_bytes(
-                    picture_xml.encode('utf-8'),
-                    image_store=SelfImageStore)
-        except ParseError, e:
-            raise AssertionError(_('Invalid XML') + ': ' + str(e))
-        except NoDublinCore:
-            raise AssertionError(_('No Dublin Core found.'))
-        except ValidationError, e:
-            raise AssertionError(_('Invalid Dublin Core') + ': ' + str(e))
-
-        valid_about = self.correct_about()
-        assert picture.picture_info.about in valid_about, \
-                _("rdf:about is not") + " " + valid_about[0]
-
-    def publishable_error(self):
-        try:
-            return self.assert_publishable()
-        except AssertionError, e:
-            return e
-        else:
-            return None
-
-    def accessible(self, request):
-        return self.public or request.user.is_authenticated()
-
-    def is_new_publishable(self):
-        change = self.publishable()
-        if not change:
-            return False
-        return not change.publish_log.exists()
-    new_publishable = cached_in_field('_new_publishable')(is_new_publishable)
-
-    def is_published(self):
-        return self.publish_log.exists()
-    published = cached_in_field('_published')(is_published)
-
-    def is_changed(self):
-        if self.head is None:
-            return False
-        return not self.head.publishable
-    changed = cached_in_field('_changed')(is_changed)
-
-    @cached_in_field('_short_html')
-    def short_html(self):
-        return render_to_string(
-                    'catalogue/image_short.html', {'image': self})
-
-    def refresh(self):
-        """This should be done offline."""
-        self.short_html
-        self.single
-        self.new_publishable
-        self.published
-
-    def touch(self):
-        update = {
-            "_changed": self.is_changed(),
-            "_short_html": None,
-            "_new_publishable": self.is_new_publishable(),
-            "_published": self.is_published(),
-        }
-        Image.objects.filter(pk=self.pk).update(**update)
-        refresh_instance(self)
-
-    def refresh(self):
-        """This should be done offline."""
-        self.changed
-        self.short_html
-
-
-    # Publishing
-    # ==========
-
-    def publish(self, user):
-        """Publishes the picture on behalf of a (local) user."""
-        from base64 import b64encode
-        import apiclient
-        from catalogue.signals import post_publish
-
-        self.assert_publishable()
-        change = self.publishable()
-        picture_xml = change.materialize()
-        picture_data = open(self.image.path).read()
-        apiclient.api_call(user, "pictures/", {
-                "picture_xml": picture_xml,
-                "picture_image_data": b64encode(picture_data),
-            })
-        # record the publish
-        log = self.publish_log.create(user=user, change=change)
-        post_publish.send(sender=log)
diff --git a/apps/catalogue/models/listeners.py b/apps/catalogue/models/listeners.py
deleted file mode 100755 (executable)
index 1cfac27..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
-# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
-#
-from django.contrib.auth.models import User
-from django.db import models
-from catalogue.models import (Book, Chunk, Image, BookPublishRecord,
-        ImagePublishRecord)
-from catalogue.signals import post_publish
-from dvcs.signals import post_publishable
-
-
-def book_changed(sender, instance, created, **kwargs):
-    instance.touch()
-    for c in instance:
-        c.touch()
-models.signals.post_save.connect(book_changed, sender=Book)
-
-
-def chunk_changed(sender, instance, created, **kwargs):
-    instance.book.touch()
-    instance.touch()
-models.signals.post_save.connect(chunk_changed, sender=Chunk)
-
-
-def image_changed(sender, instance, created, **kwargs):
-    instance.touch()
-models.signals.post_save.connect(image_changed, sender=Image)
-
-
-def user_changed(sender, instance, *args, **kwargs):
-    if 'last_login' in (kwargs.get('update_fields') or {}):
-        # Quick hack - this change seems to result from logging user in so just ignore it.
-        return
-    books = set()
-    for c in instance.chunk_set.all():
-        books.add(c.book)
-        c.touch()
-    for b in books:
-        b.touch()
-models.signals.post_save.connect(user_changed, sender=User)
-
-
-def publish_listener(sender, *args, **kwargs):
-    if isinstance(sender, BookPublishRecord):
-        sender.book.touch()
-        for c in sender.book:
-            c.touch()
-    elif isinstance(sender, ImagePublishRecord):
-        sender.image.touch()
-post_publish.connect(publish_listener)
-
-
-def chunk_publishable_listener(sender, *args, **kwargs):
-    sender.tree.touch()
-    if isinstance(sender.tree, Chunk):
-        sender.tree.book.touch()
-post_publishable.connect(chunk_publishable_listener)
-
-def publishable_listener(sender, *args, **kwargs):
-    sender.tree.touch()
-post_publishable.connect(publishable_listener, sender=Image)
-
-
-def listener_create(sender, instance, created, **kwargs):
-    if created:
-        instance.chunk_set.create(number=1, slug='1')
-models.signals.post_save.connect(listener_create, sender=Book)
-
diff --git a/apps/catalogue/models/project.py b/apps/catalogue/models/project.py
deleted file mode 100755 (executable)
index eb95102..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# This file is part of FNP-Redakcja, 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 _
-
-
-class Project(models.Model):
-    """ A project, tracked for funding purposes. """
-
-    name = models.CharField(_('name'), max_length=255, unique=True)
-    notes = models.TextField(_('notes'), blank=True, null=True)
-
-    class Meta:
-        app_label = 'catalogue'
-        ordering = ['name']
-        verbose_name = _('project')
-        verbose_name_plural = _('projects')
-
-    def __unicode__(self):
-        return self.name
diff --git a/apps/catalogue/models/publish_log.py b/apps/catalogue/models/publish_log.py
deleted file mode 100755 (executable)
index 7a8e2f9..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
-# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
-#
-from django.contrib.auth.models import User
-from django.db import models
-from django.utils.translation import ugettext_lazy as _
-from catalogue.models import Chunk, Image
-
-
-class BookPublishRecord(models.Model):
-    """
-        A record left after publishing a Book.
-    """
-
-    book = models.ForeignKey('Book', verbose_name=_('book'), related_name='publish_log')
-    timestamp = models.DateTimeField(_('time'), auto_now_add=True)
-    user = models.ForeignKey(User, verbose_name=_('user'))
-
-    class Meta:
-        app_label = 'catalogue'
-        ordering = ['-timestamp']
-        verbose_name = _('book publish record')
-        verbose_name_plural = _('book publish records')
-
-
-class ChunkPublishRecord(models.Model):
-    """
-        BookPublishRecord details for each Chunk.
-    """
-
-    book_record = models.ForeignKey(BookPublishRecord, verbose_name=_('book publish record'))
-    change = models.ForeignKey(Chunk.change_model, related_name='publish_log', verbose_name=_('change'))
-
-    class Meta:
-        app_label = 'catalogue'
-        verbose_name = _('chunk publish record')
-        verbose_name_plural = _('chunk publish records')
-
-
-class ImagePublishRecord(models.Model):
-    """A record left after publishing an Image."""
-
-    image = models.ForeignKey(Image, verbose_name=_('image'), related_name='publish_log')
-    timestamp = models.DateTimeField(_('time'), auto_now_add=True)
-    user = models.ForeignKey(User, verbose_name=_('user'))
-    change = models.ForeignKey(Image.change_model, related_name='publish_log', verbose_name=_('change'))
-
-    class Meta:
-        app_label = 'catalogue'
-        ordering = ['-timestamp']
-        verbose_name = _('image publish record')
-        verbose_name_plural = _('image publish records')
diff --git a/apps/catalogue/signals.py b/apps/catalogue/signals.py
deleted file mode 100644 (file)
index 62ca514..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-from django.dispatch import Signal
-
-post_publish = Signal()
diff --git a/apps/catalogue/tasks.py b/apps/catalogue/tasks.py
deleted file mode 100644 (file)
index 9507c41..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-# -*- coding: utf-8 -*-
-from celery.task import task
-from django.utils import translation
-
-
-@task(ignore_result=True)
-def _refresh_by_pk(cls, pk, language=None):
-    prev_language = translation.get_language()
-    if language:
-        translation.activate(language)
-    try:
-        cls._default_manager.get(pk=pk).refresh()
-    except cls.DoesNotExist:
-        pass
-    finally:
-        translation.activate(prev_language)
-
-
-def refresh_instance(instance):
-    _refresh_by_pk.delay(type(instance), instance.pk, translation.get_language())
-
-
-@task(ignore_result=True)
-def book_content_updated(book):
-    book.refresh_dc_cache()
diff --git a/apps/catalogue/templates/catalogue/active_users_list.html b/apps/catalogue/templates/catalogue/active_users_list.html
deleted file mode 100755 (executable)
index f711b60..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-{% extends "catalogue/base.html" %}
-{% load i18n %}
-
-
-{% block titleextra %}{% trans "Active users" %}{% endblock %}
-
-
-{% block content %}
-
-<h1>
-    {% trans "Active users since" %} {{ since }}
-</h1>
-
-<ul>
-{% for email, names, count in users %}
-<li>{% for name in names %}{{ name }},  {% endfor %}<a href="mailto:{{ email }}">{{ email }}</a> ({{ count }})</li>
-{% endfor %}
-</ul>
-
-{% endblock content %}
diff --git a/apps/catalogue/templates/catalogue/activity.html b/apps/catalogue/templates/catalogue/activity.html
deleted file mode 100755 (executable)
index 3bb8afb..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-{% extends "catalogue/base.html" %}
-{% load i18n %}
-{% load wall %}
-
-
-{% block titleextra %}{% trans "Activity" %}{% endblock %}
-
-
-{% block content %}
-
-<h1><a href='{% url "catalogue_activity" prev_day.isoformat %}'>&lt;</a>
-    {% trans "Activity" %}: {{ day }}
-    {% if next_day %}
-        <a href='{% url "catalogue_activity" next_day.isoformat %}'>&gt;</a>
-    {% endif %}
-</h1>
-
-    {% day_wall day %}
-{% endblock content %}
diff --git a/apps/catalogue/templates/catalogue/base.html b/apps/catalogue/templates/catalogue/base.html
deleted file mode 100644 (file)
index 8577f10..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-{% load pipeline i18n %}
-{% load catalogue %}
-<!DOCTYPE html>
-<html>
-<head lang="{{ LANGUAGE_CODE }}">
-    <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
-    <link rel="icon" href="{{ STATIC_URL }}img/pr-icon.png" type="image/png" />
-    {% stylesheet 'catalogue' %}
-    <title>{% block title %}{% block titleextra %}{% endblock %} ::
-        {% trans "Platforma Redakcyjna" %}{% endblock title %}</title>
-    {% block add_css %}{% endblock %}
-</head>
-<body>
-<!--#include file='/pozor.html'-->
-<div id="tabs-nav">
-
-    <a href="{% url 'catalogue_document_list' %}">
-        <img id="logo" src="{{ STATIC_URL }}img/wl-orange.png" alt="Platforma" />
-    </a>
-
-    <div id="tabs-nav-left">
-        {% main_tabs %}
-    </div>
-
-    <span id="login-box">
-        {% include "registration/head_login.html" %}
-    </span>
-
-    <div class='clr' ></div>
-</div>
-
-<div id="content">
-
-{% block content %}
-<div id="catalogue_layout_left_column">
-       {% block leftcolumn %}
-       {% endblock leftcolumn %}
-</div>
-<div id="catalogue_layout_right_column">
-       {% block rightcolumn %}
-       {% endblock rightcolumn %}
-</div>
-{% endblock content %}
-
-</div>
-
-
-<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
-{% javascript 'catalogue' %}
-{% block add_js %}{% endblock %}
-{% block extrabody %}
-{% endblock %}
-</body>
-</html>
diff --git a/apps/catalogue/templates/catalogue/book_append_to.html b/apps/catalogue/templates/catalogue/book_append_to.html
deleted file mode 100755 (executable)
index c1ecc29..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-{% extends "catalogue/base.html" %}
-{% load i18n %}
-
-{% block titleextra %}{% trans "Append book" %}{% endblock %}
-
-{% block leftcolumn %}
-       <form enctype="multipart/form-data" method="POST" action="">
-    {% csrf_token %}
-       {{ form.as_p }}
-
-       <p><button type="submit">{% trans "Append book" %}</button></p>
-       </form>
-{% endblock leftcolumn %}
-
-{% block rightcolumn %}
-{% endblock rightcolumn %}
diff --git a/apps/catalogue/templates/catalogue/book_detail.html b/apps/catalogue/templates/catalogue/book_detail.html
deleted file mode 100755 (executable)
index 4712edf..0000000
+++ /dev/null
@@ -1,103 +0,0 @@
-{% extends "catalogue/base.html" %}
-{% load book_list i18n %}
-
-
-{% block titleextra %}{{ book.title }}{% endblock %}
-
-
-{% block content %}
-
-
-<h1>{{ book.title }}</h1>
-
-
-{% if editable %}<form method='POST'>{% csrf_token %}{% endif %}
-<table class='editable'><tbody>
-    {{ form.as_table }}
-    {% if editable %}
-        <tr><td></td><td><button type="submit">{% trans "Save" %}</button></td></tr>
-    {% endif %}
-</tbody></table>
-{% if editable %}</form>{% endif %}
-
-{% if editable %}
-    {% if book.gallery %}
-    <p><a href="{% url 'catalogue_book_gallery' book.slug %}">{% trans "Edit gallery" %}</a></p>
-    {% endif %}
-
-    <p><a href="{% url 'catalogue_book_append' book.slug %}">{% trans "Append to other book" %}</a></p>
-{% endif %}
-
-
-<div class='section'>
-
-    <h2>{% trans "Chunks" %}</h2>
-
-    <table class='single-book-list'><tbody>
-    {% for chunk in book %}
-        {{ chunk.short_html|safe }}
-    {% endfor %}
-    </tbody></table>
-
-</div>
-
-
-
-<div class='section'>
-
-
-<h2>{% trans "Publication" %}</h2>
-
-<div class="cover-preview">
-<img class="cover-preview" src="{% url 'cover_preview' book.slug %}" />
-{% if book.dc_cover_image %}
-    <a href="{{ book.dc_cover_image.get_absolute_url }}">{{ book.dc_cover_image }}</a>
-{% endif %}
-</div>
-
-<p>{% trans "Last published" %}: 
-    {% if book.last_published %}
-        {{ book.last_published }}
-    {% else %}
-        &mdash;
-    {% endif %}
-</p>
-
-{% if publishable %}
-    <p>
-    <a href="{% url 'catalogue_book_xml' book.slug %}" rel="nofollow">{% trans "Full XML" %}</a><br/>
-    <a target="_blank" href="{% url 'catalogue_book_html' book.slug %}" rel="nofollow">{% trans "HTML version" %}</a><br/>
-    <a href="{% url 'catalogue_book_txt' book.slug %}" rel="nofollow">{% trans "TXT version" %}</a><br/>
-    <a href="{% url 'catalogue_book_pdf' book.slug %}" rel="nofollow">{% trans "PDF version" %}</a><br/>
-    <a href="{% url 'catalogue_book_pdf_mobile' book.slug %}" rel="nofollow">{% trans "PDF version for mobiles" %}</a><br/>
-    <a href="{% url 'catalogue_book_epub' book.slug %}" rel="nofollow">{% trans "EPUB version" %}</a><br/>
-    <a href="{% url 'catalogue_book_mobi' book.slug %}" rel="nofollow">{% trans "MOBI version" %}</a><br/>
-    </p>
-
-    {% if user.is_authenticated %}
-        <!--
-        Angel photos:
-        Angels in Ely Cathedral (http://www.flickr.com/photos/21804434@N02/4483220595/) /
-        mira66 (http://www.flickr.com/photos/21804434@N02/) /
-        CC BY 2.0 (http://creativecommons.org/licenses/by/2.0/)
-        -->
-        <form method="POST" action="{% url 'catalogue_publish' book.slug %}">{% csrf_token %}
-            {{ publish_options_form.as_p }}
-            <img src="{{ STATIC_URL }}img/angel-left.png" style="vertical-align: middle" />
-            <button id="publish-button" type="submit">
-                <span>{% trans "Publish" %}</span></button>
-            <img src="{{ STATIC_URL }}img/angel-right.png" style="vertical-align: middle" />
-            </form>
-    {% else %}
-        <a href="{% url 'login' %}">{% trans "Log in to publish." %}</a>
-    {% endif %}
-{% else %}
-    <p>{% trans "This book can't be published yet, because:" %}</p>
-    <ul><li>{{ publishable_error }}</li></ul>
-{% endif %}
-
-<div style="clear: both;"></div>
-</div>
-
-
-{% endblock content %}
diff --git a/apps/catalogue/templates/catalogue/book_edit.html b/apps/catalogue/templates/catalogue/book_edit.html
deleted file mode 100755 (executable)
index 43fe0ea..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-{% extends "catalogue/base.html" %}
-{% load i18n %}
-
-
-{% block titleextra %}{% trans "Edit book" %}{% endblock %}
-
-
-{% block leftcolumn %}
-       <form enctype="multipart/form-data" method="POST" action="">
-    {% csrf_token %}
-       {{ form.as_p }}
-
-       <p><button type="submit">{% trans "Save" %}</button></p>
-       </form>
-{% endblock leftcolumn %}
-
-{% block rightcolumn %}
-{% endblock rightcolumn %}
diff --git a/apps/catalogue/templates/catalogue/book_html.html b/apps/catalogue/templates/catalogue/book_html.html
deleted file mode 100755 (executable)
index 518811e..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-{% 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">
-    <head>
-        <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
-        <title>{{ book.title }}</title>
-    </head>
-    <body>
-        <div id="menu">
-            <ul>
-                <li><a href="#toc">{% trans "Table of contents" %}</a></li>
-                <li><a href="#nota_red">{% trans "Edit. note" %}</a></li>
-                <li><a href="#info">{% trans "Infobox" %}</a></li>
-            </ul>
-        </div>
-        <div id="info">
-            {#% book_info book %#}
-        </div>
-        <div id="header">
-            <div id="logo">
-                <a href="/"><img src="http://static.wolnelektury.pl/img/logo.png" alt="WolneLektury.pl - logo" /></a>
-            </div>
-        </div>
-
-        {{ html|safe }}
-
-    </body>
-</html>
diff --git a/apps/catalogue/templates/catalogue/book_list/book.html b/apps/catalogue/templates/catalogue/book_list/book.html
deleted file mode 100755 (executable)
index f6a0fcd..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-{% load i18n %}
-{% load username from common_tags %}
-
-{% if book.single %}
-    {% with book.0 as chunk %}
-    <tr>
-        <td><input type="checkbox" name="select_book" value="{{book.id}}" data-chunk-id="{{chunk.id}}"/></td>
-        <td><a href="{% url 'catalogue_book' book.slug %}" title='{% trans "Book settings" %}'>[B]</a></td>
-        <td><a href="{% url 'catalogue_chunk_edit' book.slug chunk.slug %}" title='{% trans "Chunk settings" %}'>[c]</a></td>
-        <td><a target="_blank"
-                    href="{% url 'wiki_editor' book.slug %}">
-                    {{ book.title }}</a></td>
-        <td>{% if chunk.stage %}
-            {{ chunk.stage }}
-        {% else %}–
-        {% endif %}</td>
-        <td class='user-column'>{% if chunk.user %}<a href="{% url 'catalogue_user' chunk.user.username %}">{{ chunk.user|username }}</a>{% endif %}</td>
-        <td>
-            {% if chunk.published %}P{% endif %}
-            {% if book.new_publishable %}p{% endif %}
-            {% if chunk.changed %}+{% endif %}
-        </td>
-        <td>{{ book.project.name }}</td>
-    </tr>
-    {% endwith %}
-{% else %}
-    <tr>
-        <td><input type="checkbox" name="select_book" value="{{book.id}}"/></td>
-        <td class='book-settings-link'><a href="{% url 'catalogue_book' book.slug %}" title='{% trans "Book settings" %}'>[B]</a></td>
-        <td></td>
-        <td>{{ book.title }}</td>
-        <td></td>
-        <td class='user-column'></td>
-        <td>
-            {% if book.published %}P{% endif %}
-            {% if book.new_publishable %}p{% endif %}
-        </td>
-        <td>{{ book.project.name }}</td>
-    </tr>
-{% endif %}
diff --git a/apps/catalogue/templates/catalogue/book_list/book_list.html b/apps/catalogue/templates/catalogue/book_list/book_list.html
deleted file mode 100755 (executable)
index e238827..0000000
+++ /dev/null
@@ -1,114 +0,0 @@
-{% load i18n %}
-{% load pagination_tags %}
-{% load username from common_tags %}
-
-
-<form name='filter' action='{{ request.path }}'>
-<input type='hidden' name="title" value="{{ request.GET.title }}" />
-<input type='hidden' name="stage" value="{{ request.GET.stage }}" />
-{% if not viewed_user %}
-    <input type='hidden' name="user" value="{{ request.GET.user }}" />
-{% endif %}
-<input type='hidden' name="all" value="{{ request.GET.all }}" />
-<input type='hidden' name="status" value="{{ request.GET.status }}" />
-<input type='hidden' name="project" value="{{ request.GET.project }}" />
-</form>
-
-
-<table id="file-list"{% if viewed_user %} class="book-list-user"{% endif %}>
-    <thead><tr>
-       <th></th>
-        <th></th>
-        <th>
-            <input class='check-filter' type='checkbox' name='all' title='{% trans "Show hidden books" %}'
-                {% if request.GET.all %}checked='checked'{% endif %} />
-            </th>
-        <th class='book-search-column'>
-            <form>
-            <input title='{% trans "Search in book titles" %}' name="title"
-                class='text-filter' value="{{ request.GET.title }}" />
-            </form>
-        </th>
-        <th><select name="stage" class="filter">
-            <option value=''>- {% trans "stage" %} -</option>
-            <option {% if request.GET.stage == '-' %}selected="selected"
-                    {% endif %}value="-">- {% trans "none" %} -</option>
-            {% for stage in stages %}
-                <option {% if request.GET.stage == stage.slug %}selected="selected"
-                    {% endif %}value="{{ stage.slug }}">{{ stage.name }}</option>
-            {% endfor %}
-        </select></th>
-
-        {% if not viewed_user %}
-            <th><select name="user" class="filter">
-                <option value=''>- {% trans "editor" %} -</option>
-                <option {% if request.GET.user == '-' %}selected="selected"
-                        {% endif %}value="-">- {% trans "none" %} -</option>
-                {% for user in users %}
-                    <option {% if request.GET.user == user.username %}selected="selected"
-                        {% endif %}value="{{ user.username }}">{{ user|username }} ({{ user.count }})</option>
-                {% endfor %}
-            </select></th>
-        {% else %}
-            <th style='display: none'></th>
-        {% endif %}
-
-        <th><select name="status" class="filter">
-            <option value=''>- {% trans "status" %} -</option>
-            {% for state, label in states %}
-                <option {% if request.GET.status == state %}selected="selected"
-                        {% endif %}value='{{ state }}'>{{ label }}</option>
-            {% endfor %}
-        </select></th>
-
-        <th><select name="project" class="filter">
-            <option value=''>- {% trans "project" %} -</option>
-                <option {% if request.GET.project == '-' %}selected="selected"
-                        {% endif %}value="-">- {% trans "none" %} -</option>
-            {% for project in projects %}
-                <option {% if request.GET.project == project.pk|slugify %}selected="selected"
-                        {% endif %}value='{{ project.pk }}'>{{ project.name }}</option>
-            {% endfor %}
-        </select></th>
-
-    </tr></thead>
-
-    {% with cnt=books|length %}
-    {% autopaginate books 100 %}
-    <tbody>
-    {% for item in books %}
-        {% with item.book as book %}
-            {{ book.short_html|safe }}
-            {% if not book.single %}
-                {% for chunk in item.chunks %}
-                    {{ chunk.short_html|safe }}
-                {% endfor %}
-            {% endif %}
-        {% endwith %}
-    {% endfor %}
-    <tr><th class='paginator' colspan="6">
-        {% paginate %}
-        {% blocktrans count c=cnt %}{{c}} book{% plural %}{{c}} books{% endblocktrans %}</th></tr>
-    </tbody>
-    {% endwith %}
-</table>
-{% if not books %}
-    <p>{% trans "No books found." %}</p>
-{% endif %}
-
-<form id='chunk_mass_edit' action='{% url "catalogue_chunk_mass_edit" %}' style="display:none;">
-{% csrf_token %}
-<input type="hidden" name="ids" />
-<label for="mass_edit_stage">{% trans "Set stage" %}</label><input type="hidden" name="stage" id="mass_edit_stage"/>
-<label for="mass_edit_user">{% trans "Set user" %}</label><input type="hidden" name="user" id="mass_edit_user" />
-<input type="hidden" name="status" />
-<label for="mass_edit_project">{% trans "Project" %}</label><input type="hidden" name="project" id="mass_edit_project" />
-<label for="mass_edit_more_users">{% trans "More users" %}</label>
-</form>
-
-<select name="other-user" style="display:none;">
-  {% for user in other_users %}
-  <option {% if request.GET.user == user.username %}selected="selected"
-          {% endif %}value="{{ user.username }}">{{ user|username }} ({{ user.count }})</option>
-  {% endfor %}
-</select>
diff --git a/apps/catalogue/templates/catalogue/book_list/chunk.html b/apps/catalogue/templates/catalogue/book_list/chunk.html
deleted file mode 100755 (executable)
index 0d21895..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-{% load i18n %}
-{% load username from common_tags %}
-
-<tr>
-    <td><input type="checkbox" name="select_chunk" value="{{chunk.id}}" data-book-id="{{chunk.book.id}}" /></td>
-    <td class='book-settings-column'></td>
-    <td><a href="{% url 'catalogue_chunk_edit' chunk.book.slug chunk.slug %}" title='{% trans "Chunk settings" %}'>[c]</a></td>
-    <td><a target="_blank" href="{{ chunk.get_absolute_url }}">
-            <span class='chunkno'>{{ chunk.number }}.</span>
-            {{ chunk.title }}</a></td>
-    <td>{% if chunk.stage %}
-            {{ chunk.stage }}
-        {% else %}
-            –
-        {% endif %}</td>
-        <td class='user-column'>{% if chunk.user %}
-            <a href="{% url 'catalogue_user' chunk.user.username %}">
-                {{ chunk.user|username }}
-            </a>{% else %}
-            
-            {% endif %}</td>
-</td>
-<td>
-    {% if chunk.new_publishable %}p{% endif %}
-    {% if chunk.changed %}+{% endif %}
-</td>
-</tr>
diff --git a/apps/catalogue/templates/catalogue/book_text.html b/apps/catalogue/templates/catalogue/book_text.html
deleted file mode 100644 (file)
index 2e48448..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-{% load i18n pipeline %}
-<!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">
-    <head>
-        <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
-        <title>{% trans "Redakcja" %} :: {{ book.title }}</title>
-       {% stylesheet 'book' %}
-        <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
-       {% javascript 'book' %}
-    </head>
-    <body>
-        <div id="menu">
-            <ul>
-                <li><a class="menu" href="#toc">{% trans "Table of contents" %}</a></li>
-{#                <li><a class="menu" href="#themes">{% trans "Themes" %}</a></li>#}
-                <li><a class="menu" href="#nota_red">{% trans "Edit. note" %}</a></li>
-{#                <li><a class="menu" href="#info">{% trans "Infobox" %}</a></li>#}
-{#                <li><a href="{{ book.get_absolute_url }}">{% trans "Book's page" %}</a></li> #}
-{#                <li><a class="menu" href="#download">{% trans "Download" %}</a></li>#}
-            </ul>
-        </div>
-        <div id="header">
-            <a href="/"><img src="/media/static/img/logo-220.png" alt="Wolne Lektury" /></a>
-        </div>
-        {{ html|safe }}
-    </body>
-</html>
diff --git a/apps/catalogue/templates/catalogue/chunk_add.html b/apps/catalogue/templates/catalogue/chunk_add.html
deleted file mode 100755 (executable)
index f813b6f..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-{% extends "catalogue/base.html" %}
-{% load i18n %}
-
-
-{% block titleextra %}{% trans "Split chunk" %}{% endblock %}
-
-
-{% block content %}
-    <h1>{% trans "Split chunk" %}</h1>
-
-       <form enctype="multipart/form-data" method="POST">
-    {% csrf_token %}
-    <table class='editable'>
-        <tr><th>{% trans "Insert empty chunk after" %}:</th>
-            <td><a href="{{ chunk.get_absolute_url }}">{{ chunk.pretty_name }}</a></td></tr>
-        {{ form.as_table }}
-        <tr><td></td><td><button type="submit">{% trans "Add chunk" %}</button></td></tr>
-    </table>
-       </form>
-{% endblock content %}
diff --git a/apps/catalogue/templates/catalogue/chunk_edit.html b/apps/catalogue/templates/catalogue/chunk_edit.html
deleted file mode 100755 (executable)
index 2006226..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-{% extends "catalogue/base.html" %}
-{% load i18n %}
-
-
-{% block titleextra %}{% trans "Chunk settings" %}{% endblock %}
-
-
-{% block content %}
-    <h1>{% trans "Chunk settings" %}</h1>
-
-       <form enctype="multipart/form-data" method="POST" action="{% if go_next %}?next={{ go_next }}{% endif %}">
-    {% csrf_token %}
-    <table class='editable'>
-        <tr><th>{% trans "Book" %}:</th><td>{{ chunk.book }} ({{ chunk.number }}/{{ chunk.book|length }})</td></tr>
-        {{ form.as_table}}
-        <tr><td></td><td><button type="submit">{% trans "Save" %}</button></td></tr>
-    </table>
-
-       </form>
-
-
-    <p><a href="{% url "catalogue_chunk_add" chunk.book.slug chunk.slug %}">{% trans "Split chunk" %}</a></p>
-
-{% endblock content %}
diff --git a/apps/catalogue/templates/catalogue/document_create_missing.html b/apps/catalogue/templates/catalogue/document_create_missing.html
deleted file mode 100644 (file)
index aa2ce06..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-{% extends "catalogue/base.html" %}
-{% load i18n %}
-
-
-{% block titleextra %}{% trans "Create a new book" %}{% endblock %}
-
-
-{% block content %}
-    <h1>{% trans "Create a new book" %}</h1>
-
-    <form enctype="multipart/form-data" method="POST">
-    {% csrf_token %}
-    <table class='editable'>
-        {{ form.as_table}}
-        <tr><td></td><td><button type="submit">{% trans "Create book" %}</button></td></tr>
-    </table>
-    </form>
-{% endblock content %}
diff --git a/apps/catalogue/templates/catalogue/document_list.html b/apps/catalogue/templates/catalogue/document_list.html
deleted file mode 100644 (file)
index fe3598e..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-{% extends "catalogue/base.html" %}
-
-{% load i18n %}
-{% load catalogue book_list %}
-{% load pipeline %}
-
-{% block titleextra %}{% trans "Book list" %}{% endblock %}
-
-
-{% block add_js %}
-{% javascript 'book_list' %}
-{% endblock %}
-
-{% block add_css %}
-{% stylesheet 'book_list' %}
-{% endblock %}
-
-{% block content %}
-    {% book_list %}
-{% endblock content %}
diff --git a/apps/catalogue/templates/catalogue/document_upload.html b/apps/catalogue/templates/catalogue/document_upload.html
deleted file mode 100644 (file)
index 009d154..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-{% extends "catalogue/base.html" %}
-{% load i18n %}
-
-
-{% block titleextra %}{% trans "Bulk document upload" %}{% endblock %}
-
-
-{% block leftcolumn %}
-
-
-<h2>{% trans "Bulk documents upload" %}</h2>
-
-<p>
-{% trans "Please submit a ZIP with UTF-8 encoded XML files. Files not ending with <code>.xml</code> will be ignored." %}
-</p>
-
-<form enctype="multipart/form-data" method="POST" action="{{ request.path }}">
-{% csrf_token %}
-{{ form.as_p }}
-<p><button type="submit">{% trans "Upload" %}</button></p>
-</form>
-
-<hr/>
-
-{% if error_list %}
-
-    <p class='error'>{% trans "There have been some errors. No files have been added to the repository." %}
-    <h3>{% trans "Offending files" %}</h3>
-    <ul id='error-list'>
-        {% for filename, title, error in error_list %}
-            <li>{{ title }} (<code>{{ filename }}</code>): {{ error }}</li>
-        {% endfor %}
-    </ul>
-
-    {% if ok_list %}
-    <h3>{% trans "Correct files" %}</h3>
-        <ul>
-            {% for filename, slug, title in ok_list %}
-                <li>{{ title }} (<code>{{ filename }}</code>)</li>
-            {% endfor %}
-        </ul>
-    {% endif %}
-
-{% else %}
-
-    {% if ok_list %}
-        <p class='success'>{% trans "Files have been successfully uploaded to the repository." %}</p>
-        <h3>{% trans "Uploaded files" %}</h3>
-        <ul id='ok-list'>
-        {% for filename, slug, title in ok_list %}
-            <li><a href='{% url "wiki_editor" slug %}'>{{ title }}</a> (<code>{{ filename }})</a></li>
-        {% endfor %}
-        </ul>
-    {% endif %}
-{% endif %}
-
-{% if skipped_list %}
-    <h3>{% trans "Skipped files" %}</h3>
-    <p>{% trans "Files skipped due to no <code>.xml</code> extension" %}</p>
-    <ul id='skipped-list'>
-        {% for filename in skipped_list %}
-            <li>{{ filename }}</li>
-        {% endfor %}
-    </ul>
-{% endif %}
-
-
-{% endblock leftcolumn %}
-
-
-{% block rightcolumn %}
-{% endblock rightcolumn %}
diff --git a/apps/catalogue/templates/catalogue/image_detail.html b/apps/catalogue/templates/catalogue/image_detail.html
deleted file mode 100755 (executable)
index 8ad2a63..0000000
+++ /dev/null
@@ -1,71 +0,0 @@
-{% extends "catalogue/base.html" %}
-{% load book_list i18n %}
-
-
-{% block titleextra %}{{ object.title }}{% endblock %}
-
-
-{% block content %}
-
-
-<h1>{{ object.title }}</h1>
-
-
-{% if editable %}<form method='POST'>{% csrf_token %}{% endif %}
-<table class='editable'><tbody>
-    {{ form.as_table }}
-    {% if editable %}
-        <tr><td></td><td><button type="submit">{% trans "Save" %}</button></td></tr>
-    {% endif %}
-</tbody></table>
-{% if editable %}</form>{% endif %}
-
-
-
-<div class='section'>
-    <h2>{% trans "Editor" %}</h2>
-
-    <p><a href="{% url 'wiki_img_editor' object.slug %}">{% trans "Proceed to the editor." %}</a></p>
-</div>
-
-
-
-<div class='section'>
-
-
-<h2>{% trans "Publication" %}</h2>
-
-<p>{% trans "Last published" %}: 
-    {% if object.last_published %}
-        {{ object.last_published }}
-    {% else %}
-        &mdash;
-    {% endif %}
-</p>
-
-{% if publishable %}
-    {% if user.is_authenticated %}
-        <!--
-        Angel photos:
-        Angels in Ely Cathedral (http://www.flickr.com/photos/21804434@N02/4483220595/) /
-        mira66 (http://www.flickr.com/photos/21804434@N02/) /
-        CC BY 2.0 (http://creativecommons.org/licenses/by/2.0/)
-        -->
-        <form method="POST" action="{% url 'catalogue_publish_image' object.slug %}">{% csrf_token %}
-            <!--img src="{{ STATIC_URL }}img/angel-left.png" style="vertical-align: middle" /-->
-            <button id="publish-button" type="submit">
-                <span>{% trans "Publish" %}</span></button>
-            <!--img src="{{ STATIC_URL }}img/angel-right.png" style="vertical-align: middle" /-->
-            </form>
-    {% else %}
-        <a href="{% url 'login' %}">{% trans "Log in to publish." %}</a>
-    {% endif %}
-{% else %}
-    <p>{% trans "This book can't be published yet, because:" %}</p>
-    <ul><li>{{ publishable_error }}</li></ul>
-{% endif %}
-
-</div>
-
-
-{% endblock content %}
diff --git a/apps/catalogue/templates/catalogue/image_list.html b/apps/catalogue/templates/catalogue/image_list.html
deleted file mode 100755 (executable)
index 4ce1668..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-{% extends "catalogue/base.html" %}
-
-{% load i18n %}
-{% load catalogue book_list %}
-{% load pipeline %}
-
-
-{% block titleextra %}{% trans "Image list" %}{% endblock %}
-
-
-{% block add_js %}
-{% javascript 'book_list' %}
-{% endblock %}
-
-{% block add_css %}
-{% stylesheet 'book_list' %}
-{% endblock %}
-
-
-{% block content %}
-    {% image_list %}
-{% endblock content %}
diff --git a/apps/catalogue/templates/catalogue/image_short.html b/apps/catalogue/templates/catalogue/image_short.html
deleted file mode 100755 (executable)
index c7ff77b..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-{% load i18n %}
-{% load username from common_tags %}
-
-<tr>
-    <td><input type="checkbox" name="select_chunk" value="{{image.id}}"/></td>
-    <td><a href="{% url 'catalogue_image' image.slug %}" title='{% trans "Image settings" %}'>[B]</a></td>
-    <td><a target="_blank"
-                href="{% url 'wiki_img_editor' image.slug %}">
-                {{ image.title }}</a></td>
-    <td>{% if image.stage %}
-        {{ image.stage }}
-    {% else %}–
-    {% endif %}</td>
-    <td class='user-column'>{% if image.user %}<a href="{% url 'catalogue_user' image.user.username %}">{{ image.user|username }}</a>{% endif %}</td>
-    <td>
-        {% if image.published %}P{% endif %}
-        {% if image.new_publishable %}p{% endif %}
-        {% if image.changed %}+{% endif %}
-    </td>
-    <td>{{ image.project.name }}</td>
-</tr>
diff --git a/apps/catalogue/templates/catalogue/image_table.html b/apps/catalogue/templates/catalogue/image_table.html
deleted file mode 100755 (executable)
index e6caedd..0000000
+++ /dev/null
@@ -1,100 +0,0 @@
-{% load i18n %}
-{% load pagination_tags %}
-{% load username from common_tags %}
-
-
-
-<form name='filter' action='{{ request.path }}'>
-<input type='hidden' name="title" value="{{ request.GET.title }}" />
-<input type='hidden' name="stage" value="{{ request.GET.stage }}" />
-{% if not viewed_user %}
-    <input type='hidden' name="user" value="{{ request.GET.user }}" />
-{% endif %}
-<input type='hidden' name="status" value="{{ request.GET.status }}" />
-<input type='hidden' name="project" value="{{ request.GET.project }}" />
-</form>
-
-<table id="file-list"{% if viewed_user %} class="book-list-user"{% endif %}>
-    <thead><tr>
-        <th></th>
-        <th></th>
-        <th class='book-search-column'>
-            <form>
-            <input title='{% trans "Search in book titles" %}' name="title"
-                class='text-filter' value="{{ request.GET.title }}" />
-            </form>
-        </th>
-        <th><select name="stage" class="filter">
-            <option value=''>- {% trans "stage" %} -</option>
-            <option {% if request.GET.stage == '-' %}selected="selected"
-                    {% endif %}value="-">- {% trans "none" %} -</option>
-            {% for stage in stages %}
-                <option {% if request.GET.stage == stage.slug %}selected="selected"
-                    {% endif %}value="{{ stage.slug }}">{{ stage.name }}</option>
-            {% endfor %}
-        </select></th>
-
-        {% if not viewed_user %}
-            <th><select name="user" class="filter">
-                <option value=''>- {% trans "editor" %} -</option>
-                <option {% if request.GET.user == '-' %}selected="selected"
-                        {% endif %}value="-">- {% trans "none" %} -</option>
-                {% for user in users %}
-                    <option {% if request.GET.user == user.username %}selected="selected"
-                        {% endif %}value="{{ user.username }}">{{ user|username }} ({{ user.count }})</option>
-                {% endfor %}
-            </select></th>
-        {% endif %}
-
-        <th><select name="status" class="filter">
-            <option value=''>- {% trans "status" %} -</option>
-            {% for state, label in states %}
-                <option {% if request.GET.status == state %}selected="selected"
-                        {% endif %}value='{{ state }}'>{{ label }}</option>
-            {% endfor %}
-        </select></th>
-
-        <th><select name="project" class="filter">
-            <option value=''>- {% trans "project" %} -</option>
-                <option {% if request.GET.project == '-' %}selected="selected"
-                        {% endif %}value="-">- {% trans "none" %} -</option>
-            {% for project in projects %}
-                <option {% if request.GET.project == project.pk|slugify %}selected="selected"
-                        {% endif %}value='{{ project.pk }}'>{{ project.name }}</option>
-            {% endfor %}
-        </select></th>
-
-    </tr></thead>
-
-    {% with cnt=objects|length %}
-    {% autopaginate objects 100 %}
-    <tbody>
-    {% for item in objects %}
-        {{ item.short_html|safe }}
-    {% endfor %}
-    <tr><th class='paginator' colspan="6">
-        {% paginate %}
-        {% blocktrans count c=cnt %}{{c}} image{% plural %}{{c}} images{% endblocktrans %}</th></tr>
-    </tbody>
-    {% endwith %}
-</table>
-{% if not objects %}
-    <p>{% trans "No images found." %}</p>
-{% endif %}
-
-<form id='chunk_mass_edit' action='{% url "catalogue_image_mass_edit" %}' style="display:none;">
-{% csrf_token %}
-<input type="hidden" name="ids" />
-<label for="mass_edit_stage">{% trans "Set stage" %}</label><input type="hidden" name="stage" id="mass_edit_stage"/>
-<label for="mass_edit_user">{% trans "Set user" %}</label><input type="hidden" name="user" id="mass_edit_stage" />
-<input type="hidden" name="status" />
-<label for="mass_edit_project">{% trans "Project" %}</label><input type="hidden" name="project" id="mass_edit_project" />
-<label for="mass_edit_more_users">{% trans "More users" %}</label>
-</form>
-
-<select name="other-user" style="display:none;">
-  {% for user in other_users %}
-  <option {% if request.GET.user == user.username %}selected="selected"
-          {% endif %}value="{{ user.username }}">{{ user|username }} ({{ user.count }})</option>
-  {% endfor %}
-</select>
diff --git a/apps/catalogue/templates/catalogue/main_tabs.html b/apps/catalogue/templates/catalogue/main_tabs.html
deleted file mode 100755 (executable)
index 82321cc..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-{% for tab in tabs %}
-    <a {% ifequal active_tab tab.slug %}class="active" {% endifequal %}href="{{ tab.url }}">{{ tab.caption }}</a>
-{% endfor %}
diff --git a/apps/catalogue/templates/catalogue/mark_final.html b/apps/catalogue/templates/catalogue/mark_final.html
deleted file mode 100644 (file)
index 9ed740c..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-{% extends "catalogue/base.html" %}
-
-{% block titleextra %}Oznacz książki{% endblock %}
-
-
-{% block leftcolumn %}
-
-<h1>Oznacz książki</h1>
-
-<form method="post" action="">
-    {% csrf_token %}
-    {{ form.as_p }}
-    <input type="submit" value="Oznacz">
-</form>
-
-{% endblock leftcolumn %}
\ No newline at end of file
diff --git a/apps/catalogue/templates/catalogue/mark_final_completed.html b/apps/catalogue/templates/catalogue/mark_final_completed.html
deleted file mode 100644 (file)
index 1b37c83..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-{% extends "catalogue/base.html" %}
-
-{% block titleextra %}Oznaczono książki{% endblock %}
-
-
-{% block leftcolumn %}
-
-<h1>Oznaczono książki</h1>
-
-<p>Książki zostały oznaczone.</p>
-
-{% endblock leftcolumn %}
\ No newline at end of file
diff --git a/apps/catalogue/templates/catalogue/my_page.html b/apps/catalogue/templates/catalogue/my_page.html
deleted file mode 100755 (executable)
index d334750..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-{% extends "catalogue/base.html" %}
-
-{% load i18n %}
-{% load catalogue book_list wall %}
-{% load pipeline %}
-
-{% block add_js %}
-{% javascript 'book_list' %}
-{% endblock %}
-
-{% block add_css %}
-{% stylesheet 'book_list' %}
-{% endblock %}
-
-{% block titleextra %}{% trans "My page" %}{% endblock %}
-
-
-{% block leftcolumn %}
-    {% book_list request.user %}
-{% endblock leftcolumn %}
-
-{% block rightcolumn %}
-    <div id="last-edited-list">
-        <h2>{% trans "Your last edited documents" %}</h2>
-        <ol>
-            {% for edit_url, item in last_books %}
-                <li><a
-                {% if edit_url|length == 2 %}
-                    {# Temporary support for old-style last_books. #}
-                    href="{% url 'wiki_editor' edit_url.0 edit_url.1 %}"
-                {% else %}
-                    href="{{ edit_url }}"
-                {% endif %}
-                target="_blank">{{ item.title }}</a><br/><span class="date">({{ item.time|date:"H:i:s, d/m/Y" }})</span></li>
-            {% endfor %}
-        </ol>
-    </div>
-
-    <h2>{% trans "Recent activity for" %} {{ request.user|nice_name }}</h2>
-    {% wall request.user 10 %}
-{% endblock rightcolumn %}
diff --git a/apps/catalogue/templates/catalogue/upload_pdf.html b/apps/catalogue/templates/catalogue/upload_pdf.html
deleted file mode 100755 (executable)
index 265b84a..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-{% extends "catalogue/base.html" %}
-{% load i18n %}
-
-
-{% block titleextra %}{% trans "PDF file upload" %}{% endblock %}
-
-
-{% block content %}
-
-
-<h2>{% trans "PDF file upload" %}</h2>
-
-<form enctype="multipart/form-data" method="POST" action="">
-{% csrf_token %}
-{{ form.as_p }}
-<p><button type="submit">{% trans "Upload" %}</button></p>
-</form>
-
-
-{% endblock content %}
diff --git a/apps/catalogue/templates/catalogue/user_list.html b/apps/catalogue/templates/catalogue/user_list.html
deleted file mode 100755 (executable)
index 460a38b..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-{% extends "catalogue/base.html" %}
-
-{% load i18n %}
-{% load username from common_tags %}
-
-
-{% block titleextra %}{% trans "Users" %}{% endblock %}
-
-
-{% block leftcolumn %}
-
-<h1>{% trans "Users" %}</h1>
-
-<ul>
-{% for user in users %}
-    <li><a href="{% url 'catalogue_user' user.username %}">
-        <span class="chunkno">{{ forloop.counter }}.</span>
-        {{ user|username }}</a>
-        ({{ user.count }})</li>
-{% endfor %}
-</ul>
-
-{% endblock leftcolumn %}
diff --git a/apps/catalogue/templates/catalogue/user_page.html b/apps/catalogue/templates/catalogue/user_page.html
deleted file mode 100755 (executable)
index 4be4ca3..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-{% extends "catalogue/base.html" %}
-
-{% load i18n %}
-{% load catalogue book_list wall %}
-
-
-{% block titleextra %}{{ viewed_user|nice_name }}{% endblock %}
-
-
-{% block leftcolumn %}
-    <h1>{{ viewed_user|nice_name }}</h1>
-    {% book_list viewed_user %}
-{% endblock leftcolumn %}
-
-{% block rightcolumn %}
-    <h2>{% trans "Recent activity for" %} {{ viewed_user|nice_name }}</h2>
-    {% wall viewed_user 10 %}
-{% endblock rightcolumn %}
diff --git a/apps/catalogue/templates/catalogue/wall.html b/apps/catalogue/templates/catalogue/wall.html
deleted file mode 100755 (executable)
index a107dfa..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-{% load i18n %}
-{% load gravatar %}
-{% load email %}
-{% load username from common_tags %}
-
-<ul class='wall'>
-{% for item in wall %}
-    <li class="{{ item.tag }}{% if not item.user %} anonymous{% endif %}">
-        <div class='gravatar'>
-            {% if item.get_email %}
-                <img src="{% gravatar_url item.get_email 32 %}"
-                    height="32" width="32" alt='Avatar' />
-                <br/>
-            {% endif %}
-        </div>
-
-        <div class="time">{{ item.timestamp }}</div>
-        <h3>{{ item.header }}</h3>
-        <a target="_blank" href='{{ item.url }}'>{{ item.title }}</a>
-        <br/><strong>{% trans "user" %}:</strong>
-        {% if item.user %}
-            <a href="{% url 'catalogue_user' item.user.username %}">
-            {{ item.user|username }}</a>
-            &lt;{{ item.user.email|email_link }}>
-        {% else %}
-            {{ item.user_name }}
-            {% if item.email %}
-                &lt;{{ item.email|email_link }}>
-            {% endif %}
-            ({% trans "not logged in" %})
-        {% endif %}
-        <br/>{{ item.summary|linebreaksbr }}
-    </li>
-{% empty %}
-    <li>{% trans "No activity recorded." %}</li>
-{% endfor %}
-</ul>
diff --git a/apps/catalogue/templatetags/__init__.py b/apps/catalogue/templatetags/__init__.py
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/apps/catalogue/templatetags/book_list.py b/apps/catalogue/templatetags/book_list.py
deleted file mode 100755 (executable)
index 9ac996b..0000000
+++ /dev/null
@@ -1,209 +0,0 @@
-from __future__ import absolute_import
-
-from re import split
-from django.db.models import Q, Count
-from django import template
-from django.utils.translation import ugettext_lazy as _
-from django.contrib.auth.models import User
-from catalogue.models import Chunk, Image, Project
-
-register = template.Library()
-
-
-class ChunksList(object):
-    def __init__(self, chunk_qs):
-        #self.chunk_qs = chunk_qs#.annotate(
-            #book_length=Count('book__chunk')).select_related(
-            #'book')#, 'stage__name',
-            #'user')
-        self.chunk_qs = chunk_qs.select_related('book__hidden')
-
-        self.book_qs = chunk_qs.values('book_id')
-
-    def __getitem__(self, key):
-        if isinstance(key, slice):
-            return self.get_slice(key)
-        elif isinstance(key, int):
-            return self.get_slice(slice(key, key+1))[0]
-        else:
-            raise TypeError('Unsupported list index. Must be a slice or an int.')
-
-    def __len__(self):
-        return self.book_qs.count()
-
-    def get_slice(self, slice_):
-        book_ids = [x['book_id'] for x in self.book_qs[slice_]]
-        chunk_qs = self.chunk_qs.filter(book__in=book_ids)
-
-        chunks_list = []
-        book = None
-        for chunk in chunk_qs:
-            if chunk.book != book:
-                book = chunk.book
-                chunks_list.append(ChoiceChunks(book, [chunk]))
-            else:
-                chunks_list[-1].chunks.append(chunk)
-        return chunks_list
-
-
-class ChoiceChunks(object):
-    """
-        Associates the given chunks iterable for a book.
-    """
-
-    chunks = None
-
-    def __init__(self, book, chunks):
-        self.book = book
-        self.chunks = chunks
-
-
-def foreign_filter(qs, value, filter_field, model, model_field='slug', unset='-'):
-    if value == unset:
-        return qs.filter(**{filter_field: None})
-    if not value:
-        return qs
-    try:
-        obj = model._default_manager.get(**{model_field: value})
-    except model.DoesNotExist:
-        return qs.none()
-    else:
-        return qs.filter(**{filter_field: obj})
-
-
-def search_filter(qs, value, filter_fields):
-    if not value:
-        return qs
-    q = Q(**{"%s__icontains" % filter_fields[0]: value})
-    for field in filter_fields[1:]:
-        q |= Q(**{"%s__icontains" % field: value})
-    return qs.filter(q)
-
-
-_states = [
-        ('publishable', _('publishable'), Q(book___new_publishable=True)),
-        ('changed', _('changed'), Q(_changed=True)),
-        ('published', _('published'), Q(book___published=True)),
-        ('unpublished', _('unpublished'), Q(book___published=False)),
-        ('empty', _('empty'), Q(head=None)),
-    ]
-_states_options = [s[:2] for s in _states]
-_states_dict = dict([(s[0], s[2]) for s in _states])
-
-
-def document_list_filter(request, **kwargs):
-
-    def arg_or_GET(field):
-        return kwargs.get(field, request.GET.get(field))
-
-    if arg_or_GET('all'):
-        chunks = Chunk.objects.all()
-    else:
-        chunks = Chunk.visible_objects.all()
-
-    chunks = chunks.order_by('book__title', 'book', 'number')
-
-    if not request.user.is_authenticated():
-        chunks = chunks.filter(book__public=True)
-
-    state = arg_or_GET('status')
-    if state in _states_dict:
-        chunks = chunks.filter(_states_dict[state])
-
-    chunks = foreign_filter(chunks, arg_or_GET('user'), 'user', User, 'username')
-    chunks = foreign_filter(chunks, arg_or_GET('stage'), 'stage', Chunk.tag_model, 'slug')
-    chunks = search_filter(chunks, arg_or_GET('title'), ['book__title', 'title'])
-    chunks = foreign_filter(chunks, arg_or_GET('project'), 'book__project', Project, 'pk')
-    return chunks
-
-
-@register.inclusion_tag('catalogue/book_list/book_list.html', takes_context=True)
-def book_list(context, user=None):
-    request = context['request']
-
-    if user:
-        filters = {"user": user}
-        new_context = {"viewed_user": user}
-    else:
-        filters = {}
-        new_context = {
-            "users": User.objects.annotate(
-                count=Count('chunk')).filter(count__gt=0).order_by(
-                '-count', 'last_name', 'first_name'),
-            "other_users": User.objects.annotate(
-                count=Count('chunk')).filter(count=0).order_by(
-                'last_name', 'first_name'),
-                }
-
-    new_context.update({
-        "filters": True,
-        "request": request,
-        "books": ChunksList(document_list_filter(request, **filters)),
-        "stages": Chunk.tag_model.objects.all(),
-        "states": _states_options,
-        "projects": Project.objects.all(),
-    })
-
-    return new_context
-
-
-
-_image_states = [
-        ('publishable', _('publishable'), Q(_new_publishable=True)),
-        ('changed', _('changed'), Q(_changed=True)),
-        ('published', _('published'), Q(_published=True)),
-        ('unpublished', _('unpublished'), Q(_published=False)),
-        ('empty', _('empty'), Q(head=None)),
-    ]
-_image_states_options = [s[:2] for s in _image_states]
-_image_states_dict = dict([(s[0], s[2]) for s in _image_states])
-
-def image_list_filter(request, **kwargs):
-
-    def arg_or_GET(field):
-        return kwargs.get(field, request.GET.get(field))
-
-    images = Image.objects.all()
-
-    if not request.user.is_authenticated():
-        images = images.filter(public=True)
-
-    state = arg_or_GET('status')
-    if state in _image_states_dict:
-        images = images.filter(_image_states_dict[state])
-
-    images = foreign_filter(images, arg_or_GET('user'), 'user', User, 'username')
-    images = foreign_filter(images, arg_or_GET('stage'), 'stage', Image.tag_model, 'slug')
-    images = search_filter(images, arg_or_GET('title'), ['title', 'title'])
-    images = foreign_filter(images, arg_or_GET('project'), 'project', Project, 'pk')
-    return images
-
-
-@register.inclusion_tag('catalogue/image_table.html', takes_context=True)
-def image_list(context, user=None):
-    request = context['request']
-
-    if user:
-        filters = {"user": user}
-        new_context = {"viewed_user": user}
-    else:
-        filters = {}
-        new_context = {
-            "users": User.objects.annotate(
-                count=Count('image')).filter(count__gt=0).order_by(
-                '-count', 'last_name', 'first_name'),
-            "other_users": User.objects.annotate(
-                count=Count('image')).filter(count=0).order_by(
-                'last_name', 'first_name'),
-                }
-
-    new_context.update({
-        "filters": True,
-        "request": request,
-        "objects": image_list_filter(request, **filters),
-        "stages": Image.tag_model.objects.all(),
-        "states": _image_states_options,
-        "projects": Project.objects.all(),
-    })
-
-    return new_context
diff --git a/apps/catalogue/templatetags/catalogue.py b/apps/catalogue/templatetags/catalogue.py
deleted file mode 100644 (file)
index 07c5cf9..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-from __future__ import absolute_import
-
-from django.core.urlresolvers import reverse
-from django import template
-from django.utils.translation import ugettext as _
-
-register = template.Library()
-
-
-class Tab(object):
-    slug = None
-    caption = None
-    url = None
-
-    def __init__(self, slug, caption, url):
-        self.slug = slug
-        self.caption = caption
-        self.url = url
-
-
-@register.inclusion_tag("catalogue/main_tabs.html", takes_context=True)
-def main_tabs(context):
-    active = getattr(context['request'], 'catalogue_active_tab', None)
-
-    tabs = []
-    user = context['user']
-    tabs.append(Tab('my', _('My page'), reverse("catalogue_user")))
-
-    tabs.append(Tab('activity', _('Activity'), reverse("catalogue_activity")))
-    tabs.append(Tab('all', _('All'), reverse("catalogue_document_list")))
-    tabs.append(Tab('images', _('Images'), reverse("catalogue_image_list")))
-    tabs.append(Tab('users', _('Users'), reverse("catalogue_users")))
-
-    if user.has_perm('catalogue.add_book'):
-        tabs.append(Tab('create', _('Add'), reverse("catalogue_create_missing")))
-        tabs.append(Tab('upload', _('Upload'), reverse("catalogue_upload")))
-
-    tabs.append(Tab('cover', _('Covers'), reverse("cover_image_list")))
-
-    return {"tabs": tabs, "active_tab": active}
-
-
-@register.filter
-def nice_name(user):
-    return user.get_full_name() or user.username
-
diff --git a/apps/catalogue/templatetags/common_tags.py b/apps/catalogue/templatetags/common_tags.py
deleted file mode 100755 (executable)
index 7f5d0e9..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-from django import template
-register = template.Library()
-
-@register.filter
-def username(user):
-    return ("%s %s" % (user.first_name, user.last_name)).lstrip() or user.username
diff --git a/apps/catalogue/templatetags/set_get_parameter.py b/apps/catalogue/templatetags/set_get_parameter.py
deleted file mode 100755 (executable)
index b3d44d7..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-from re import split
-
-from django import template
-
-register = template.Library()
-
-
-"""
-In template:
-    {% set_get_paramater param1='const_value',param2=,param3=variable %}
-results with changes to query string:
-    param1 is set to `const_value' string
-    param2 is unset, if exists,
-    param3 is set to the value of variable in context
-
-Using 'django.core.context_processors.request' is required.
-
-"""
-
-
-class SetGetParameter(template.Node):
-    def __init__(self, values):
-        self.values = values
-        
-    def render(self, context):
-        request = template.Variable('request').resolve(context)
-        params = request.GET.copy()
-        for key, value in self.values.items():
-            if value == '':
-                if key in params:
-                    del(params[key])
-            else:
-                params[key] = template.Variable(value).resolve(context)
-        return '?%s' %  params.urlencode()
-
-
-@register.tag
-def set_get_parameter(parser, token):
-    parts = split(r'\s+', token.contents, 2)
-
-    values = {}
-    for pair in parts[1].split(','):
-        s = pair.split('=')
-        values[s[0]] = s[1]
-
-    return SetGetParameter(values)
diff --git a/apps/catalogue/templatetags/wall.py b/apps/catalogue/templatetags/wall.py
deleted file mode 100755 (executable)
index d000421..0000000
+++ /dev/null
@@ -1,210 +0,0 @@
-from __future__ import absolute_import
-
-from datetime import timedelta
-from django.db.models import Q
-from django.core.urlresolvers import reverse
-from django.contrib.comments.models import Comment
-from django import template
-from django.utils.translation import ugettext as _
-
-from catalogue.models import Chunk, BookPublishRecord, Image, ImagePublishRecord
-
-register = template.Library()
-
-
-class WallItem(object):
-    title = ''
-    summary = ''
-    url = ''
-    timestamp = ''
-    user = None
-    user_name = ''
-    email = ''
-
-    def __init__(self, tag):
-        self.tag = tag
-
-    def get_email(self):
-        if self.user:
-            return self.user.email
-        else:
-            return self.email
-
-
-def changes_wall(user=None, max_len=None, day=None):
-    qs = Chunk.change_model.objects.order_by('-created_at')
-    qs = qs.select_related('author', 'tree', 'tree__book__title')
-    if user is not None:
-        qs = qs.filter(Q(author=user) | Q(tree__user=user))
-    if max_len is not None:
-        qs = qs[:max_len]
-    if day is not None:
-        next_day = day + timedelta(1)
-        qs = qs.filter(created_at__gte=day, created_at__lt=next_day)
-    for item in qs:
-        tag = 'stage' if item.tags.count() else 'change'
-        chunk = item.tree
-        w = WallItem(tag)
-        if user and item.author != user:
-            w.header = _('Related edit')
-        else:
-            w.header = _('Edit')
-        w.title = chunk.pretty_name()
-        w.summary = item.description
-        w.url = reverse('wiki_editor', 
-                args=[chunk.book.slug, chunk.slug]) + '?diff=%d' % item.revision
-        w.timestamp = item.created_at
-        w.user = item.author
-        w.user_name = item.author_name
-        w.email = item.author_email
-        yield w
-
-
-def image_changes_wall(user=None, max_len=None, day=None):
-    qs = Image.change_model.objects.order_by('-created_at')
-    qs = qs.select_related('author', 'tree', 'tree__title')
-    if user is not None:
-        qs = qs.filter(Q(author=user) | Q(tree__user=user))
-    if max_len is not None:
-        qs = qs[:max_len]
-    if day is not None:
-        next_day = day + timedelta(1)
-        qs = qs.filter(created_at__gte=day, created_at__lt=next_day)
-    for item in qs:
-        tag = 'stage' if item.tags.count() else 'change'
-        image = item.tree
-        w  = WallItem(tag)
-        if user and item.author != user:
-            w.header = _('Related edit')
-        else:
-            w.header = _('Edit')
-        w.title = image.title
-        w.summary = item.description
-        w.url = reverse('wiki_img_editor', 
-                args=[image.slug]) + '?diff=%d' % item.revision
-        w.timestamp = item.created_at
-        w.user = item.author
-        w.user_name = item.author_name
-        w.email = item.author_email
-        yield w
-
-
-
-# TODO: marked for publishing
-
-
-def published_wall(user=None, max_len=None, day=None):
-    qs = BookPublishRecord.objects.select_related('book__title')
-    if user:
-        # TODO: published my book
-        qs = qs.filter(Q(user=user))
-    if max_len is not None:
-        qs = qs[:max_len]
-    if day is not None:
-        next_day = day + timedelta(1)
-        qs = qs.filter(timestamp__gte=day, timestamp__lt=next_day)
-    for item in qs:
-        w = WallItem('publish')
-        w.header = _('Publication')
-        w.title = item.book.title
-        w.timestamp = item.timestamp
-        w.url = item.book.get_absolute_url()
-        w.user = item.user
-        w.email = item.user.email
-        yield w
-
-
-def image_published_wall(user=None, max_len=None, day=None):
-    qs = ImagePublishRecord.objects.select_related('image__title')
-    if user:
-        # TODO: published my book
-        qs = qs.filter(Q(user=user))
-    if max_len is not None:
-        qs = qs[:max_len]
-    if day is not None:
-        next_day = day + timedelta(1)
-        qs = qs.filter(timestamp__gte=day, timestamp__lt=next_day)
-    for item in qs:
-        w = WallItem('publish')
-        w.header = _('Publication')
-        w.title = item.image.title
-        w.timestamp = item.timestamp
-        w.url = item.image.get_absolute_url()
-        w.user = item.user
-        w.email = item.user.email
-        yield w
-
-
-def comments_wall(user=None, max_len=None, day=None):
-    qs = Comment.objects.filter(is_public=True).select_related().order_by('-submit_date')
-    if user:
-        # TODO: comments concerning my books
-        qs = qs.filter(Q(user=user))
-    if max_len is not None:
-        qs = qs[:max_len]
-    if day is not None:
-        next_day = day + timedelta(1)
-        qs = qs.filter(submit_date__gte=day, submit_date__lt=next_day)
-    for item in qs:
-        w  = WallItem('comment')
-        w.header = _('Comment')
-        w.title = item.content_object
-        w.summary = item.comment
-        w.url = item.content_object.get_absolute_url()
-        w.timestamp = item.submit_date
-        w.user = item.user
-        ui = item.userinfo
-        w.email = item.email
-        w.user_name = item.name
-        yield w
-
-
-def big_wall(walls, max_len=None):
-    """
-        Takes some WallItem iterators and zips them into one big wall.
-        Input iterators must already be sorted by timestamp.
-    """
-    subwalls = []
-    for w in walls:
-        try:
-            subwalls.append([next(w), w])
-        except StopIteration:
-            pass
-
-    if max_len is None:
-        max_len = -1
-    while max_len and subwalls:
-        i, next_item = max(enumerate(subwalls), key=lambda x: x[1][0].timestamp)
-        yield next_item[0]
-        max_len -= 1
-        try:
-            next_item[0] = next(next_item[1])
-        except StopIteration:
-            del subwalls[i]
-
-
-@register.inclusion_tag("catalogue/wall.html", takes_context=True)
-def wall(context, user=None, max_len=100):
-    return {
-        "request": context['request'],
-        "STATIC_URL": context['STATIC_URL'],
-        "wall": big_wall([
-            changes_wall(user, max_len),
-            published_wall(user, max_len),
-            image_changes_wall(user, max_len),
-            image_published_wall(user, max_len),
-            comments_wall(user, max_len),
-        ], max_len)}
-
-@register.inclusion_tag("catalogue/wall.html", takes_context=True)
-def day_wall(context, day):
-    return {
-        "request": context['request'],
-        "STATIC_URL": context['STATIC_URL'],
-        "wall": big_wall([
-            changes_wall(day=day),
-            published_wall(day=day),
-            image_changes_wall(day=day),
-            image_published_wall(day=day),
-            comments_wall(day=day),
-        ])}
diff --git a/apps/catalogue/test_utils.py b/apps/catalogue/test_utils.py
deleted file mode 100644 (file)
index 2b08545..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
-# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
-#
-"""Testing utilities."""
-
-from os.path import abspath, dirname, join
-
-
-def get_fixture(path):
-    f_path = join(dirname(abspath(__file__)), 'tests/files', path)
-    with open(f_path) as f:
-        return unicode(f.read(), 'utf-8')
diff --git a/apps/catalogue/tests/__init__.py b/apps/catalogue/tests/__init__.py
deleted file mode 100644 (file)
index 533a6c5..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
-# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
-#
-from catalogue.tests.book import *
-from catalogue.tests.gallery import *
-from catalogue.tests.publish import *
-from catalogue.tests.xml_updater import *
diff --git a/apps/catalogue/tests/book.py b/apps/catalogue/tests/book.py
deleted file mode 100644 (file)
index df6f3b4..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
-# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
-#
-"""Tests for manipulating books in the catalogue."""
-
-from django.test import TestCase
-from django.contrib.auth.models import User
-from catalogue.models import Book
-
-
-class ManipulationTests(TestCase):
-
-    def setUp(self):
-        self.user = User.objects.create(username='tester')
-        self.book1 = Book.create(self.user, 'book 1', slug='book1')
-        self.book2 = Book.create(self.user, 'book 2', slug='book2')
-
-    def test_append(self):
-        self.book1.append(self.book2)
-        self.assertEqual(Book.objects.all().count(), 1)
-        self.assertEqual(len(self.book1), 2)
-
-    def test_append_to_self(self):
-        with self.assertRaises(AssertionError):
-            self.book1.append(Book.objects.get(pk=self.book1.pk))
-        self.assertEqual(Book.objects.all().count(), 2)
-        self.assertEqual(len(self.book1), 1)
-
-    def test_prepend_history(self):
-        self.book1.prepend_history(self.book2)
-        self.assertEqual(Book.objects.all().count(), 1)
-        self.assertEqual(len(self.book1), 1)
-        self.assertEqual(self.book1.materialize(), 'book 1')
-
-    def test_prepend_history_to_self(self):
-        with self.assertRaises(AssertionError):
-            self.book1.prepend_history(self.book1)
-        self.assertEqual(Book.objects.all().count(), 2)
-        self.assertEqual(self.book1.materialize(), 'book 1')
-        self.assertEqual(self.book2.materialize(), 'book 2')
-
-    def test_split_book(self):
-        self.book1.chunk_set.create(number=2, title='Second chunk',
-                slug='book3')
-        self.book1[1].commit('I survived!')
-        self.assertEqual(len(self.book1), 2)
-        self.book1.split()
-        self.assertEqual(set([b.slug for b in Book.objects.all()]),
-                set(['book2', '1', 'book3']))
-        self.assertEqual(
-                Book.objects.get(slug='book3').materialize(),
-                'I survived!')
diff --git a/apps/catalogue/tests/files/chunk1.xml b/apps/catalogue/tests/files/chunk1.xml
deleted file mode 100755 (executable)
index 6a75580..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-<utwor>
-  <liryka_l>
-
-<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
-<rdf:Description rdf:about="http://example.com/documents/book/test-book/">
-<dc:creator xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">Mickiewicz, Adam</dc:creator>
-<dc:title xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">Do M***</dc:title>
-<dc:publisher xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">Fundacja Nowoczesna Polska</dc:publisher>
-<dc:subject.period xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">Romantyzm</dc:subject.period>
-<dc:subject.type xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">Liryka</dc:subject.type>
-<dc:subject.genre xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">Wiersz</dc:subject.genre>
-<!--dc:description xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">Publikacja zrealizowana w ramach projektu Wolne Lektury (http://wolnelektury.pl). Reprodukcja cyfrowa wykonana przez Bibliotekę Narodową z egzemplarza pochodzącego ze zbiorów BN.</dc:description-->
-<dc:identifier.url xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">http://wolnelektury.pl/katalog/lektura/sonety-odeskie-do-m</dc:identifier.url>
-<dc:source.URL xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">http://www.polona.pl/Content/2222</dc:source.URL>
-<dc:source xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">Mickiewicz, Adam (1798-1855), Poezje, tom 1 (Wiersze młodzieńcze - Ballady i romanse - Wiersze do r. 1824), Krakowska Spółdzielnia Wydawnicza, wyd. 2 zwiększone, Kraków, 1922</dc:source>
-
-<dc:rights xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">Domena publiczna - Adam Mickiewicz zm. 1855</dc:rights>
-<dc:date.pd xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">1926</dc:date.pd>
-<dc:format xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">xml</dc:format>
-<dc:type xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">text</dc:type>
-<dc:type xml:lang="en" xmlns:dc="http://purl.org/dc/elements/1.1/">text</dc:type>
-<dc:date xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">2007-09-06</dc:date>
-<dc:language xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">pol</dc:language>
-
-</rdf:Description>
-</rdf:RDF>
-
-<autor_utworu>Adam Mickiewicz</autor_utworu>
-<dzielo_nadrzedne>Sonety odeskie</dzielo_nadrzedne>
-<nazwa_utworu>Do M***</nazwa_utworu>
-
-<nota><akap>Wiérsz napisany w roku 1822</akap></nota>
-
-
-<strofa>Precz z moich oczu!... posłucham od razu,/
-Precz z mego serca!... i serce posłucha,/
-Precz z méj pamięci!... Nie! tego rozkazu/
-Moja i twoja pamięć nie posłucha.</strofa>
-
-<!-- TRIM_END -->
-</liryka_l>
-</utwor>
diff --git a/apps/catalogue/tests/files/chunk2.xml b/apps/catalogue/tests/files/chunk2.xml
deleted file mode 100755 (executable)
index 63a243e..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-<utwor><liryka_l>
-<!-- TRIM_BEGIN -->
-
-<strofa>Jak cień tém dłuższy, gdy padnie z daleka,/
-Tém szerzéj koło żałobne roztoczy,/
-Tak moja postać, im daléj ucieka,/
-Tém grubszym kirem twą pamięć pomroczy.</strofa>
-
-
-</liryka_l>
-</utwor>
diff --git a/apps/catalogue/tests/files/expected.xml b/apps/catalogue/tests/files/expected.xml
deleted file mode 100755 (executable)
index ff225a0..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-<utwor>
-  <liryka_l>
-
-<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
-<rdf:Description rdf:about="http://example.com/documents/book/test-book/">
-<dc:creator xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">Mickiewicz, Adam</dc:creator>
-<dc:title xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">Do M***</dc:title>
-<dc:publisher xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">Fundacja Nowoczesna Polska</dc:publisher>
-<dc:subject.period xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">Romantyzm</dc:subject.period>
-<dc:subject.type xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">Liryka</dc:subject.type>
-<dc:subject.genre xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">Wiersz</dc:subject.genre>
-<!--dc:description xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">Publikacja zrealizowana w ramach projektu Wolne Lektury (http://wolnelektury.pl). Reprodukcja cyfrowa wykonana przez Bibliotekę Narodową z egzemplarza pochodzącego ze zbiorów BN.</dc:description-->
-<dc:identifier.url xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">http://wolnelektury.pl/katalog/lektura/sonety-odeskie-do-m</dc:identifier.url>
-<dc:source.URL xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">http://www.polona.pl/Content/2222</dc:source.URL>
-<dc:source xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">Mickiewicz, Adam (1798-1855), Poezje, tom 1 (Wiersze młodzieńcze - Ballady i romanse - Wiersze do r. 1824), Krakowska Spółdzielnia Wydawnicza, wyd. 2 zwiększone, Kraków, 1922</dc:source>
-
-<dc:rights xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">Domena publiczna - Adam Mickiewicz zm. 1855</dc:rights>
-<dc:date.pd xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">1926</dc:date.pd>
-<dc:format xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">xml</dc:format>
-<dc:type xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">text</dc:type>
-<dc:type xml:lang="en" xmlns:dc="http://purl.org/dc/elements/1.1/">text</dc:type>
-<dc:date xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">2007-09-06</dc:date>
-<dc:language xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">pol</dc:language>
-
-</rdf:Description>
-</rdf:RDF>
-
-<autor_utworu>Adam Mickiewicz</autor_utworu>
-<dzielo_nadrzedne>Sonety odeskie</dzielo_nadrzedne>
-<nazwa_utworu>Do M***</nazwa_utworu>
-
-<nota><akap>Wiérsz napisany w roku 1822</akap></nota>
-
-
-<strofa>Precz z moich oczu!... posłucham od razu,/
-Precz z mego serca!... i serce posłucha,/
-Precz z méj pamięci!... Nie! tego rozkazu/
-Moja i twoja pamięć nie posłucha.</strofa>
-
-
-
-<strofa>Jak cień tém dłuższy, gdy padnie z daleka,/
-Tém szerzéj koło żałobne roztoczy,/
-Tak moja postać, im daléj ucieka,/
-Tém grubszym kirem twą pamięć pomroczy.</strofa>
-
-
-</liryka_l>
-</utwor>
diff --git a/apps/catalogue/tests/gallery.py b/apps/catalogue/tests/gallery.py
deleted file mode 100644 (file)
index 4b8ea3f..0000000
+++ /dev/null
@@ -1,148 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
-# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
-#
-"""Tests for galleries of scans."""
-
-from os.path import join, basename, exists
-from os import makedirs, listdir
-from django.test import TestCase
-from django.contrib.auth.models import User
-from catalogue.models import Book
-from tempfile import mkdtemp
-from django.conf import settings
-
-
-class GalleryAppendTests(TestCase):
-    def setUp(self):
-        self.user = User.objects.create(username='tester')
-        self.book1 = Book.create(self.user, 'book 1', slug='book1')
-        self.book1.chunk_set.create(number=2, title='Second chunk',
-                slug='book1-2')
-        c=self.book1[1]
-        c.gallery_start=3
-        
-        self.scandir = join(settings.MEDIA_ROOT, settings.IMAGE_DIR)
-        if not exists(self.scandir):
-            makedirs(self.scandir)
-
-    def make_gallery(self, book, files):
-        d = mkdtemp('gallery', dir=self.scandir)
-        for named, cont in files.items():
-            f = open(join(d, named), 'w')
-            f.write(cont)
-            f.close()
-        book.gallery = basename(d)
-
-
-    def test_both_indexed(self):
-        self.book2 = Book.create(self.user, 'book 2', slug='book2')
-        self.book2.chunk_set.create(number=2, title='Second chunk of second book',
-                slug='book2-2')
-
-        c = self.book2[1]
-        c.gallery_start = 3
-        c.save()
-        
-        print "gallery starts:",self.book2[0].gallery_start, self.book2[1].gallery_start
-
-        self.make_gallery(self.book1, {
-            '1-0001_1l' : 'aa',
-            '1-0001_2r' : 'bb',
-            '1-0002_1l' : 'cc',
-            '1-0002_2r' : 'dd',
-            })
-
-        self.make_gallery(self.book2, {
-            '1-0001_1l' : 'dd', # the same, should not be moved
-            '1-0001_2r' : 'ff',
-            '2-0002_1l' : 'gg',
-            '2-0002_2r' : 'hh',
-            })
-
-        self.book1.append(self.book2)
-
-        files = listdir(join(self.scandir, self.book1.gallery))
-        files.sort()
-        print files
-        self.assertEqual(files, [
-            '1-0001_1l',
-            '1-0001_2r',
-            '1-0002_1l',
-            '1-0002_2r',
-            #            '2-0001_1l',
-            '2-0001_2r',
-            '3-0002_1l',
-            '3-0002_2r',
-            ])        
-
-        self.assertEqual((4, 6), (self.book1[2].gallery_start, self.book1[3].gallery_start))
-        
-        
-    def test_none_indexed(self):
-        self.book2 = Book.create(self.user, 'book 2', slug='book2')
-        self.make_gallery(self.book1, {
-            '0001_1l' : 'aa',
-            '0001_2r' : 'bb',
-            '0002_1l' : 'cc',
-            '0002_2r' : 'dd',
-            })
-
-        self.make_gallery(self.book2, {
-            '0001_1l' : 'ee',
-            '0001_2r' : 'ff',
-            '0002_1l' : 'gg',
-            '0002_2r' : 'hh',
-            })
-
-        self.book1.append(self.book2)
-
-        files = listdir(join(self.scandir, self.book1.gallery))
-        files.sort()
-        print files
-        self.assertEqual(files, [
-            '0-0001_1l',
-            '0-0001_2r',
-            '0-0002_1l',
-            '0-0002_2r',
-            '1-0001_1l',
-            '1-0001_2r',
-            '1-0002_1l',
-            '1-0002_2r',
-            ])        
-
-
-    def test_none_indexed(self):
-        import nose.tools
-        self.book2 = Book.create(self.user, 'book 2', slug='book2')
-        self.make_gallery(self.book1, {
-            '1-0001_1l' : 'aa',
-            '1-0001_2r' : 'bb',
-            '1002_1l' : 'cc',
-            '1002_2r' : 'dd',
-            })
-
-        self.make_gallery(self.book2, {
-            '0001_1l' : 'ee',
-            '0001_2r' : 'ff',
-            '0002_1l' : 'gg',
-            '0002_2r' : 'hh',
-            })
-
-        self.book1.append(self.book2)
-
-        files = listdir(join(self.scandir, self.book1.gallery))
-        files.sort()
-        print files
-        self.assertEqual(files, [
-            '0-1-0001_1l',
-            '0-1-0001_2r',
-            '0-1002_1l',
-            '0-1002_2r',
-            '1-0001_1l',
-            '1-0001_2r',
-            '1-0002_1l',
-            '1-0002_2r',
-            ])        
-
diff --git a/apps/catalogue/tests/publish.py b/apps/catalogue/tests/publish.py
deleted file mode 100644 (file)
index 93e02da..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
-# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
-#
-"""Tests for the publishing process."""
-
-from catalogue.test_utils import get_fixture
-
-from mock import patch
-from django.test import TestCase
-from django.contrib.auth.models import User
-from catalogue.models import Book
-
-
-class PublishTests(TestCase):
-    def setUp(self):
-        self.user = User.objects.create(username='tester')
-        self.text1 = get_fixture('chunk1.xml')
-        self.book = Book.create(self.user, self.text1, slug='test-book')
-
-    @patch('apiclient.api_call')
-    def test_unpublishable(self, api_call):
-        with self.assertRaises(AssertionError):
-            self.book.publish(self.user)
-
-    @patch('apiclient.api_call')
-    def test_publish(self, api_call):
-        self.book[0].head.set_publishable(True)
-        self.book.publish(self.user)
-        api_call.assert_called_with(self.user, 'books/', {"book_xml": self.text1, "days": 0}, beta=False)
-
-    @patch('apiclient.api_call')
-    def test_publish_multiple(self, api_call):
-        self.book[0].head.set_publishable(True)
-        self.book[0].split(slug='part-2')
-        self.book[1].commit(get_fixture('chunk2.xml'))
-        self.book[1].head.set_publishable(True)
-        self.book.publish(self.user)
-        api_call.assert_called_with(self.user, 'books/', {"book_xml": get_fixture('expected.xml'), "days": 0}, beta=False)
diff --git a/apps/catalogue/tests/xml_updater.py b/apps/catalogue/tests/xml_updater.py
deleted file mode 100644 (file)
index 9fb5a4a..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
-# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
-#
-"""XmlUpdater tests."""
-
-from catalogue.test_utils import get_fixture
-from django.test import TestCase
-from django.contrib.auth.models import User
-from catalogue.models import Book
-from catalogue.management import XmlUpdater
-from librarian import DCNS
-
-
-class XmlUpdaterTests(TestCase):
-    class SimpleUpdater(XmlUpdater):
-        @XmlUpdater.fixes_elements('.//' + DCNS('title'))
-        def fix_title(element, **kwargs):
-            element.text = element.text + " fixed"
-            return True
-
-    def setUp(self):
-        self.user = User.objects.create(username='tester')
-        text = get_fixture('chunk1.xml')
-        Book.create(self.user, text, slug='test-book')
-        self.title = "Do M***"
-
-    def test_xml_updater(self):
-        self.SimpleUpdater().run(self.user)
-        self.assertEqual(
-            Book.objects.get(slug='test-book').wldocument(
-                publishable=False).book_info.title,
-            self.title + " fixed"
-            )
diff --git a/apps/catalogue/urls.py b/apps/catalogue/urls.py
deleted file mode 100644 (file)
index e81c0a3..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-# -*- coding: utf-8
-from django.conf.urls import patterns, url
-from django.contrib.auth.decorators import permission_required
-from django.views.generic import RedirectView
-from catalogue.feeds import PublishTrackFeed
-from catalogue.views import GalleryView
-
-
-urlpatterns = patterns('catalogue.views',
-    url(r'^$', RedirectView.as_view(url='catalogue/')),
-
-    url(r'^images/$', 'image_list', name='catalogue_image_list'),
-    url(r'^image/(?P<slug>[^/]+)/$', 'image', name="catalogue_image"),
-    url(r'^image/(?P<slug>[^/]+)/publish$', 'publish_image',
-            name="catalogue_publish_image"),
-
-    url(r'^catalogue/$', 'document_list', name='catalogue_document_list'),
-    url(r'^user/$', 'my', name='catalogue_user'),
-    url(r'^user/(?P<username>[^/]+)/$', 'user', name='catalogue_user'),
-    url(r'^users/$', 'users', name='catalogue_users'),
-    url(r'^activity/$', 'activity', name='catalogue_activity'),
-    url(r'^activity/(?P<isodate>\d{4}-\d{2}-\d{2})/$', 
-        'activity', name='catalogue_activity'),
-
-    url(r'^upload/$',
-        'upload', name='catalogue_upload'),
-
-    url(r'^create/(?P<slug>[^/]*)/',
-        'create_missing', name='catalogue_create_missing'),
-    url(r'^create/',
-        'create_missing', name='catalogue_create_missing'),
-
-    url(r'^book/(?P<slug>[^/]+)/publish$', 'publish', name="catalogue_publish"),
-
-    url(r'^book/(?P<slug>[^/]+)/$', 'book', name="catalogue_book"),
-    url(r'^book/(?P<slug>[^/]+)/gallery/$',
-            permission_required('catalogue.change_book')(GalleryView.as_view()),
-            name="catalogue_book_gallery"),
-    url(r'^book/(?P<slug>[^/]+)/xml$', 'book_xml', name="catalogue_book_xml"),
-    url(r'^book/dc/(?P<slug>[^/]+)/xml$', 'book_xml_dc', name="catalogue_book_xml_dc"),
-    url(r'^book/(?P<slug>[^/]+)/txt$', 'book_txt', name="catalogue_book_txt"),
-    url(r'^book/(?P<slug>[^/]+)/html$', 'book_html', name="catalogue_book_html"),
-    url(r'^book/(?P<slug>[^/]+)/epub$', 'book_epub', name="catalogue_book_epub"),
-    url(r'^book/(?P<slug>[^/]+)/mobi$', 'book_mobi', name="catalogue_book_mobi"),
-    url(r'^book/(?P<slug>[^/]+)/pdf$', 'book_pdf', name="catalogue_book_pdf"),
-    url(r'^book/(?P<slug>[^/]+)/pdf-mobile$', 'book_pdf', kwargs={'mobile': True}, name="catalogue_book_pdf_mobile"),
-
-    url(r'^chunk_add/(?P<slug>[^/]+)/(?P<chunk>[^/]+)/$',
-        'chunk_add', name="catalogue_chunk_add"),
-    url(r'^chunk_edit/(?P<slug>[^/]+)/(?P<chunk>[^/]+)/$',
-        'chunk_edit', name="catalogue_chunk_edit"),
-    url(r'^book_append/(?P<slug>[^/]+)/$',
-        'book_append', name="catalogue_book_append"),
-    url(r'^chunk_mass_edit',
-        'chunk_mass_edit', name='catalogue_chunk_mass_edit'),
-    url(r'^image_mass_edit',
-        'image_mass_edit', name='catalogue_image_mass_edit'),
-
-    url(r'^track/(?P<slug>[^/]*)/$', PublishTrackFeed()),
-    url(r'^active/$', 'active_users_list', name='active_users_list'),
-
-    url(r'^mark-final/$', 'mark_final', name='mark_final'),
-    url(r'^mark-final-completed/$', 'mark_final_completed', name='mark_final_completed'),
-)
diff --git a/apps/catalogue/views.py b/apps/catalogue/views.py
deleted file mode 100644 (file)
index f620805..0000000
+++ /dev/null
@@ -1,672 +0,0 @@
-# -*- coding: utf-8 -*-
-from collections import defaultdict
-from datetime import datetime, date, timedelta
-import logging
-import os
-from StringIO import StringIO
-from urllib import unquote
-from urlparse import urlsplit, urlunsplit
-
-from django.conf import settings
-from django.contrib import auth
-from django.contrib.auth.models import User
-from django.contrib.auth.decorators import login_required, permission_required
-from django.core.urlresolvers import reverse
-from django.db.models import Count, Q
-from django.db import transaction
-from django import http
-from django.http import Http404, HttpResponse, HttpResponseForbidden
-from django.http.response import HttpResponseRedirect
-from django.shortcuts import get_object_or_404, render
-from django.utils.encoding import iri_to_uri
-from django.utils.http import urlquote_plus
-from django.utils.translation import ugettext_lazy as _
-from django.views.decorators.http import require_POST
-from django_cas.decorators import user_passes_test
-
-from apiclient import NotAuthorizedError
-from catalogue import forms
-from catalogue import helpers
-from catalogue.helpers import active_tab
-from catalogue.models import (Book, Chunk, Image, BookPublishRecord, 
-        ChunkPublishRecord, ImagePublishRecord, Project)
-from fileupload.views import UploadView
-
-#
-# Quick hack around caching problems, TODO: use ETags
-#
-from django.views.decorators.cache import never_cache
-
-logger = logging.getLogger("fnp.catalogue")
-
-
-@active_tab('all')
-@never_cache
-def document_list(request):
-    return render(request, 'catalogue/document_list.html')
-
-
-@active_tab('images')
-@never_cache
-def image_list(request, user=None):
-    return render(request, 'catalogue/image_list.html')
-
-
-@never_cache
-def user(request, username):
-    user = get_object_or_404(User, username=username)
-    return render(request, 'catalogue/user_page.html', {"viewed_user": user})
-
-
-@login_required
-@active_tab('my')
-@never_cache
-def my(request):
-    last_books = sorted(request.session.get("wiki_last_books", {}).items(),
-        key=lambda x: x[1]['time'], reverse=True)
-    for k, v in last_books:
-        v['time'] = datetime.fromtimestamp(v['time'])
-    return render(request, 'catalogue/my_page.html', {
-        'last_books': last_books,
-        "logout_to": '/',
-        })
-
-
-@active_tab('users')
-def users(request):
-    return render(request, 'catalogue/user_list.html', {
-        'users': User.objects.all().annotate(count=Count('chunk')).order_by(
-            '-count', 'last_name', 'first_name'),
-    })
-
-
-@active_tab('activity')
-def activity(request, isodate=None):
-    today = date.today()
-    try:
-        day = helpers.parse_isodate(isodate)
-    except ValueError:
-        day = today
-
-    if day > today:
-        raise Http404
-    if day != today:
-        next_day = day + timedelta(1)
-    prev_day = day - timedelta(1)
-
-    return render(request, 'catalogue/activity.html', locals())
-
-
-@never_cache
-def logout_then_redirect(request):
-    auth.logout(request)
-    return http.HttpResponseRedirect(urlquote_plus(request.GET.get('next', '/'), safe='/?='))
-
-
-@permission_required('catalogue.add_book')
-@active_tab('create')
-def create_missing(request, slug=None):
-    if slug is None:
-        slug = ''
-    slug = slug.replace(' ', '-')
-
-    if request.method == "POST":
-        form = forms.DocumentCreateForm(request.POST, request.FILES)
-        if form.is_valid():
-            
-            if request.user.is_authenticated():
-                creator = request.user
-            else:
-                creator = None
-            book = Book.create(
-                text=form.cleaned_data['text'],
-                creator=creator,
-                slug=form.cleaned_data['slug'],
-                title=form.cleaned_data['title'],
-                gallery=form.cleaned_data['gallery'],
-            )
-
-            return http.HttpResponseRedirect(reverse("catalogue_book", args=[book.slug]))
-    else:
-        form = forms.DocumentCreateForm(initial={
-                "slug": slug,
-                "title": slug.replace('-', ' ').title(),
-                "gallery": slug,
-        })
-
-    return render(request, "catalogue/document_create_missing.html", {
-        "slug": slug,
-        "form": form,
-
-        "logout_to": '/',
-    })
-
-
-@permission_required('catalogue.add_book')
-@active_tab('upload')
-def upload(request):
-    if request.method == "POST":
-        form = forms.DocumentsUploadForm(request.POST, request.FILES)
-        if form.is_valid():
-            from slugify import slugify
-
-            if request.user.is_authenticated():
-                creator = request.user
-            else:
-                creator = None
-
-            zip = form.cleaned_data['zip']
-            skipped_list = []
-            ok_list = []
-            error_list = []
-            slugs = {}
-            existing = [book.slug for book in Book.objects.all()]
-            for filename in zip.namelist():
-                if filename[-1] == '/':
-                    continue
-                title = os.path.basename(filename)[:-4]
-                slug = slugify(title)
-                if not (slug and filename.endswith('.xml')):
-                    skipped_list.append(filename)
-                elif slug in slugs:
-                    error_list.append((filename, slug, _('Slug already used for %s' % slugs[slug])))
-                elif slug in existing:
-                    error_list.append((filename, slug, _('Slug already used in repository.')))
-                else:
-                    try:
-                        zip.read(filename).decode('utf-8') # test read
-                        ok_list.append((filename, slug, title))
-                    except UnicodeDecodeError:
-                        error_list.append((filename, title, _('File should be UTF-8 encoded.')))
-                    slugs[slug] = filename
-
-            if not error_list:
-                for filename, slug, title in ok_list:
-                    book = Book.create(
-                        text=zip.read(filename).decode('utf-8'),
-                        creator=creator,
-                        slug=slug,
-                        title=title,
-                    )
-
-            return render(request, "catalogue/document_upload.html", {
-                "form": form,
-                "ok_list": ok_list,
-                "skipped_list": skipped_list,
-                "error_list": error_list,
-
-                "logout_to": '/',
-            })
-    else:
-        form = forms.DocumentsUploadForm()
-
-    return render(request, "catalogue/document_upload.html", {
-        "form": form,
-
-        "logout_to": '/',
-    })
-
-
-def serve_xml(request, book, slug):
-    if not book.accessible(request):
-        return HttpResponseForbidden("Not authorized.")
-    xml = book.materialize(publishable=True)
-    response = http.HttpResponse(xml, content_type='application/xml')
-    response['Content-Disposition'] = 'attachment; filename=%s.xml' % slug
-    return response
-
-
-@never_cache
-def book_xml(request, slug):
-    book = get_object_or_404(Book, slug=slug)
-    return serve_xml(request, book, slug)
-
-
-@never_cache
-def book_xml_dc(request, slug):
-    book = get_object_or_404(Book, dc_slug=slug)
-    return serve_xml(request, book, slug)
-
-
-@never_cache
-def book_txt(request, slug):
-    book = get_object_or_404(Book, slug=slug)
-    if not book.accessible(request):
-        return HttpResponseForbidden("Not authorized.")
-
-    doc = book.wldocument()
-    text = doc.as_text().get_bytes()
-    response = http.HttpResponse(text, content_type='text/plain')
-    response['Content-Disposition'] = 'attachment; filename=%s.txt' % slug
-    return response
-
-
-@never_cache
-def book_html(request, slug):
-    book = get_object_or_404(Book, slug=slug)
-    if not book.accessible(request):
-        return HttpResponseForbidden("Not authorized.")
-
-    doc = book.wldocument(parse_dublincore=False)
-    html = doc.as_html(options={'gallery': "'%s'" % book.gallery_url()})
-
-    html = html.get_bytes() if html is not None else ''
-    # response = http.HttpResponse(html, content_type='text/html')
-    # return response
-    # book_themes = {}
-    # for fragment in book.fragments.all().iterator():
-    #     for theme in fragment.tags.filter(category='theme').iterator():
-    #         book_themes.setdefault(theme, []).append(fragment)
-
-    # book_themes = book_themes.items()
-    # book_themes.sort(key=lambda s: s[0].sort_key)
-    return render(request, 'catalogue/book_text.html', locals())
-
-
-@never_cache
-def book_pdf(request, slug, mobile=False):
-    book = get_object_or_404(Book, slug=slug)
-    if not book.accessible(request):
-        return HttpResponseForbidden("Not authorized.")
-
-    # TODO: move to celery
-    doc = book.wldocument()
-    # TODO: error handling
-    customizations = ['26pt', 'nothemes', 'nomargins', 'notoc'] if mobile else None
-    pdf_file = doc.as_pdf(cover=True, ilustr_path=book.gallery_path(), customizations=customizations)
-    from catalogue.ebook_utils import serve_file
-    return serve_file(pdf_file.get_filename(),
-                book.slug + '.pdf', 'application/pdf')
-
-
-@never_cache
-def book_epub(request, slug):
-    book = get_object_or_404(Book, slug=slug)
-    if not book.accessible(request):
-        return HttpResponseForbidden("Not authorized.")
-
-    # TODO: move to celery
-    doc = book.wldocument()
-    # TODO: error handling
-    epub = doc.as_epub(ilustr_path=book.gallery_path()).get_bytes()
-    response = HttpResponse(content_type='application/epub+zip')
-    response['Content-Disposition'] = 'attachment; filename=%s' % book.slug + '.epub'
-    response.write(epub)
-    return response
-
-
-@never_cache
-def book_mobi(request, slug):
-    book = get_object_or_404(Book, slug=slug)
-    if not book.accessible(request):
-        return HttpResponseForbidden("Not authorized.")
-
-    # TODO: move to celery
-    doc = book.wldocument()
-    # TODO: error handling
-    mobi = doc.as_mobi(ilustr_path=book.gallery_path()).get_bytes()
-    response = HttpResponse(content_type='application/x-mobipocket-ebook')
-    response['Content-Disposition'] = 'attachment; filename=%s' % book.slug + '.mobi'
-    response.write(mobi)
-    return response
-
-
-@never_cache
-def revision(request, slug, chunk=None):
-    try:
-        doc = Chunk.get(slug, chunk)
-    except (Chunk.MultipleObjectsReturned, Chunk.DoesNotExist):
-        raise Http404
-    if not doc.book.accessible(request):
-        return HttpResponseForbidden("Not authorized.")
-    return http.HttpResponse(str(doc.revision()))
-
-
-def book(request, slug):
-    book = get_object_or_404(Book, slug=slug)
-    if not book.accessible(request):
-        return HttpResponseForbidden("Not authorized.")
-
-    if request.user.has_perm('catalogue.change_book'):
-        if request.method == "POST":
-            form = forms.BookForm(request.POST, instance=book)
-            if form.is_valid():
-                form.save()
-                return http.HttpResponseRedirect(book.get_absolute_url())
-        else:
-            form = forms.BookForm(instance=book)
-        publish_options_form = forms.PublishOptionsForm()
-        editable = True
-    else:
-        form = forms.ReadonlyBookForm(instance=book)
-        publish_options_form = forms.PublishOptionsForm()
-        editable = False
-
-    publish_error = book.publishable_error()
-    publishable = publish_error is None
-
-    return render(request, "catalogue/book_detail.html", {
-        "book": book,
-        "publishable": publishable,
-        "publishable_error": publish_error,
-        "form": form,
-        "publish_options_form": publish_options_form,
-        "editable": editable,
-    })
-
-
-def image(request, slug):
-    image = get_object_or_404(Image, slug=slug)
-    if not image.accessible(request):
-        return HttpResponseForbidden("Not authorized.")
-
-    if request.user.has_perm('catalogue.change_image'):
-        if request.method == "POST":
-            form = forms.ImageForm(request.POST, instance=image)
-            if form.is_valid():
-                form.save()
-                return http.HttpResponseRedirect(image.get_absolute_url())
-        else:
-            form = forms.ImageForm(instance=image)
-        editable = True
-    else:
-        form = forms.ReadonlyImageForm(instance=image)
-        editable = False
-
-    publish_error = image.publishable_error()
-    publishable = publish_error is None
-
-    return render(request, "catalogue/image_detail.html", {
-        "object": image,
-        "publishable": publishable,
-        "publishable_error": publish_error,
-        "form": form,
-        "editable": editable,
-    })
-
-
-@permission_required('catalogue.add_chunk')
-def chunk_add(request, slug, chunk):
-    try:
-        doc = Chunk.get(slug, chunk)
-    except (Chunk.MultipleObjectsReturned, Chunk.DoesNotExist):
-        raise Http404
-    if not doc.book.accessible(request):
-        return HttpResponseForbidden("Not authorized.")
-
-    if request.method == "POST":
-        form = forms.ChunkAddForm(request.POST, instance=doc)
-        if form.is_valid():
-            if request.user.is_authenticated():
-                creator = request.user
-            else:
-                creator = None
-            doc.split(creator=creator,
-                slug=form.cleaned_data['slug'],
-                title=form.cleaned_data['title'],
-                gallery_start=form.cleaned_data['gallery_start'],
-                user=form.cleaned_data['user'],
-                stage=form.cleaned_data['stage']
-            )
-
-            return http.HttpResponseRedirect(doc.book.get_absolute_url())
-    else:
-        form = forms.ChunkAddForm(initial={
-                "slug": str(doc.number + 1),
-                "title": "cz. %d" % (doc.number + 1, ),
-        })
-
-    return render(request, "catalogue/chunk_add.html", {
-        "chunk": doc,
-        "form": form,
-    })
-
-
-@login_required
-def chunk_edit(request, slug, chunk):
-    try:
-        doc = Chunk.get(slug, chunk)
-    except (Chunk.MultipleObjectsReturned, Chunk.DoesNotExist):
-        raise Http404
-    if not doc.book.accessible(request):
-        return HttpResponseForbidden("Not authorized.")
-
-    if request.method == "POST":
-        form = forms.ChunkForm(request.POST, instance=doc)
-        if form.is_valid():
-            form.save()
-            go_next = request.GET.get('next', None)
-            if go_next:
-                go_next = urlquote_plus(unquote(iri_to_uri(go_next)), safe='/?=&')
-            else:
-                go_next = doc.book.get_absolute_url()
-            return http.HttpResponseRedirect(go_next)
-    else:
-        form = forms.ChunkForm(instance=doc)
-
-    referer = request.META.get('HTTP_REFERER')
-    if referer:
-        parts = urlsplit(referer)
-        parts = ['', ''] + list(parts[2:])
-        go_next = urlquote_plus(urlunsplit(parts))
-    else:
-        go_next = ''
-
-    return render(request, "catalogue/chunk_edit.html", {
-        "chunk": doc,
-        "form": form,
-        "go_next": go_next,
-    })
-
-
-@transaction.atomic
-@login_required
-@require_POST
-def chunk_mass_edit(request):
-    ids = map(int, filter(lambda i: i.strip()!='', request.POST.get('ids').split(',')))
-    chunks = map(lambda i: Chunk.objects.get(id=i), ids)
-    
-    stage = request.POST.get('stage')
-    if stage:
-        try:
-            stage = Chunk.tag_model.objects.get(slug=stage)
-        except Chunk.DoesNotExist, e:
-            stage = None
-       
-        for c in chunks: c.stage = stage
-
-    username = request.POST.get('user')
-    logger.info("username: %s" % username)
-    logger.info(request.POST)
-    if username:
-        try:
-            user = User.objects.get(username=username)
-        except User.DoesNotExist, e:
-            user = None
-            
-        for c in chunks: c.user = user
-
-    project_id = request.POST.get('project')
-    if project_id:
-        try:
-            project = Project.objects.get(pk=int(project_id))
-        except (Project.DoesNotExist, ValueError), e:
-            project = None
-        for c in chunks:
-            book = c.book
-            book.project = project
-            book.save()
-
-    for c in chunks: c.save()
-
-    return HttpResponse("", content_type="text/plain")
-
-
-@transaction.atomic
-@login_required
-@require_POST
-def image_mass_edit(request):
-    ids = map(int, filter(lambda i: i.strip()!='', request.POST.get('ids').split(',')))
-    images = map(lambda i: Image.objects.get(id=i), ids)
-    
-    stage = request.POST.get('stage')
-    if stage:
-        try:
-            stage = Image.tag_model.objects.get(slug=stage)
-        except Image.DoesNotExist, e:
-            stage = None
-       
-        for c in images: c.stage = stage
-
-    username = request.POST.get('user')
-    logger.info("username: %s" % username)
-    logger.info(request.POST)
-    if username:
-        try:
-            user = User.objects.get(username=username)
-        except User.DoesNotExist, e:
-            user = None
-            
-        for c in images: c.user = user
-
-    project_id = request.POST.get('project')
-    if project_id:
-        try:
-            project = Project.objects.get(pk=int(project_id))
-        except (Project.DoesNotExist, ValueError), e:
-            project = None
-        for c in images:
-            c.project = project
-
-    for c in images: c.save()
-
-    return HttpResponse("", content_type="text/plain")
-
-
-@permission_required('catalogue.change_book')
-def book_append(request, slug):
-    book = get_object_or_404(Book, slug=slug)
-    if not book.accessible(request):
-        return HttpResponseForbidden("Not authorized.")
-
-    if request.method == "POST":
-        form = forms.BookAppendForm(book, request.POST)
-        if form.is_valid():
-            append_to = form.cleaned_data['append_to']
-            append_to.append(book)
-            return http.HttpResponseRedirect(append_to.get_absolute_url())
-    else:
-        form = forms.BookAppendForm(book)
-    return render(request, "catalogue/book_append_to.html", {
-        "book": book,
-        "form": form,
-
-        "logout_to": '/',
-    })
-
-
-@require_POST
-@login_required
-def publish(request, slug):
-    form = forms.PublishOptionsForm(request.POST)
-    if form.is_valid():
-        days = form.cleaned_data['days']
-        beta = form.cleaned_data['beta']
-    else:
-        days = 0
-        beta = False
-    book = get_object_or_404(Book, slug=slug)
-    if not book.accessible(request):
-        return HttpResponseForbidden("Not authorized.")
-
-    try:
-        protocol = 'https://' if request.is_secure() else 'http://'
-        book.publish(request.user, host=protocol + request.get_host(), days=days, beta=beta)
-    except NotAuthorizedError:
-        return http.HttpResponseRedirect(reverse('apiclient_oauth' if not beta else 'apiclient_beta_oauth'))
-    except BaseException, e:
-        return http.HttpResponse(repr(e))
-    else:
-        return http.HttpResponseRedirect(book.get_absolute_url())
-
-
-@require_POST
-@login_required
-def publish_image(request, slug):
-    image = get_object_or_404(Image, slug=slug)
-    if not image.accessible(request):
-        return HttpResponseForbidden("Not authorized.")
-
-    try:
-        image.publish(request.user)
-    except NotAuthorizedError:
-        return http.HttpResponseRedirect(reverse('apiclient_oauth'))
-    except BaseException, e:
-        return http.HttpResponse(e)
-    else:
-        return http.HttpResponseRedirect(image.get_absolute_url())
-
-
-class GalleryView(UploadView):
-    def get_object(self, request, slug):
-        book = get_object_or_404(Book, slug=slug)
-        if not book.gallery:
-            raise Http404
-        return book
-
-    def breadcrumbs(self):
-        return [
-            (_('books'), reverse('catalogue_document_list')),
-            (self.object.title, self.object.get_absolute_url()),
-            (_('scan gallery'),),
-        ]
-
-    def get_directory(self):
-        return "%s%s/" % (settings.IMAGE_DIR, self.object.gallery)
-
-
-def active_users_list(request):
-    since = date(date.today().year, 1, 1)
-    by_user = defaultdict(lambda: 0)
-    by_email = defaultdict(lambda: 0)
-    names_by_email = defaultdict(set)
-    for change_model in (Chunk.change_model, Image.change_model):
-        for c in change_model.objects.filter(
-                created_at__gte=since).order_by(
-                'author', 'author_email', 'author_name').values(
-                'author', 'author_name', 'author_email').annotate(
-                c=Count('author'), ce=Count('author_email')).distinct():
-            if c['author']:
-                by_user[c['author']] += c['c']
-            else:
-                by_email[c['author_email']] += c['ce']
-                if c['author_name'].strip():
-                    names_by_email[c['author_email']].add(c['author_name'])
-    for user in User.objects.filter(pk__in=by_user):
-        by_email[user.email] += by_user[user.pk]
-        names_by_email[user.email].add("%s %s" % (user.first_name, user.last_name))
-
-    active_users = []
-    for email, count in by_email.items():
-        active_users.append((email, names_by_email[email], count))
-    active_users.sort(key=lambda x: -x[2])
-    return render(request, 'catalogue/active_users_list.html', {
-        'users': active_users,
-        'since': since,
-    })
-
-
-@user_passes_test(lambda u: u.is_superuser)
-def mark_final(request):
-    if request.method == 'POST':
-        form = forms.MarkFinalForm(data=request.POST)
-        if form.is_valid():
-            form.save()
-            return HttpResponseRedirect(reverse('mark_final_completed'))
-    else:
-        form = forms.MarkFinalForm()
-    return render(request, 'catalogue/mark_final.html', {'form': form})
-
-
-def mark_final_completed(request):
-    return render(request, 'catalogue/mark_final_completed.html')
diff --git a/apps/catalogue/xml_tools.py b/apps/catalogue/xml_tools.py
deleted file mode 100644 (file)
index 242714b..0000000
+++ /dev/null
@@ -1,141 +0,0 @@
-# -*- coding: utf-8 -*-
-from copy import deepcopy
-import re
-
-from lxml import etree
-from catalogue.constants import TRIM_BEGIN, TRIM_END, MASTERS
-
-RE_TRIM_BEGIN = re.compile("^<!--%s-->$" % TRIM_BEGIN, re.M)
-RE_TRIM_END = re.compile("^<!--%s-->$" % TRIM_END, re.M)
-
-
-class ParseError(BaseException):
-    pass
-
-
-def _trim(text, trim_begin=True, trim_end=True):
-    """ 
-        Cut off everything before RE_TRIM_BEGIN and after RE_TRIM_END, so
-        that eg. one big XML file can be compiled from many small XML files.
-    """
-    if trim_begin:
-        text = RE_TRIM_BEGIN.split(text, maxsplit=1)[-1]
-    if trim_end:
-        text = RE_TRIM_END.split(text, maxsplit=1)[0]
-    return text
-
-
-def compile_text(parts):
-    """ 
-        Compiles full text from an iterable of parts,
-        trimming where applicable.
-    """
-    texts = []
-    trim_begin = False
-    text = ''
-    for next_text in parts:
-        if not next_text:
-            continue
-        if text:
-            # trim the end, because there's more non-empty text
-            # don't trim beginning, if `text' is the first non-empty part
-            texts.append(_trim(text, trim_begin=trim_begin))
-            trim_begin = True
-        text = next_text
-    # don't trim the end, because there's no more text coming after `text'
-    # only trim beginning if it's not still the first non-empty
-    texts.append(_trim(text, trim_begin=trim_begin, trim_end=False))
-    return "".join(texts)
-
-
-def add_trim_begin(text):
-    trim_tag = etree.Comment(TRIM_BEGIN)
-    e = etree.fromstring(text)
-    for master in e[::-1]:
-        if master.tag in MASTERS:
-            break
-    if master.tag not in MASTERS:
-        raise ParseError('No master tag found!')
-
-    master.insert(0, trim_tag)
-    trim_tag.tail = '\n\n\n' + (master.text or '')
-    master.text = '\n'
-    return unicode(etree.tostring(e, encoding="utf-8"), 'utf-8')
-
-
-def add_trim_end(text):
-    trim_tag = etree.Comment(TRIM_END)
-    e = etree.fromstring(text)
-    for master in e[::-1]:
-        if master.tag in MASTERS:
-            break
-    if master.tag not in MASTERS:
-        raise ParseError('No master tag found!')
-
-    master.append(trim_tag)
-    trim_tag.tail = '\n'
-    prev = trim_tag.getprevious()
-    if prev is not None:
-        prev.tail = (prev.tail or '') + '\n\n\n'
-    else:
-        master.text = (master.text or '') + '\n\n\n'
-    return unicode(etree.tostring(e, encoding="utf-8"), 'utf-8')
-
-
-def split_xml(text):
-    """Splits text into chapters.
-
-    All this stuff really must go somewhere else.
-
-    """
-    src = etree.fromstring(text)
-    chunks = []
-
-    splitter = u'naglowek_rozdzial'
-    parts = src.findall('.//naglowek_rozdzial')
-    while parts:
-        # copy the document
-        copied = deepcopy(src)
-
-        element = parts[-1]
-
-        # find the chapter's title
-        name_elem = deepcopy(element)
-        for tag in 'extra', 'motyw', 'pa', 'pe', 'pr', 'pt', 'uwaga':
-            for a in name_elem.findall('.//' + tag):
-                a.text=''
-                del a[:]
-        name = etree.tostring(name_elem, method='text', encoding='utf-8').strip()
-
-        # in the original, remove everything from the start of the last chapter
-        parent = element.getparent()
-        del parent[parent.index(element):]
-        element, parent = parent, parent.getparent()
-        while parent is not None:
-            del parent[parent.index(element) + 1:]
-            element, parent = parent, parent.getparent()
-
-        # in the copy, remove everything before the last chapter
-        element = copied.findall('.//naglowek_rozdzial')[-1]
-        parent = element.getparent()
-        while parent is not None:
-            parent.text = None
-            while parent[0] is not element:
-                del parent[0]
-            element, parent = parent, parent.getparent()
-        chunks[:0] = [[name,
-            unicode(etree.tostring(copied, encoding='utf-8'), 'utf-8')
-            ]]
-
-        parts = src.findall('.//naglowek_rozdzial')
-
-    chunks[:0] = [[u'początek',
-        unicode(etree.tostring(src, encoding='utf-8'), 'utf-8')
-        ]]
-
-    for ch in chunks[1:]:
-        ch[1] = add_trim_begin(ch[1])
-    for ch in chunks[:-1]:
-        ch[1] = add_trim_end(ch[1])
-
-    return chunks
diff --git a/apps/cover/__init__.py b/apps/cover/__init__.py
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/apps/cover/forms.py b/apps/cover/forms.py
deleted file mode 100755 (executable)
index 513bdef..0000000
+++ /dev/null
@@ -1,106 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
-# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
-#
-from StringIO import StringIO
-
-from django import forms
-from django.conf import settings
-from django.utils.translation import ugettext_lazy as _, ugettext
-from cover.models import Image
-from django.utils.text import mark_safe
-from PIL import Image as PILImage
-
-from cover.utils import get_flickr_data, FlickrError, URLOpener
-
-
-class ImageAddForm(forms.ModelForm):
-    class Meta:
-        model = Image
-
-    def __init__(self, *args, **kwargs):
-        super(ImageAddForm, self).__init__(*args, **kwargs)
-        self.fields['file'].required = False
-
-    def clean_download_url(self):
-        cl = self.cleaned_data['download_url'] or None
-        if cl is not None:
-            try:
-                img = Image.objects.get(download_url=cl)
-            except Image.DoesNotExist:
-                pass
-            else:
-                raise forms.ValidationError(mark_safe(
-                    ugettext('Image <a href="%(url)s">already in repository</a>.')
-                    % {'url': img.get_absolute_url()}))
-        return cl
-
-    def clean_source_url(self):
-        source_url = self.cleaned_data['source_url'] or None
-        if source_url is not None:
-            same_source = Image.objects.filter(source_url=source_url)
-            if same_source:
-                raise forms.ValidationError(mark_safe(
-                    ugettext('Image <a href="%s">already in repository</a>'
-                             % same_source.first().get_absolute_url())))
-        return source_url
-
-    def clean(self):
-        cleaned_data = super(ImageAddForm, self).clean()
-        download_url = cleaned_data.get('download_url', None)
-        uploaded_file = cleaned_data.get('file', None)
-        if not download_url and not uploaded_file:
-            raise forms.ValidationError(ugettext('No image specified'))
-        if download_url:
-            image_data = URLOpener().open(download_url).read()
-            width, height = PILImage.open(StringIO(image_data)).size
-        else:
-            width, height = PILImage.open(uploaded_file.file).size
-        min_width, min_height = settings.MIN_COVER_SIZE
-        if width < min_width or height < min_height:
-            raise forms.ValidationError(ugettext('Image too small: %sx%s, minimal dimensions %sx%s') %
-                                        (width, height, min_width, min_height))
-        return cleaned_data
-
-
-class ImageEditForm(forms.ModelForm):
-    """Form used for editing a Book."""
-    class Meta:
-        model = Image
-        exclude = ['download_url']
-
-    def clean(self):
-        cleaned_data = super(ImageEditForm, self).clean()
-        uploaded_file = cleaned_data.get('file', None)
-        width, height = PILImage.open(uploaded_file.file).size
-        min_width, min_height = settings.MIN_COVER_SIZE
-        if width < min_width or height < min_height:
-            raise forms.ValidationError(ugettext('Image too small: %sx%s, minimal dimensions %sx%s') %
-                                        (width, height, min_width, min_height))
-
-
-class ReadonlyImageEditForm(ImageEditForm):
-    """Form used for not editing an Image."""
-
-    def __init__(self, *args, **kwargs):
-        super(ReadonlyImageEditForm, self).__init__(*args, **kwargs)
-        for field in self.fields.values():
-            field.widget.attrs.update({"readonly": True})
-
-    def save(self, *args, **kwargs):
-        raise AssertionError("ReadonlyImageEditForm should not be saved.")
-
-
-class FlickrForm(forms.Form):
-    source_url = forms.URLField(label=_('Flickr URL'))
-
-    def clean_source_url(self):
-        url = self.cleaned_data['source_url']
-        try:
-            flickr_data = get_flickr_data(url)
-        except FlickrError as e:
-            raise forms.ValidationError(e)
-        for field_name in ('license_url', 'license_name', 'author', 'title', 'download_url'):
-            self.cleaned_data[field_name] = flickr_data[field_name]
-        return flickr_data['source_url']
diff --git a/apps/cover/locale/pl/LC_MESSAGES/django.mo b/apps/cover/locale/pl/LC_MESSAGES/django.mo
deleted file mode 100644 (file)
index 1c297fe..0000000
Binary files a/apps/cover/locale/pl/LC_MESSAGES/django.mo and /dev/null differ
diff --git a/apps/cover/locale/pl/LC_MESSAGES/django.po b/apps/cover/locale/pl/LC_MESSAGES/django.po
deleted file mode 100644 (file)
index 67547ce..0000000
+++ /dev/null
@@ -1,100 +0,0 @@
-# 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: 2014-02-25 13:29+0100\n"
-"PO-Revision-Date: 2014-02-25 13:30+0100\n"
-"Last-Translator: Radek Czajka <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"
-"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
-"|| n%100>=20) ? 1 : 2);\n"
-"X-Generator: Poedit 1.5.4\n"
-
-#: forms.py:29
-#, python-format
-msgid "Image <a href=\"%(url)s\">already in repository</a>."
-msgstr "Obraz <a href=\"%(url)s\">jest już w repozytorium</a>."
-
-#: forms.py:60
-msgid "Flickr URL"
-msgstr "URL z Flickra"
-
-#: models.py:19
-msgid "title"
-msgstr "tytuł"
-
-#: models.py:20
-msgid "author"
-msgstr "autor"
-
-#: models.py:21
-msgid "license name"
-msgstr "nazwa licencji"
-
-#: models.py:22
-msgid "license URL"
-msgstr "URL licencji"
-
-#: models.py:23
-msgid "source URL"
-msgstr "URL źródła"
-
-#: models.py:24
-msgid "image download URL"
-msgstr "URL pliku do pobrania"
-
-#: models.py:25
-msgid "file"
-msgstr "plik"
-
-#: models.py:28
-msgid "cover image"
-msgstr "obrazek na okładkę"
-
-#: models.py:29
-msgid "cover images"
-msgstr "obrazki na okładki"
-
-#: templates/cover/add_image.html:33 templates/cover/add_image.html.py:62
-msgid "Add image"
-msgstr "Dodaj obrazek"
-
-#: templates/cover/add_image.html:40
-msgid "Load from Flickr"
-msgstr "Pobierz z Flickra"
-
-#: templates/cover/image_detail.html:7
-msgid "Cover image"
-msgstr "Obrazek na okładkę"
-
-#: templates/cover/image_detail.html:23
-msgid "source"
-msgstr "źródło"
-
-#: templates/cover/image_detail.html:35
-msgid "Change"
-msgstr "Zmień"
-
-#: templates/cover/image_detail.html:41
-msgid "Used in:"
-msgstr "Użyte w:"
-
-#: templates/cover/image_detail.html:49
-msgid "None"
-msgstr "Brak"
-
-#: templates/cover/image_list.html:7
-msgid "Cover images"
-msgstr "Obrazki na okładki"
-
-#: templates/cover/image_list.html:10
-msgid "Add new"
-msgstr "Dodaj nowy"
diff --git a/apps/cover/management/__init__.py b/apps/cover/management/__init__.py
deleted file mode 100644 (file)
index d384124..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-# -*- 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.
-#
diff --git a/apps/cover/management/commands/__init__.py b/apps/cover/management/commands/__init__.py
deleted file mode 100644 (file)
index d384124..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-# -*- 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.
-#
diff --git a/apps/cover/management/commands/refresh_covers.py b/apps/cover/management/commands/refresh_covers.py
deleted file mode 100644 (file)
index cc0ef31..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
-# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
-#
-import urllib2 as urllib
-from optparse import make_option
-
-from django.core.files.base import ContentFile
-from django.core.management import BaseCommand
-
-from cover.models import Image
-from cover.utils import get_flickr_data, URLOpener, FlickrError
-
-
-class Command(BaseCommand):
-    option_list = BaseCommand.option_list + (
-        make_option('--from', dest='from_id', type=int, default=1),
-    )
-
-    def handle(self, *args, **options):
-        from_id = options.get('from_id', 1)
-        images = Image.objects.filter(id__gte=from_id).exclude(book=None).order_by('id')
-        images = images.filter(source_url__contains='flickr.com').exclude(download_url__endswith='_o.jpg')
-        for image in images:
-            print image.id
-            try:
-                flickr_data = get_flickr_data(image.source_url)
-                print flickr_data
-            except FlickrError as e:
-                print 'Flickr analysis failed: %s' % e
-            else:
-                flickr_url = flickr_data['download_url']
-                if flickr_url != image.download_url:
-                    same_url = Image.objects.filter(download_url=flickr_url)
-                    if same_url:
-                        print 'Download url already present in image %s' % same_url.get().id
-                        continue
-                try:
-                    t = URLOpener().open(flickr_url).read()
-                except urllib.URLError:
-                    print 'Broken download url'
-                except IOError:
-                    print 'Connection failed'
-                else:
-                    image.download_url = flickr_url
-                    image.file.save(image.file.name, ContentFile(t))
-                    image.save()
diff --git a/apps/cover/migrations/0001_initial.py b/apps/cover/migrations/0001_initial.py
deleted file mode 100644 (file)
index f31c405..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-# -*- coding: utf-8 -*-
-import datetime
-from south.db import db
-from south.v2 import SchemaMigration
-from django.db import models
-
-
-class Migration(SchemaMigration):
-
-    def forwards(self, orm):
-        # Adding model 'Image'
-        db.create_table('cover_image', (
-            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
-            ('title', self.gf('django.db.models.fields.CharField')(max_length=255)),
-            ('author', self.gf('django.db.models.fields.CharField')(max_length=255)),
-            ('license_name', self.gf('django.db.models.fields.CharField')(max_length=255)),
-            ('license_url', self.gf('django.db.models.fields.URLField')(max_length=255, blank=True)),
-            ('source_url', self.gf('django.db.models.fields.URLField')(max_length=200)),
-            ('download_url', self.gf('django.db.models.fields.URLField')(unique=True, max_length=200)),
-            ('file', self.gf('django.db.models.fields.files.ImageField')(max_length=100)),
-        ))
-        db.send_create_signal('cover', ['Image'])
-
-
-    def backwards(self, orm):
-        # Deleting model 'Image'
-        db.delete_table('cover_image')
-
-
-    models = {
-        'cover.image': {
-            'Meta': {'object_name': 'Image'},
-            'author': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'download_url': ('django.db.models.fields.URLField', [], {'unique': 'True', 'max_length': '200'}),
-            'file': ('django.db.models.fields.files.ImageField', [], {'max_length': '100'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'license_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'license_url': ('django.db.models.fields.URLField', [], {'max_length': '255', 'blank': 'True'}),
-            'source_url': ('django.db.models.fields.URLField', [], {'max_length': '200'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        }
-    }
-
-    complete_apps = ['cover']
\ No newline at end of file
diff --git a/apps/cover/migrations/0002_auto__chg_field_image_download_url.py b/apps/cover/migrations/0002_auto__chg_field_image_download_url.py
deleted file mode 100644 (file)
index 8a64c39..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-# -*- coding: utf-8 -*-
-import datetime
-from south.db import db
-from south.v2 import SchemaMigration
-from django.db import models
-
-
-class Migration(SchemaMigration):
-
-    def forwards(self, orm):
-
-        # Changing field 'Image.download_url'
-        db.alter_column(u'cover_image', 'download_url', self.gf('django.db.models.fields.URLField')(max_length=200, unique=True, null=True))
-
-    def backwards(self, orm):
-
-        # User chose to not deal with backwards NULL issues for 'Image.download_url'
-        raise RuntimeError("Cannot reverse this migration. 'Image.download_url' and its values cannot be restored.")
-
-    models = {
-        u'cover.image': {
-            'Meta': {'object_name': 'Image'},
-            'author': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'download_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'unique': 'True', 'null': 'True'}),
-            'file': ('django.db.models.fields.files.ImageField', [], {'max_length': '100'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'license_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'license_url': ('django.db.models.fields.URLField', [], {'max_length': '255', 'blank': 'True'}),
-            'source_url': ('django.db.models.fields.URLField', [], {'max_length': '200'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        }
-    }
-
-    complete_apps = ['cover']
\ No newline at end of file
diff --git a/apps/cover/migrations/0003_auto__chg_field_image_source_url.py b/apps/cover/migrations/0003_auto__chg_field_image_source_url.py
deleted file mode 100644 (file)
index 98951e3..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-# -*- coding: utf-8 -*-
-import datetime
-from south.db import db
-from south.v2 import SchemaMigration
-from django.db import models
-
-
-class Migration(SchemaMigration):
-
-    def forwards(self, orm):
-
-        # Changing field 'Image.source_url'
-        db.alter_column(u'cover_image', 'source_url', self.gf('django.db.models.fields.URLField')(max_length=200, null=True))
-
-    def backwards(self, orm):
-
-        # User chose to not deal with backwards NULL issues for 'Image.source_url'
-        raise RuntimeError("Cannot reverse this migration. 'Image.source_url' and its values cannot be restored.")
-
-    models = {
-        u'cover.image': {
-            'Meta': {'object_name': 'Image'},
-            'author': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'download_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'unique': 'True', 'null': 'True'}),
-            'file': ('django.db.models.fields.files.ImageField', [], {'max_length': '100'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'license_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'license_url': ('django.db.models.fields.URLField', [], {'max_length': '255', 'blank': 'True'}),
-            'source_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        }
-    }
-
-    complete_apps = ['cover']
\ No newline at end of file
diff --git a/apps/cover/migrations/__init__.py b/apps/cover/migrations/__init__.py
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/apps/cover/models.py b/apps/cover/models.py
deleted file mode 100644 (file)
index d83dad3..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
-# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
-#
-from django.core.files.base import ContentFile
-from django.core.files.storage import FileSystemStorage
-from django.db import models
-from django.db.models.signals import post_save
-from django.dispatch import receiver
-from django.utils.translation import ugettext_lazy as _
-from django.contrib.sites.models import Site
-from cover.utils import URLOpener
-
-
-class OverwriteStorage(FileSystemStorage):
-
-    def get_available_name(self, name, max_length=None):
-        self.delete(name)
-        return name
-
-
-class Image(models.Model):
-    title = models.CharField(max_length=255, verbose_name=_('title'))
-    author = models.CharField(max_length=255, verbose_name=_('author'))
-    license_name = models.CharField(max_length=255, verbose_name=_('license name'))
-    license_url = models.URLField(max_length=255, blank=True, verbose_name=_('license URL'))
-    source_url = models.URLField(verbose_name=_('source URL'), null=True, blank=True)
-    download_url = models.URLField(unique=True, verbose_name=_('image download URL'), null=True, blank=True)
-    file = models.ImageField(
-        upload_to='cover/image', storage=OverwriteStorage(), editable=True, verbose_name=_('file'))
-
-    class Meta:
-        verbose_name = _('cover image')
-        verbose_name_plural = _('cover images')
-
-    def __unicode__(self):
-        return u"%s - %s" % (self.author, self.title)
-
-    @models.permalink
-    def get_absolute_url(self):
-        return 'cover_image', [self.id]
-
-    def get_full_url(self):
-        return "http://%s%s" % (Site.objects.get_current().domain, self.get_absolute_url())
-
-
-@receiver(post_save, sender=Image)
-def download_image(sender, instance, **kwargs):
-    if instance.pk and not instance.file:
-        t = URLOpener().open(instance.download_url).read()
-        instance.file.save("%d.jpg" % instance.pk, ContentFile(t))
diff --git a/apps/cover/templates/cover/add_image.html b/apps/cover/templates/cover/add_image.html
deleted file mode 100755 (executable)
index 293c100..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-{% extends "catalogue/base.html" %}
-{% load i18n %}
-
-{% block titleextra %}{% trans "Add image" %}{% endblock %}
-
-{% block add_js %}
-    {{block.super}}
-    <script>
-        $(function() {
-            var radio_buttons = $('input[type=radio][name=upload_type]'),
-                image_fields = $('.upload_type input[type=text],input[type=file]');
-
-            var enable_image_field = function(field) {
-                field.attr('disabled', false);
-            },
-            disable_image_fields = function() {
-                image_fields.attr('disabled', true);
-            }
-
-            radio_buttons.change(function() {
-                var radio_button = $(this),
-                    related_image_field = $('#'+radio_button.attr('data-for'));
-                disable_image_fields();
-                enable_image_field(related_image_field);
-            });
-
-            /* initial state */
-            disable_image_fields();
-            enable_image_field($('#id_download_url'));
-        });
-    </script>
-{% endblock %}
-
-{% block content %}
-<h1>{% trans "Add image" %}</h1>
-
-
-<form method="post">{% csrf_token %}
-<input type="hidden" name='form_id' value="flickr" />
-<table class='editable'><tbody>
-    {{ ff.as_table }}
-    <tr><td></td><td><button type="submit">{% trans "Load from Flickr" %}</button></td></tr>
-</tbody></table>
-</form>
-
-<form method="post" enctype="multipart/form-data">{% csrf_token %}
-{{ form.non_field_errors }}
-<table class='editable'><tbody>
-    {% for field in form %}
-        {% if field.name != 'download_url' and field.name != 'file' %}
-        <tr>
-            <th>{{field.errors}} {{field.label}}</th>
-            <td>{{field}}</td>
-        </tr>
-        {% endif %}
-    {% endfor %}
-    <tr class="upload_type">
-        <th>{{ form.download_url.errors }} <input style="width: auto;" checked data-for="id_download_url" type="radio" name="upload_type" value="url"/>{{form.download_url.label}}</th>
-        <td>{{form.download_url}}</td>
-        <th>{{ form.file.errors }} <input style="width: auto;" data-for="id_file" type="radio" name="upload_type" value="file"/> Lub {{form.file.label}}</th>
-        <td>{{form.file}}</td>
-
-    </tr>
-    <tr><td></td><td><button type="submit">{% trans "Add image" %}</button></td></tr>
-</tbody></table>
-</form>
-
-
-{% endblock %}
diff --git a/apps/cover/templates/cover/image_detail.html b/apps/cover/templates/cover/image_detail.html
deleted file mode 100755 (executable)
index db9b176..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-{% extends "catalogue/base.html" %}
-{% load i18n %}
-{% load thumbnail %}
-{% load build_absolute_uri from fnp_common %}
-
-{% block titleextra %}{% trans "Cover image" %}{% endblock %}
-
-{% block content %}
-<h1>{% trans "Cover image" %}</h1>
-
-<div style="float: right; margin-bottom:1em;">
-
-<a href="{{ object.file.url }}"><img
-        src="{% thumbnail object.file "565x833" as thumb %}
-                {{ thumb.url }}
-             {% empty %}
-                {{ object.file.url }}
-             {% endthumbnail %}" />
-    </a>
-<br/><a href="{{ object.source_url }}">{{ object.title }}</a> by {{ object.author }},
-    {% if object.license_url %}<a href="{{ object.license_url }}">{% endif %}
-        {{ object.license_name }}
-    {% if object.license_url %}</a>{% endif %}
-
-<br/>{% trans "source" %}: {{ object.download_url }}
-</div>
-
-
-{% if editable %}
-    <form method="post" enctype="multipart/form-data">
-    {% csrf_token %}
-{% endif %}
-<table class='editable'><tbody>
-    {{ form.as_table }}
-    {% if editable %}
-        <tr><td></td><td><button type="submit">{% trans "Change" %}</button></td></tr>
-    {% endif %}
-</tbody></table>
-{% if editable %}</form>{% endif %}
-
-
-<h2>{% trans "Used in:" %}</h2>
-{% if object.book_set %}
-<ul>
-    {% for book in object.book_set.all %}
-        <li><a href="{{ book.get_absolute_url }}">{{ book }}</a></li>
-    {% endfor %}
-</ul>
-{% else %}
-    <p>{% trans "None" %}</p>
-{% endif %}
-
-
-<textarea style="width:100%" rows="5">
-&lt;dc:relation.coverImage.url xmlns:dc="http://purl.org/dc/elements/1.1/">{{ object.file.url|build_absolute_uri:request }}&lt;/dc:relation.coverImage.url>
-&lt;dc:relation.coverImage.attribution xmlns:dc="http://purl.org/dc/elements/1.1/">{{ object.author }}, {{ object.license_name }}&lt;/dc:relation.coverImage.attribution>
-&lt;dc:relation.coverImage.source xmlns:dc="http://purl.org/dc/elements/1.1/">{{ object.get_full_url }}&lt;/dc:relation.coverImage.source>
-</textarea>
-{% endblock %}
diff --git a/apps/cover/templates/cover/image_list.html b/apps/cover/templates/cover/image_list.html
deleted file mode 100755 (executable)
index 50443ed..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-{% extends "catalogue/base.html" %}
-{% load i18n %}
-{% load thumbnail pagination_tags %}
-
-{% block titleextra %}{% trans "Cover images" %}{% endblock %}
-
-{% block content %}
-<h1>{% trans "Cover images" %}</h1>
-
-{% if can_add %}
-    <a href="{% url 'cover_add_image' %}">{% trans "Add new" %}</a>
-{% endif %}
-
-<ul>
-{% autopaginate object_list 100 %}
-{% for image in object_list %}
-    <a href="{{ image.get_absolute_url }}" style="display:inline-block; width:200px;">
-    
-    <img style="height: 100px"
-        src="{% thumbnail image.file "x100" as thumb %}
-                {{ thumb.url }}
-             {% empty %}
-                {{ image.file.url }}
-             {% endthumbnail %}" />
-        <br/>
-        {{ image }}</a>
-{% endfor %}
-{% paginate %}
-</ul>
-
-{% endblock %}
diff --git a/apps/cover/tests.py b/apps/cover/tests.py
deleted file mode 100644 (file)
index be8d003..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
-# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
-#
-from nose.tools import *
-from django.test import TestCase
-from cover.forms import FlickrForm
-
-
-class FlickrTests(TestCase):
-    def test_flickr(self):
-        form = FlickrForm({"source_url": "https://www.flickr.com/photos/rczajka/6941928577/in/photostream"})
-        self.assertTrue(form.is_valid())
-        self.assertEqual(form.cleaned_data['source_url'], "https://www.flickr.com/photos/rczajka/6941928577/")
-        self.assertEqual(form.cleaned_data['author'], "Radek Czajka@Flickr")
-        self.assertEqual(form.cleaned_data['title'], u"Pirate Stańczyk")
-        self.assertEqual(form.cleaned_data['license_name'], "CC BY 2.0")
-        self.assertEqual(form.cleaned_data['license_url'], "https://creativecommons.org/licenses/by/2.0/")
-        self.assertTrue('.staticflickr.com' in form.cleaned_data['download_url'])
diff --git a/apps/cover/urls.py b/apps/cover/urls.py
deleted file mode 100644 (file)
index 1146f62..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
-# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
-#
-from django.conf.urls import patterns, url
-
-
-urlpatterns = patterns('cover.views',
-    url(r'^preview/$', 'preview_from_xml', name='cover_preview'),
-    url(r'^preview/(?P<book>[^/]+)/$', 'preview', name='cover_preview'),
-    url(r'^preview/(?P<book>[^/]+)/(?P<chunk>[^/]+)/$',
-            'preview', name='cover_preview'),
-    url(r'^preview/(?P<book>[^/]+)/(?P<chunk>[^/]+)/(?P<rev>\d+)/$',
-            'preview', name='cover_preview'),
-
-    url(r'^image/$', 'image_list', name='cover_image_list'),
-    url(r'^image/(?P<pk>\d+)/?$', 'image', name='cover_image'),
-    url(r'^image/(?P<pk>\d+)/file/', 'image_file', name='cover_file'),
-    url(r'^add_image/$', 'add_image', name='cover_add_image'),
-)
diff --git a/apps/cover/utils.py b/apps/cover/utils.py
deleted file mode 100755 (executable)
index 51aee19..0000000
+++ /dev/null
@@ -1,73 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
-# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
-#
-import json
-import re
-from urllib import FancyURLopener
-
-from django.contrib.sites.models import Site
-
-
-class URLOpener(FancyURLopener):
-    @property
-    def version(self):
-        return 'FNP Redakcja (http://%s)' % Site.objects.get_current()
-
-
-class FlickrError(Exception):
-    pass
-
-
-def get_flickr_data(url):
-    m = re.match(r'(https?://)?(www\.|secure\.)?flickr\.com/photos/(?P<author>[^/]+)/(?P<img>\d+)/?', url)
-    if not m:
-        raise FlickrError("It doesn't look like Flickr URL.")
-    author_slug, img_id = m.group('author'), m.group('img')
-    base_url = "https://www.flickr.com/photos/%s/%s/" % (author_slug, img_id)
-    try:
-        html = URLOpener().open(url).read().decode('utf-8')
-    except IOError:
-        raise FlickrError('Error reading page')
-    match = re.search(r'<a href="([^"]*)"[^>]* rel="license ', html)
-    if not match:
-        raise FlickrError('License not found.')
-    else:
-        license_url = match.group(1)
-        re_license = re.compile(r'https?://creativecommons.org/licenses/([^/]*)/([^/]*)/.*')
-        m = re_license.match(license_url)
-        if not m:
-            re_pd = re.compile(r'https?://creativecommons.org/publicdomain/([^/]*)/([^/]*)/.*')
-            m = re_pd.match(license_url)
-            if not m:
-                raise FlickrError('License does not look like CC: %s' % license_url)
-            if m.group(1).lower() == 'zero':
-                license_name = 'Public domain (CC0 %s)' % m.group(2)
-            else:
-                license_name = 'Public domain'
-        else:
-            license_name = 'CC %s %s' % (m.group(1).upper(), m.group(2))
-    m = re.search(r'<a[^>]* class="owner-name [^>]*>([^<]*)<', html)
-    if m:
-        author = "%s@Flickr" % m.group(1)
-    else:
-        raise FlickrError('Error reading author name.')
-    m = re.search(r'<h1[^>]*>(.*?)</h1>', html, re.S)
-    if not m:
-        raise FlickrError('Error reading image title.')
-    title = m.group(1).strip()
-    m = re.search(r'modelExport: (\{.*\})', html)
-    try:
-        assert m
-        download_url = 'https:' + json.loads(m.group(1))['main']['photo-models'][0]['sizes']['o']['url']
-    except (AssertionError, ValueError, IndexError, KeyError):
-        raise FlickrError('Error reading image URL.')
-    return {
-        'source_url': base_url,
-        'license_url': license_url,
-        'license_name': license_name,
-        'author': author,
-        'title': title,
-        'download_url': download_url,
-    }
diff --git a/apps/cover/views.py b/apps/cover/views.py
deleted file mode 100644 (file)
index 3f2c46f..0000000
+++ /dev/null
@@ -1,141 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
-# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
-#
-import os.path
-from django.conf import settings
-from django.contrib.auth.decorators import permission_required
-from django.http import HttpResponse, HttpResponseRedirect, Http404
-from django.shortcuts import get_object_or_404, render
-from django.views.decorators.csrf import csrf_exempt
-from django.views.decorators.http import require_POST
-from catalogue.helpers import active_tab
-from catalogue.models import Chunk
-from cover.models import Image
-from cover import forms
-
-PREVIEW_SIZE = (216, 300)
-
-
-def preview(request, book, chunk=None, rev=None):
-    """Creates a cover image.
-
-    If chunk and rev number are given, use version from given revision.
-    If rev is not given, use publishable version.
-    """
-    from PIL import Image
-    from librarian.cover import make_cover
-    from librarian.dcparser import BookInfo
-
-    chunk = Chunk.get(book, chunk)
-    if rev is not None:
-        try:
-            revision = chunk.at_revision(rev)
-        except Chunk.change_model.DoesNotExist:
-            raise Http404
-    else:
-        revision = chunk.publishable()
-        if revision is None:
-            raise Http404
-    xml = revision.materialize().encode('utf-8')
-
-    try:
-        info = BookInfo.from_bytes(xml)
-    except:
-        return HttpResponseRedirect(os.path.join(settings.STATIC_URL, "img/sample_cover.png"))
-    cover = make_cover(info)
-    response = HttpResponse(content_type=cover.mime_type())
-    img = cover.image().resize(PREVIEW_SIZE, Image.ANTIALIAS)
-    img.save(response, cover.format)
-    return response
-
-
-@csrf_exempt
-@require_POST
-def preview_from_xml(request):
-    from hashlib import sha1
-    from PIL import Image
-    from os import makedirs
-    from lxml import etree
-    from librarian.cover import make_cover
-    from librarian.dcparser import BookInfo
-
-    xml = request.POST['xml']
-    try:
-        info = BookInfo.from_bytes(xml.encode('utf-8'))
-    except:
-        return HttpResponse(os.path.join(settings.STATIC_URL, "img/sample_cover.png"))
-    coverid = sha1(etree.tostring(info.to_etree())).hexdigest()
-    cover = make_cover(info)
-
-    cover_dir = 'cover/preview'
-    try:
-        makedirs(os.path.join(settings.MEDIA_ROOT, cover_dir))
-    except OSError:
-        pass
-    fname = os.path.join(cover_dir, "%s.%s" % (coverid, cover.ext()))
-    img = cover.image().resize(PREVIEW_SIZE, Image.ANTIALIAS)
-    img.save(os.path.join(settings.MEDIA_ROOT, fname))
-    return HttpResponse(os.path.join(settings.MEDIA_URL, fname))
-
-
-@active_tab('cover')
-def image(request, pk):
-    img = get_object_or_404(Image, pk=pk)
-
-    if request.user.has_perm('cover.change_image'):
-        if request.method == "POST":
-            form = forms.ImageEditForm(request.POST, request.FILES, instance=img)
-            if form.is_valid():
-                form.save()
-                return HttpResponseRedirect(img.get_absolute_url())
-        else:
-            form = forms.ImageEditForm(instance=img)
-        editable = True
-    else:
-        form = forms.ReadonlyImageEditForm(instance=img)
-        editable = False
-
-    return render(request, "cover/image_detail.html", {
-        "object": Image.objects.get(id=img.id),
-        "form": form,
-        "editable": editable,
-    })
-
-
-def image_file(request, pk):
-    img = get_object_or_404(Image, pk=pk)
-    return HttpResponseRedirect(img.file.url)
-
-
-@active_tab('cover')
-def image_list(request):
-    return render(request, "cover/image_list.html", {
-        'object_list': Image.objects.all(),
-        'can_add': request.user.has_perm('cover.add_image'),
-    })
-
-
-@permission_required('cover.add_image')
-@active_tab('cover')
-def add_image(request):
-    form = ff = None
-    if request.method == 'POST':
-        if request.POST.get('form_id') == 'flickr':
-            ff = forms.FlickrForm(request.POST)
-            if ff.is_valid():
-                form = forms.ImageAddForm(ff.cleaned_data)
-        else:
-            form = forms.ImageAddForm(request.POST, request.FILES)
-            if form.is_valid():
-                obj = form.save()
-                return HttpResponseRedirect(obj.get_absolute_url())
-    if form is None:
-        form = forms.ImageAddForm()
-    if ff is None:
-        ff = forms.FlickrForm()
-    return render(request, 'cover/add_image.html', {
-            'form': form,
-            'ff': ff,
-        })
diff --git a/apps/dvcs/__init__.py b/apps/dvcs/__init__.py
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/apps/dvcs/locale/pl/LC_MESSAGES/django.mo b/apps/dvcs/locale/pl/LC_MESSAGES/django.mo
deleted file mode 100644 (file)
index dfd85c2..0000000
Binary files a/apps/dvcs/locale/pl/LC_MESSAGES/django.mo and /dev/null differ
diff --git a/apps/dvcs/locale/pl/LC_MESSAGES/django.po b/apps/dvcs/locale/pl/LC_MESSAGES/django.po
deleted file mode 100644 (file)
index c0365d5..0000000
+++ /dev/null
@@ -1,124 +0,0 @@
-# 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: 2011-12-14 15:25+0100\n"
-"PO-Revision-Date: 2011-12-14 15:27+0100\n"
-"Last-Translator: Radek Czajka <radoslaw.czajka@nowoczesnapolska.org.pl>\n"
-"Language-Team: LANGUAGE <LL@li.org>\n"
-"Language: \n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n"
-
-#: models.py:19
-msgid "name"
-msgstr "nazwa"
-
-#: models.py:20
-msgid "slug"
-msgstr "slug"
-
-#: models.py:22
-msgid "ordering"
-msgstr "kolejność"
-
-#: models.py:70
-msgid "author"
-msgstr "autor"
-
-#: models.py:71
-msgid "author name"
-msgstr "imię i nazwisko autora"
-
-#: models.py:73
-#: models.py:77
-msgid "Used if author is not set."
-msgstr "Używane, gdy nie jest ustawiony autor."
-
-#: models.py:75
-msgid "author email"
-msgstr "e-mail autora"
-
-#: models.py:79
-msgid "revision"
-msgstr "rewizja"
-
-#: models.py:83
-msgid "parent"
-msgstr "rodzic"
-
-#: models.py:88
-msgid "merge parent"
-msgstr "drugi rodzic"
-
-#: models.py:91
-msgid "description"
-msgstr "opis"
-
-#: models.py:94
-msgid "publishable"
-msgstr "do publikacji"
-
-#: models.py:176
-msgid "tag"
-msgstr "tag"
-
-#: models.py:176
-#: models.py:178
-#: models.py:194
-#: models.py:196
-msgid "for:"
-msgstr "dla:"
-
-#: models.py:178
-#: models.py:202
-msgid "tags"
-msgstr "tagi"
-
-#: models.py:194
-msgid "change"
-msgstr "zmiana"
-
-#: models.py:196
-msgid "changes"
-msgstr "zmiany"
-
-#: models.py:201
-msgid "document"
-msgstr "dokument"
-
-#: models.py:203
-msgid "data"
-msgstr "dane"
-
-#: models.py:217
-msgid "stage"
-msgstr "etap"
-
-#: models.py:225
-msgid "head"
-msgstr "głowica"
-
-#: models.py:226
-msgid "This document's current head."
-msgstr "Aktualna wersja dokumentu."
-
-#: models.py:230
-msgid "creator"
-msgstr "utworzył"
-
-#: models.py:245
-msgid "user"
-msgstr "użytkownik"
-
-#: models.py:245
-msgid "Work assignment."
-msgstr "Przypisanie pracy użytkownikowi."
-
diff --git a/apps/dvcs/models.py b/apps/dvcs/models.py
deleted file mode 100644 (file)
index 24bdeb3..0000000
+++ /dev/null
@@ -1,332 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
-# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
-#
-from datetime import datetime
-import os.path
-
-from django.contrib.auth.models import User
-from django.core.files.base import ContentFile
-from django.db import models, transaction
-from django.db.models.base import ModelBase
-from django.utils.translation import string_concat, ugettext_lazy as _
-from mercurial import simplemerge
-
-from django.conf import settings
-from dvcs.signals import post_commit, post_publishable
-from dvcs.storage import GzipFileSystemStorage
-
-
-class Tag(models.Model):
-    """A tag (e.g. document stage) which can be applied to a Change."""
-    name = models.CharField(_('name'), max_length=64)
-    slug = models.SlugField(_('slug'), unique=True, max_length=64, null=True, blank=True)
-    ordering = models.IntegerField(_('ordering'))
-
-    _object_cache = {}
-
-    class Meta:
-        abstract = True
-        ordering = ['ordering']
-
-    def __unicode__(self):
-        return self.name
-
-    @classmethod
-    def get(cls, slug):
-        if slug in cls._object_cache:
-            return cls._object_cache[slug]
-        else:
-            obj = cls.objects.get(slug=slug)
-            cls._object_cache[slug] = obj
-            return obj
-
-    @staticmethod
-    def listener_changed(sender, instance, **kwargs):
-        sender._object_cache = {}
-
-    def get_next(self):
-        """
-            Returns the next tag - stage to work on.
-            Returns None for the last stage.
-        """
-        try:
-            return type(self).objects.filter(ordering__gt=self.ordering)[0]
-        except IndexError:
-            return None
-
-models.signals.pre_save.connect(Tag.listener_changed, sender=Tag)
-
-
-def data_upload_to(instance, filename):
-    return "%d/%d" % (instance.tree.pk, instance.pk)
-
-
-class Change(models.Model):
-    """
-        Single document change related to previous change. The "parent"
-        argument points to the version against which this change has been 
-        recorded. Initial text will have a null parent.
-        
-        Data file contains a gzipped text of the document.
-    """
-    author = models.ForeignKey(User, null=True, blank=True, verbose_name=_('author'))
-    author_name = models.CharField(
-        _('author name'), max_length=128, null=True, blank=True, help_text=_("Used if author is not set."))
-    author_email = models.CharField(
-        _('author email'), max_length=128, null=True, blank=True, help_text=_("Used if author is not set."))
-    revision = models.IntegerField(_('revision'), db_index=True)
-
-    parent = models.ForeignKey(
-        'self', null=True, blank=True, default=None, verbose_name=_('parent'), related_name="children")
-
-    merge_parent = models.ForeignKey(
-        'self', null=True, blank=True, default=None, verbose_name=_('merge parent'), related_name="merge_children")
-
-    description = models.TextField(_('description'), blank=True, default='')
-    created_at = models.DateTimeField(editable=False, db_index=True, default=datetime.now)
-    publishable = models.BooleanField(_('publishable'), default=False)
-
-    class Meta:
-        abstract = True
-        ordering = ('created_at',)
-        unique_together = ['tree', 'revision']
-
-    def __unicode__(self):
-        return u"Id: %r, Tree %r, Parent %r, Data: %s" % (self.id, self.tree_id, self.parent_id, self.data)
-
-    def author_str(self):
-        if self.author:
-            return "%s %s <%s>" % (
-                self.author.first_name,
-                self.author.last_name, 
-                self.author.email)
-        else:
-            return "%s <%s>" % (
-                self.author_name,
-                self.author_email
-                )
-
-    def save(self, *args, **kwargs):
-        """
-            take the next available revision number if none yet
-        """
-        if self.revision is None:
-            tree_rev = self.tree.revision()
-            if tree_rev is None:
-                self.revision = 1
-            else:
-                self.revision = tree_rev + 1
-        return super(Change, self).save(*args, **kwargs)
-
-    def materialize(self):
-        f = self.data.storage.open(self.data)
-        text = f.read()
-        f.close()
-        return unicode(text, 'utf-8')
-
-    def merge_with(self, other, author=None, 
-            author_name=None, author_email=None, 
-            description=u"Automatic merge."):
-        """Performs an automatic merge after straying commits."""
-        assert self.tree_id == other.tree_id  # same tree
-        if other.parent_id == self.pk:
-            # immediate child - fast forward
-            return other
-
-        local = self.materialize().encode('utf-8')
-        base = other.parent.materialize().encode('utf-8')
-        remote = other.materialize().encode('utf-8')
-
-        merge = simplemerge.Merge3Text(base, local, remote)
-        result = ''.join(merge.merge_lines())
-        merge_node = self.children.create(
-                    merge_parent=other, tree=self.tree,
-                    author=author,
-                    author_name=author_name,
-                    author_email=author_email,
-                    description=description)
-        merge_node.data.save('', ContentFile(result))
-        return merge_node
-
-    def revert(self, **kwargs):
-        """ commit this version of a doc as new head """
-        self.tree.commit(text=self.materialize(), **kwargs)
-
-    def set_publishable(self, publishable):
-        self.publishable = publishable
-        self.save()
-        post_publishable.send(sender=self, publishable=publishable)
-
-
-def create_tag_model(model):
-    name = model.__name__ + 'Tag'
-
-    class Meta(Tag.Meta):
-        app_label = model._meta.app_label
-        verbose_name = string_concat(
-            _("tag"), " ", _("for:"), " ", model._meta.verbose_name)
-        verbose_name_plural = string_concat(
-            _("tags"), " ", _("for:"), " ", model._meta.verbose_name)
-
-    attrs = {
-        '__module__': model.__module__,
-        'Meta': Meta,
-    }
-    return type(name, (Tag,), attrs)
-
-
-def create_change_model(model):
-    name = model.__name__ + 'Change'
-    repo = GzipFileSystemStorage(location=model.REPO_PATH)
-
-    class Meta(Change.Meta):
-        app_label = model._meta.app_label
-        verbose_name = string_concat(
-            _("change"), " ", _("for:"), " ", model._meta.verbose_name)
-        verbose_name_plural = string_concat(
-            _("changes"), " ", _("for:"), " ", model._meta.verbose_name)
-
-    attrs = {
-        '__module__': model.__module__,
-        'tree': models.ForeignKey(model, related_name='change_set', verbose_name=_('document')),
-        'tags': models.ManyToManyField(model.tag_model, verbose_name=_('tags'), related_name='change_set'),
-        'data': models.FileField(_('data'), upload_to=data_upload_to, storage=repo),
-        'Meta': Meta,
-    }
-    return type(name, (Change,), attrs)
-
-
-class DocumentMeta(ModelBase):
-    """Metaclass for Document models."""
-    def __new__(cls, name, bases, attrs):
-
-        model = super(DocumentMeta, cls).__new__(cls, name, bases, attrs)
-        if not model._meta.abstract:
-            # create a real Tag object and `stage' fk
-            model.tag_model = create_tag_model(model)
-            models.ForeignKey(model.tag_model, verbose_name=_('stage'),
-                null=True, blank=True).contribute_to_class(model, 'stage')
-
-            # create real Change model and `head' fk
-            model.change_model = create_change_model(model)
-
-            models.ForeignKey(
-                model.change_model, null=True, blank=True, default=None,
-                verbose_name=_('head'), help_text=_("This document's current head."),
-                editable=False).contribute_to_class(model, 'head')
-
-            models.ForeignKey(
-                User, null=True, blank=True, editable=False,
-                verbose_name=_('creator'), related_name="created_%s" % name.lower()
-                ).contribute_to_class(model, 'creator')
-
-        return model
-
-
-class Document(models.Model):
-    """File in repository. Subclass it to use version control in your app."""
-
-    __metaclass__ = DocumentMeta
-
-    # default repository path
-    REPO_PATH = os.path.join(settings.MEDIA_ROOT, 'dvcs')
-
-    user = models.ForeignKey(User, null=True, blank=True, verbose_name=_('user'), help_text=_('Work assignment.'))
-
-    class Meta:
-        abstract = True
-
-    def __unicode__(self):
-        return u"{0}, HEAD: {1}".format(self.id, self.head_id)
-
-    def materialize(self, change=None):
-        if self.head is None:
-            return u''
-        if change is None:
-            change = self.head
-        elif not isinstance(change, Change):
-            change = self.change_set.get(pk=change)
-        return change.materialize()
-
-    def commit(self, text, author=None, author_name=None, author_email=None, publishable=False, **kwargs):
-        """Commits a new revision.
-
-        This will automatically merge the commit into the main branch,
-        if parent is not document's head.
-
-        :param unicode text: new version of the document
-        :param parent: parent revision (head, if not specified)
-        :type parent: Change or None
-        :param User author: the commiter
-        :param unicode author_name: commiter name (if ``author`` not specified)
-        :param unicode author_email: commiter e-mail (if ``author`` not specified)
-        :param Tag[] tags: list of tags to apply to the new commit
-        :param bool publishable: set new commit as ready to publish
-        :returns: new head
-        """
-        if 'parent' not in kwargs:
-            parent = self.head
-        else:
-            parent = kwargs['parent']
-            if parent is not None and not isinstance(parent, Change):
-                parent = self.change_set.objects.get(pk=kwargs['parent'])
-
-        tags = kwargs.get('tags', [])
-        if tags:
-            # set stage to next tag after the commited one
-            self.stage = max(tags, key=lambda t: t.ordering).get_next()
-
-        change = self.change_set.create(
-            author=author, author_name=author_name, author_email=author_email,
-            description=kwargs.get('description', ''), publishable=publishable, parent=parent)
-
-        change.tags = tags
-        change.data.save('', ContentFile(text.encode('utf-8')))
-        change.save()
-
-        if self.head:
-            # merge new change as new head
-            self.head = self.head.merge_with(change, author=author,
-                    author_name=author_name,
-                    author_email=author_email)
-        else:
-            self.head = change
-        self.save()
-
-        post_commit.send(sender=self.head)
-
-        return self.head
-
-    def history(self):
-        return self.change_set.all().order_by('revision')
-
-    def revision(self):
-        rev = self.change_set.aggregate(
-                models.Max('revision'))['revision__max']
-        return rev
-
-    def at_revision(self, rev):
-        """Returns a Change with given revision number."""
-        return self.change_set.get(revision=rev)
-
-    def publishable(self):
-        changes = self.history().filter(publishable=True)
-        if changes.exists():
-            return changes.order_by('-revision')[0]
-        else:
-            return None
-
-    @transaction.atomic
-    def prepend_history(self, other):
-        """Takes over the the other document's history and prepends to own."""
-
-        assert self != other
-        other_revs = other.change_set.all().count()
-        # workaround for a non-atomic UPDATE in SQLITE
-        self.change_set.all().update(revision=0-models.F('revision'))
-        self.change_set.all().update(revision=other_revs - models.F('revision'))
-        other.change_set.all().update(tree=self)
-        assert not other.change_set.exists()
-        other.delete()
diff --git a/apps/dvcs/signals.py b/apps/dvcs/signals.py
deleted file mode 100755 (executable)
index 5da075b..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-from django.dispatch import Signal
-
-post_commit = Signal()
-post_publishable = Signal(providing_args=['publishable'])
diff --git a/apps/dvcs/storage.py b/apps/dvcs/storage.py
deleted file mode 100755 (executable)
index 6bb5b59..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-from zlib import compress, decompress
-
-from django.core.files.base import ContentFile, File
-from django.core.files.storage import FileSystemStorage
-
-
-class GzipFileSystemStorage(FileSystemStorage):
-    def _open(self, name, mode='rb'):
-        """TODO: This is good for reading; what about writing?"""
-        f = open(self.path(name), 'rb')
-        text = f.read()
-        f.close()
-        return ContentFile(decompress(text))
-
-    def _save(self, name, content):
-        content = ContentFile(compress(content.read()))
-
-        return super(GzipFileSystemStorage, self)._save(name, content)
diff --git a/apps/dvcs/tests/__init__.py b/apps/dvcs/tests/__init__.py
deleted file mode 100755 (executable)
index 868f00a..0000000
+++ /dev/null
@@ -1,178 +0,0 @@
-from nose.tools import *
-from django.test import TestCase
-from dvcs.models import Document
-
-
-class ADocument(Document):
-    class Meta:
-        app_label = 'dvcs'
-
-
-class DocumentModelTests(TestCase):
-
-    def assertTextEqual(self, given, expected):
-        return self.assertEqual(given, expected,
-            "Expected '''%s'''\n differs from text: '''%s'''" % (expected, given)
-        )
-
-    def test_empty_file(self):
-        doc = ADocument.objects.create()
-        self.assertTextEqual(doc.materialize(), u"")
-
-    def test_single_commit(self):
-        doc = ADocument.objects.create()
-        doc.commit(text=u"Ala ma kota", description="Commit #1")
-        self.assertTextEqual(doc.materialize(), u"Ala ma kota")
-
-    def test_chained_commits(self):
-        doc = ADocument.objects.create()
-        text1 = u"""
-            Line #1
-            Line #2 is cool
-        """
-        text2 = u"""
-            Line #1
-            Line #2 is hot
-        """
-        text3 = u"""
-            Line #1
-            ... is hot
-            Line #3 ate Line #2
-        """
-
-        c1 = doc.commit(description="Commit #1", text=text1)
-        c2 = doc.commit(description="Commit #2", text=text2)
-        c3 = doc.commit(description="Commit #3", text=text3)
-
-        self.assertTextEqual(doc.materialize(), text3)
-        self.assertTextEqual(doc.materialize(change=c3), text3)
-        self.assertTextEqual(doc.materialize(change=c2), text2)
-        self.assertTextEqual(doc.materialize(change=c1), text1)
-
-    def test_parallel_commit_noconflict(self):
-        doc = ADocument.objects.create()
-        text1 = u"""
-            Line #1
-            Line #2
-        """
-        text2 = u"""
-            Line #1 is hot
-            Line #2
-        """
-        text3 = u"""
-            Line #1
-            Line #2
-            Line #3
-        """
-        text_merged = u"""
-            Line #1 is hot
-            Line #2
-            Line #3
-        """
-
-        base = doc.commit(description="Commit #1", text=text1)
-        c1 = doc.commit(description="Commit #2", text=text2)
-        commits = doc.change_set.count()
-        c2 = doc.commit(description="Commit #3", text=text3, parent=base)
-        self.assertEqual(doc.change_set.count(), commits + 2,
-            u"Parallel commits should create an additional merge commit")
-        self.assertTextEqual(doc.materialize(), text_merged)
-
-    def test_parallel_commit_conflict(self):
-        doc = ADocument.objects.create()
-        text1 = u"""
-            Line #1
-            Line #2
-            Line #3
-        """
-        text2 = u"""
-            Line #1
-            Line #2 is hot
-            Line #3
-        """
-        text3 = u"""
-            Line #1
-            Line #2 is cool
-            Line #3
-        """
-        text_merged = u"""
-            Line #1
-<<<<<<<
-            Line #2 is hot
-=======
-            Line #2 is cool
->>>>>>>
-            Line #3
-        """
-        base = doc.commit(description="Commit #1", text=text1)
-        c1 = doc.commit(description="Commit #2", text=text2)
-        commits = doc.change_set.count()
-        c2 = doc.commit(description="Commit #3", text=text3, parent=base)
-        self.assertEqual(doc.change_set.count(), commits + 2,
-            u"Parallel commits should create an additional merge commit")
-        self.assertTextEqual(doc.materialize(), text_merged)
-
-
-    def test_multiple_parallel_commits(self):
-        text_a1 = u"""
-            Line #1
-
-            Line #2
-
-            Line #3
-            """
-        text_a2 = u"""
-            Line #1 *
-
-            Line #2
-
-            Line #3
-            """
-        text_b1 = u"""
-            Line #1
-
-            Line #2 **
-
-            Line #3
-            """
-        text_c1 = u"""
-            Line #1
-
-            Line #2
-
-            Line #3 ***
-            """
-        text_merged = u"""
-            Line #1 *
-
-            Line #2 **
-
-            Line #3 ***
-            """
-
-
-        doc = ADocument.objects.create()
-        c1 = doc.commit(description="Commit A1", text=text_a1)
-        c2 = doc.commit(description="Commit A2", text=text_a2, parent=c1)
-        c3 = doc.commit(description="Commit B1", text=text_b1, parent=c1)
-        c4 = doc.commit(description="Commit C1", text=text_c1, parent=c1)
-        self.assertTextEqual(doc.materialize(), text_merged)
-
-
-    def test_prepend_history(self):
-        doc1 = ADocument.objects.create()
-        doc2 = ADocument.objects.create()
-        doc1.commit(text='Commit 1')
-        doc2.commit(text='Commit 2')
-        doc2.prepend_history(doc1)
-        self.assertEqual(ADocument.objects.all().count(), 1)
-        self.assertTextEqual(doc2.at_revision(1).materialize(), 'Commit 1')
-        self.assertTextEqual(doc2.materialize(), 'Commit 2')
-
-    def test_prepend_to_self(self):
-        doc = ADocument.objects.create()
-        doc.commit(text='Commit 1')
-        with self.assertRaises(AssertionError):
-            doc.prepend_history(doc)
-        self.assertTextEqual(doc.materialize(), 'Commit 1')
-
diff --git a/apps/email_mangler/__init__.py b/apps/email_mangler/__init__.py
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/apps/email_mangler/locale/pl/LC_MESSAGES/django.mo b/apps/email_mangler/locale/pl/LC_MESSAGES/django.mo
deleted file mode 100644 (file)
index ed20bfb..0000000
Binary files a/apps/email_mangler/locale/pl/LC_MESSAGES/django.mo and /dev/null differ
diff --git a/apps/email_mangler/locale/pl/LC_MESSAGES/django.po b/apps/email_mangler/locale/pl/LC_MESSAGES/django.po
deleted file mode 100644 (file)
index 046b883..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-# 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: 2011-11-30 14:27+0100\n"
-"PO-Revision-Date: 2011-11-30 14:27+0100\n"
-"Last-Translator: Radek Czajka <radoslaw.czajka@nowoczesnapolska.org.pl>\n"
-"Language-Team: LANGUAGE <LL@li.org>\n"
-"Language: \n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n"
-
-#: templatetags/email.py:17
-msgid "at"
-msgstr "na"
-
-#: templatetags/email.py:18
-msgid "dot"
-msgstr "kropka"
-
diff --git a/apps/email_mangler/models.py b/apps/email_mangler/models.py
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/apps/email_mangler/templatetags/__init__.py b/apps/email_mangler/templatetags/__init__.py
deleted file mode 100755 (executable)
index e69de29..0000000
diff --git a/apps/email_mangler/templatetags/email.py b/apps/email_mangler/templatetags/email.py
deleted file mode 100755 (executable)
index 376117a..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-from django.utils.html import escape
-from django.utils.safestring import mark_safe
-from django.utils.translation import ugettext as _
-from django import template
-
-register = template.Library()
-
-
-@register.filter
-def email_link(email):
-    email_safe = escape(email)
-    try:
-        name, domain = email_safe.split('@', 1)
-    except ValueError:
-        return email
-
-    at = escape(_('at'))
-    dot = escape(_('dot'))
-    mangled = "%s %s %s" % (name, at, (' %s ' % dot).join(domain.split('.')))
-    return mark_safe("<a class='mangled' data-addr1='%(name)s' "
-        "data-addr2='%(domain)s'>%(mangled)s</a>" % {
-            'name': name.encode('rot13'),
-            'domain': domain.encode('rot13'),
-            'mangled': mangled,
-        })
diff --git a/apps/fileupload/__init__.py b/apps/fileupload/__init__.py
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/apps/fileupload/forms.py b/apps/fileupload/forms.py
deleted file mode 100644 (file)
index f5e1069..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-from django import forms
-
-class UploadForm(forms.Form):
-    files = forms.FileField()
diff --git a/apps/fileupload/locale/pl/LC_MESSAGES/django.mo b/apps/fileupload/locale/pl/LC_MESSAGES/django.mo
deleted file mode 100644 (file)
index 8fdb9c4..0000000
Binary files a/apps/fileupload/locale/pl/LC_MESSAGES/django.mo and /dev/null differ
diff --git a/apps/fileupload/locale/pl/LC_MESSAGES/django.po b/apps/fileupload/locale/pl/LC_MESSAGES/django.po
deleted file mode 100644 (file)
index a4b6099..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-# 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: 2013-03-07 16:27+0100\n"
-"PO-Revision-Date: 2013-03-07 16:27+0100\n"
-"Last-Translator: Radek Czajka <radoslaw.czajka@nowoczesnapolska.org.pl>\n"
-"Language-Team: LANGUAGE <LL@li.org>\n"
-"Language: \n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
-
-#: templates/fileupload/picture_form.html:18
-msgid "Browse:"
-msgstr "Przeglądanie:"
-
-#: templates/fileupload/picture_form.html:35
-msgid "Add files..."
-msgstr "Dodaj pliki..."
-
-#: templates/fileupload/picture_form.html:40
-msgid "Start upload"
-msgstr "Zacznij wysyłać"
-
-#: templates/fileupload/picture_form.html:44
-msgid "Cancel upload"
-msgstr "Anuluj wysyłanie"
-
-#: templates/fileupload/picture_form.html:48
-msgid "Delete"
-msgstr "Usuń"
-
diff --git a/apps/fileupload/models.py b/apps/fileupload/models.py
deleted file mode 100644 (file)
index 8b13789..0000000
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/apps/fileupload/static/fileupload/css/bootstrap-image-gallery.min.css b/apps/fileupload/static/fileupload/css/bootstrap-image-gallery.min.css
deleted file mode 100644 (file)
index a2bffbf..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-@charset 'UTF-8';
-.modal-gallery{width:auto;max-height:none;}
-.modal-gallery .modal-body{max-height:none;}
-.modal-gallery .modal-title{display:inline-block;max-height:54px;overflow:hidden;}
-.modal-gallery .modal-image{position:relative;margin:auto;min-width:128px;min-height:128px;overflow:hidden;cursor:pointer;}
-.modal-gallery .modal-image:hover:before,.modal-gallery .modal-image:hover:after{content:'‹';position:absolute;top:50%;left:15px;width:40px;height:40px;margin-top:-20px;font-size:60px;font-weight:100;line-height:30px;color:#ffffff;text-align:center;background:#222222;border:3px solid #ffffff;-webkit-border-radius:23px;-moz-border-radius:23px;border-radius:23px;opacity:0.5;filter:alpha(opacity=50);z-index:1;}
-.modal-gallery .modal-image:hover:after{content:'›';left:auto;right:15px;}
-.modal-single .modal-image:hover:before,.modal-single .modal-image:hover:after{display:none;}
-.modal-loading .modal-image{background:url(../img/loading.gif) center no-repeat;}
-.modal-gallery.fade .modal-image{-webkit-transition:width 0.15s ease, height 0.15s ease;-moz-transition:width 0.15s ease, height 0.15s ease;-ms-transition:width 0.15s ease, height 0.15s ease;-o-transition:width 0.15s ease, height 0.15s ease;transition:width 0.15s ease, height 0.15s ease;}
-.modal-gallery .modal-image *{position:absolute;top:0;opacity:0;filter:alpha(opacity=0);}
-.modal-gallery.fade .modal-image *{-webkit-transition:opacity 0.5s linear;-moz-transition:opacity 0.5s linear;-ms-transition:opacity 0.5s linear;-o-transition:opacity 0.5s linear;transition:opacity 0.5s linear;}
-.modal-gallery .modal-image *.in{opacity:1;filter:alpha(opacity=100);}
-.modal-fullscreen{border:none;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;background:transparent;overflow:hidden;}
-.modal-fullscreen.modal-loading{border:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;}
-.modal-fullscreen .modal-body{padding:0;}
-.modal-fullscreen .modal-header,.modal-fullscreen .modal-footer{position:absolute;top:0;right:0;left:0;background:transparent;border:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;opacity:0;z-index:2000;}
-.modal-fullscreen .modal-footer{top:auto;bottom:0;}
-.modal-fullscreen .close,.modal-fullscreen .modal-title{color:#fff;text-shadow:0 0 2px rgba(33, 33, 33, 0.8);}
-.modal-fullscreen .modal-header:hover,.modal-fullscreen .modal-footer:hover{opacity:1;}
-@media (max-width:480px){.modal-gallery .btn span{display:none;}}
diff --git a/apps/fileupload/static/fileupload/css/bootstrap.min.css b/apps/fileupload/static/fileupload/css/bootstrap.min.css
deleted file mode 100644 (file)
index 9999524..0000000
+++ /dev/null
@@ -1,766 +0,0 @@
-article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block;}
-audio,canvas,video{display:inline-block;*display:inline;*zoom:1;}
-audio:not([controls]){display:none;}
-a:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;}
-a:hover,a:active{outline:0;}
-sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline;}
-sup{top:-0.5em;}
-sub{bottom:-0.25em;}
-img{max-width:100%;vertical-align:middle;border:0;-ms-interpolation-mode:bicubic;}
-button,input,select,textarea{margin:0;font-size:100%;vertical-align:middle;}
-button,input{*overflow:visible;line-height:normal;}
-button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0;}
-button,input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button;}
-input[type="search"]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield;}
-input[type="search"]::-webkit-search-decoration,input[type="search"]::-webkit-search-cancel-button{-webkit-appearance:none;}
-textarea{overflow:auto;vertical-align:top;}
-.clearfix{*zoom:1;}
-.clearfix:before,.clearfix:after{display:table;content:"";}
-.clearfix:after{clear:both;}
-.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0;}
-.input-block-level{display:block;width:100%;min-height:28px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;}
-.row{margin-left:-20px;*zoom:1;}
-.row:before,.row:after{display:table;content:"";}
-.row:after{clear:both;}
-[class*="span"]{float:left;margin-left:20px;}
-.container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px;}
-.span12{width:940px;}
-.span11{width:860px;}
-.span10{width:780px;}
-.span9{width:700px;}
-.span8{width:620px;}
-.span7{width:540px;}
-.span6{width:460px;}
-.span5{width:380px;}
-.span4{width:300px;}
-.span3{width:220px;}
-.span2{width:140px;}
-.span1{width:60px;}
-.offset12{margin-left:980px;}
-.offset11{margin-left:900px;}
-.offset10{margin-left:820px;}
-.offset9{margin-left:740px;}
-.offset8{margin-left:660px;}
-.offset7{margin-left:580px;}
-.offset6{margin-left:500px;}
-.offset5{margin-left:420px;}
-.offset4{margin-left:340px;}
-.offset3{margin-left:260px;}
-.offset2{margin-left:180px;}
-.offset1{margin-left:100px;}
-.row-fluid{width:100%;*zoom:1;}
-.row-fluid:before,.row-fluid:after{display:table;content:"";}
-.row-fluid:after{clear:both;}
-.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:28px;margin-left:2.127659574%;*margin-left:2.0744680846382977%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;}
-.row-fluid [class*="span"]:first-child{margin-left:0;}
-.row-fluid .span12{width:99.99999998999999%;*width:99.94680850063828%;}
-.row-fluid .span11{width:91.489361693%;*width:91.4361702036383%;}
-.row-fluid .span10{width:82.97872339599999%;*width:82.92553190663828%;}
-.row-fluid .span9{width:74.468085099%;*width:74.4148936096383%;}
-.row-fluid .span8{width:65.95744680199999%;*width:65.90425531263828%;}
-.row-fluid .span7{width:57.446808505%;*width:57.3936170156383%;}
-.row-fluid .span6{width:48.93617020799999%;*width:48.88297871863829%;}
-.row-fluid .span5{width:40.425531911%;*width:40.3723404216383%;}
-.row-fluid .span4{width:31.914893614%;*width:31.8617021246383%;}
-.row-fluid .span3{width:23.404255317%;*width:23.3510638276383%;}
-.row-fluid .span2{width:14.89361702%;*width:14.8404255306383%;}
-.row-fluid .span1{width:6.382978723%;*width:6.329787233638298%;}
-.container{margin-right:auto;margin-left:auto;*zoom:1;}
-.container:before,.container:after{display:table;content:"";}
-.container:after{clear:both;}
-.container-fluid{padding-right:20px;padding-left:20px;*zoom:1;}
-.container-fluid:before,.container-fluid:after{display:table;content:"";}
-.container-fluid:after{clear:both;}
-p{margin:0 0 9px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;line-height:18px;}
-p small{font-size:11px;color:#999999;}
-.lead{margin-bottom:18px;font-size:20px;font-weight:200;line-height:27px;}
-.page-header{padding-bottom:17px;margin:18px 0;border-bottom:1px solid #eeeeee;}
-.page-header h1{line-height:1;}
-ul,ol{padding:0;margin:0 0 9px 25px;}
-ul ul,ul ol,ol ol,ol ul{margin-bottom:0;}
-ul{list-style:disc;}
-ol{list-style:decimal;}
-li{line-height:18px;}
-ul.unstyled,ol.unstyled{margin-left:0;list-style:none;}
-dl{margin-bottom:18px;}
-dt,dd{line-height:18px;}
-dt{font-weight:bold;line-height:17px;}
-dd{margin-left:9px;}
-.dl-horizontal dt{float:left;width:120px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap;}
-.dl-horizontal dd{margin-left:130px;}
-hr{margin:18px 0;border:0;border-top:1px solid #eeeeee;border-bottom:1px solid #ffffff;}
-strong{font-weight:bold;}
-em{font-style:italic;}
-.muted{color:#999999;}
-abbr[title]{cursor:help;border-bottom:1px dotted #ddd;}
-abbr.initialism{font-size:90%;text-transform:uppercase;}
-blockquote{padding:0 0 0 15px;margin:0 0 18px;border-left:5px solid #eeeeee;}
-blockquote p{margin-bottom:0;font-size:16px;font-weight:300;line-height:22.5px;}
-blockquote small{display:block;line-height:18px;color:#999999;}
-blockquote small:before{content:'\2014 \00A0';}
-blockquote.pull-right{float:right;padding-right:15px;padding-left:0;border-right:5px solid #eeeeee;border-left:0;}
-blockquote.pull-right p,blockquote.pull-right small{text-align:right;}
-q:before,q:after,blockquote:before,blockquote:after{content:"";}
-address{display:block;margin-bottom:18px;font-style:normal;line-height:18px;}
-small{font-size:100%;}
-cite{font-style:normal;}
-code,pre{padding:0 3px 2px;font-family:Menlo,Monaco,Consolas,"Courier New",monospace;font-size:12px;color:#333333;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;}
-code{padding:2px 4px;color:#d14;background-color:#f7f7f9;border:1px solid #e1e1e8;}
-pre{display:block;padding:8.5px;margin:0 0 9px;font-size:12.025px;line-height:18px;word-break:break-all;word-wrap:break-word;white-space:pre;white-space:pre-wrap;background-color:#f5f5f5;border:1px solid #ccc;border:1px solid rgba(0, 0, 0, 0.15);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}
-pre.prettyprint{margin-bottom:18px;}
-pre code{padding:0;color:inherit;background-color:transparent;border:0;}
-.pre-scrollable{max-height:340px;overflow-y:scroll;}
-form{margin:0 0 18px;}
-fieldset{padding:0;margin:0;border:0;}
-legend{display:block;width:100%;padding:0;margin-bottom:27px;font-size:19.5px;line-height:36px;color:#333333;border:0;border-bottom:1px solid #eee;}
-legend small{font-size:13.5px;color:#999999;}
-label,input,button,select,textarea{font-size:13px;font-weight:normal;line-height:18px;}
-input,button,select,textarea{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;}
-label{display:block;margin-bottom:5px;color:#333333;}
-input,textarea,select,.uneditable-input{display:inline-block;width:210px;height:18px;padding:4px;margin-bottom:9px;font-size:13px;line-height:18px;color:#555555;background-color:#ffffff;border:1px solid #cccccc;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;}
-.uneditable-textarea{width:auto;height:auto;}
-label input,label textarea,label select{display:block;}
-input[type="image"],input[type="checkbox"],input[type="radio"]{width:auto;height:auto;padding:0;margin:3px 0;*margin-top:0;line-height:normal;cursor:pointer;background-color:transparent;border:0 \9;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}
-input[type="image"]{border:0;}
-input[type="file"]{width:auto;padding:initial;line-height:initial;background-color:#ffffff;background-color:initial;border:initial;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;}
-input[type="button"],input[type="reset"],input[type="submit"]{width:auto;height:auto;}
-select,input[type="file"]{height:28px;*margin-top:4px;line-height:28px;}
-input[type="file"]{line-height:18px \9;}
-select{width:220px;background-color:#ffffff;}
-select[multiple],select[size]{height:auto;}
-input[type="image"]{-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;}
-textarea{height:auto;}
-input[type="hidden"]{display:none;}
-.radio,.checkbox{min-height:18px;padding-left:18px;}
-.radio input[type="radio"],.checkbox input[type="checkbox"]{float:left;margin-left:-18px;}
-.controls>.radio:first-child,.controls>.checkbox:first-child{padding-top:5px;}
-.radio.inline,.checkbox.inline{display:inline-block;padding-top:5px;margin-bottom:0;vertical-align:middle;}
-.radio.inline+.radio.inline,.checkbox.inline+.checkbox.inline{margin-left:10px;}
-input,textarea{-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);-webkit-transition:border linear 0.2s,box-shadow linear 0.2s;-moz-transition:border linear 0.2s,box-shadow linear 0.2s;-ms-transition:border linear 0.2s,box-shadow linear 0.2s;-o-transition:border linear 0.2s,box-shadow linear 0.2s;transition:border linear 0.2s,box-shadow linear 0.2s;}
-input:focus,textarea:focus{border-color:rgba(82, 168, 236, 0.8);outline:0;outline:thin dotted \9;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075),0 0 8px rgba(82, 168, 236, 0.6);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075),0 0 8px rgba(82, 168, 236, 0.6);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075),0 0 8px rgba(82, 168, 236, 0.6);}
-input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus,select:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;}
-.input-mini{width:60px;}
-.input-small{width:90px;}
-.input-medium{width:150px;}
-.input-large{width:210px;}
-.input-xlarge{width:270px;}
-.input-xxlarge{width:530px;}
-input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input[class*="span"],.row-fluid input[class*="span"],.row-fluid select[class*="span"],.row-fluid textarea[class*="span"],.row-fluid .uneditable-input[class*="span"]{float:none;margin-left:0;}
-input,textarea,.uneditable-input{margin-left:0;}
-input.span12,textarea.span12,.uneditable-input.span12{width:930px;}
-input.span11,textarea.span11,.uneditable-input.span11{width:850px;}
-input.span10,textarea.span10,.uneditable-input.span10{width:770px;}
-input.span9,textarea.span9,.uneditable-input.span9{width:690px;}
-input.span8,textarea.span8,.uneditable-input.span8{width:610px;}
-input.span7,textarea.span7,.uneditable-input.span7{width:530px;}
-input.span6,textarea.span6,.uneditable-input.span6{width:450px;}
-input.span5,textarea.span5,.uneditable-input.span5{width:370px;}
-input.span4,textarea.span4,.uneditable-input.span4{width:290px;}
-input.span3,textarea.span3,.uneditable-input.span3{width:210px;}
-input.span2,textarea.span2,.uneditable-input.span2{width:130px;}
-input.span1,textarea.span1,.uneditable-input.span1{width:50px;}
-input[disabled],select[disabled],textarea[disabled],input[readonly],select[readonly],textarea[readonly]{cursor:not-allowed;background-color:#eeeeee;border-color:#ddd;}
-input[type="radio"][disabled],input[type="checkbox"][disabled],input[type="radio"][readonly],input[type="checkbox"][readonly]{background-color:transparent;}
-.control-group.warning>label,.control-group.warning .help-block,.control-group.warning .help-inline{color:#c09853;}
-.control-group.warning input,.control-group.warning select,.control-group.warning textarea{color:#c09853;border-color:#c09853;}
-.control-group.warning input:focus,.control-group.warning select:focus,.control-group.warning textarea:focus{border-color:#a47e3c;-webkit-box-shadow:0 0 6px #dbc59e;-moz-box-shadow:0 0 6px #dbc59e;box-shadow:0 0 6px #dbc59e;}
-.control-group.warning .input-prepend .add-on,.control-group.warning .input-append .add-on{color:#c09853;background-color:#fcf8e3;border-color:#c09853;}
-.control-group.error>label,.control-group.error .help-block,.control-group.error .help-inline{color:#b94a48;}
-.control-group.error input,.control-group.error select,.control-group.error textarea{color:#b94a48;border-color:#b94a48;}
-.control-group.error input:focus,.control-group.error select:focus,.control-group.error textarea:focus{border-color:#953b39;-webkit-box-shadow:0 0 6px #d59392;-moz-box-shadow:0 0 6px #d59392;box-shadow:0 0 6px #d59392;}
-.control-group.error .input-prepend .add-on,.control-group.error .input-append .add-on{color:#b94a48;background-color:#f2dede;border-color:#b94a48;}
-.control-group.success>label,.control-group.success .help-block,.control-group.success .help-inline{color:#468847;}
-.control-group.success input,.control-group.success select,.control-group.success textarea{color:#468847;border-color:#468847;}
-.control-group.success input:focus,.control-group.success select:focus,.control-group.success textarea:focus{border-color:#356635;-webkit-box-shadow:0 0 6px #7aba7b;-moz-box-shadow:0 0 6px #7aba7b;box-shadow:0 0 6px #7aba7b;}
-.control-group.success .input-prepend .add-on,.control-group.success .input-append .add-on{color:#468847;background-color:#dff0d8;border-color:#468847;}
-input:focus:required:invalid,textarea:focus:required:invalid,select:focus:required:invalid{color:#b94a48;border-color:#ee5f5b;}
-input:focus:required:invalid:focus,textarea:focus:required:invalid:focus,select:focus:required:invalid:focus{border-color:#e9322d;-webkit-box-shadow:0 0 6px #f8b9b7;-moz-box-shadow:0 0 6px #f8b9b7;box-shadow:0 0 6px #f8b9b7;}
-.form-actions{padding:17px 20px 18px;margin-top:18px;margin-bottom:18px;background-color:#f5f5f5;border-top:1px solid #ddd;*zoom:1;}
-.form-actions:before,.form-actions:after{display:table;content:"";}
-.form-actions:after{clear:both;}
-.uneditable-input{overflow:hidden;white-space:nowrap;cursor:not-allowed;background-color:#ffffff;border-color:#eee;-webkit-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.025);-moz-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.025);box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.025);}
-:-moz-placeholder{color:#999999;}
-::-webkit-input-placeholder{color:#999999;}
-.help-block,.help-inline{color:#555555;}
-.help-block{display:block;margin-bottom:9px;}
-.help-inline{display:inline-block;*display:inline;padding-left:5px;vertical-align:middle;*zoom:1;}
-.input-prepend,.input-append{margin-bottom:5px;}
-.input-prepend input,.input-append input,.input-prepend select,.input-append select,.input-prepend .uneditable-input,.input-append .uneditable-input{position:relative;margin-bottom:0;*margin-left:0;vertical-align:middle;-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0;}
-.input-prepend input:focus,.input-append input:focus,.input-prepend select:focus,.input-append select:focus,.input-prepend .uneditable-input:focus,.input-append .uneditable-input:focus{z-index:2;}
-.input-prepend .uneditable-input,.input-append .uneditable-input{border-left-color:#ccc;}
-.input-prepend .add-on,.input-append .add-on{display:inline-block;width:auto;height:18px;min-width:16px;padding:4px 5px;font-weight:normal;line-height:18px;text-align:center;text-shadow:0 1px 0 #ffffff;vertical-align:middle;background-color:#eeeeee;border:1px solid #ccc;}
-.input-prepend .add-on,.input-append .add-on,.input-prepend .btn,.input-append .btn{margin-left:-1px;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}
-.input-prepend .active,.input-append .active{background-color:#a9dba9;border-color:#46a546;}
-.input-prepend .add-on,.input-prepend .btn{margin-right:-1px;}
-.input-prepend .add-on:first-child,.input-prepend .btn:first-child{-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;}
-.input-append input,.input-append select,.input-append .uneditable-input{-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;}
-.input-append .uneditable-input{border-right-color:#ccc;border-left-color:#eee;}
-.input-append .add-on:last-child,.input-append .btn:last-child{-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0;}
-.input-prepend.input-append input,.input-prepend.input-append select,.input-prepend.input-append .uneditable-input{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}
-.input-prepend.input-append .add-on:first-child,.input-prepend.input-append .btn:first-child{margin-right:-1px;-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;}
-.input-prepend.input-append .add-on:last-child,.input-prepend.input-append .btn:last-child{margin-left:-1px;-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0;}
-.search-query{padding-right:14px;padding-right:4px \9;padding-left:14px;padding-left:4px \9;margin-bottom:0;-webkit-border-radius:14px;-moz-border-radius:14px;border-radius:14px;}
-.form-search input,.form-inline input,.form-horizontal input,.form-search textarea,.form-inline textarea,.form-horizontal textarea,.form-search select,.form-inline select,.form-horizontal select,.form-search .help-inline,.form-inline .help-inline,.form-horizontal .help-inline,.form-search .uneditable-input,.form-inline .uneditable-input,.form-horizontal .uneditable-input,.form-search .input-prepend,.form-inline .input-prepend,.form-horizontal .input-prepend,.form-search .input-append,.form-inline .input-append,.form-horizontal .input-append{display:inline-block;*display:inline;margin-bottom:0;*zoom:1;}
-.form-search .hide,.form-inline .hide,.form-horizontal .hide{display:none;}
-.form-search label,.form-inline label{display:inline-block;}
-.form-search .input-append,.form-inline .input-append,.form-search .input-prepend,.form-inline .input-prepend{margin-bottom:0;}
-.form-search .radio,.form-search .checkbox,.form-inline .radio,.form-inline .checkbox{padding-left:0;margin-bottom:0;vertical-align:middle;}
-.form-search .radio input[type="radio"],.form-search .checkbox input[type="checkbox"],.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{float:left;margin-right:3px;margin-left:0;}
-.control-group{margin-bottom:9px;}
-legend+.control-group{margin-top:18px;-webkit-margin-top-collapse:separate;}
-.form-horizontal .control-group{margin-bottom:18px;*zoom:1;}
-.form-horizontal .control-group:before,.form-horizontal .control-group:after{display:table;content:"";}
-.form-horizontal .control-group:after{clear:both;}
-.form-horizontal .control-label{float:left;width:140px;padding-top:5px;text-align:right;}
-.form-horizontal .controls{*display:inline-block;*padding-left:20px;margin-left:160px;*margin-left:0;}
-.form-horizontal .controls:first-child{*padding-left:160px;}
-.form-horizontal .help-block{margin-top:9px;margin-bottom:0;}
-.form-horizontal .form-actions{padding-left:160px;}
-table{max-width:100%;background-color:transparent;border-collapse:collapse;border-spacing:0;}
-.table{width:100%;margin-bottom:18px;}
-.table th,.table td{padding:8px;line-height:18px;text-align:left;vertical-align:top;border-top:1px solid #dddddd;}
-.table th{font-weight:bold;}
-.table thead th{vertical-align:bottom;}
-.table caption+thead tr:first-child th,.table caption+thead tr:first-child td,.table colgroup+thead tr:first-child th,.table colgroup+thead tr:first-child td,.table thead:first-child tr:first-child th,.table thead:first-child tr:first-child td{border-top:0;}
-.table tbody+tbody{border-top:2px solid #dddddd;}
-.table-condensed th,.table-condensed td{padding:4px 5px;}
-.table-bordered{border:1px solid #dddddd;border-collapse:separate;*border-collapse:collapsed;border-left:0;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}
-.table-bordered th,.table-bordered td{border-left:1px solid #dddddd;}
-.table-bordered caption+thead tr:first-child th,.table-bordered caption+tbody tr:first-child th,.table-bordered caption+tbody tr:first-child td,.table-bordered colgroup+thead tr:first-child th,.table-bordered colgroup+tbody tr:first-child th,.table-bordered colgroup+tbody tr:first-child td,.table-bordered thead:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child td{border-top:0;}
-.table-bordered thead:first-child tr:first-child th:first-child,.table-bordered tbody:first-child tr:first-child td:first-child{-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topleft:4px;}
-.table-bordered thead:first-child tr:first-child th:last-child,.table-bordered tbody:first-child tr:first-child td:last-child{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-topright:4px;}
-.table-bordered thead:last-child tr:last-child th:first-child,.table-bordered tbody:last-child tr:last-child td:first-child{-webkit-border-radius:0 0 0 4px;-moz-border-radius:0 0 0 4px;border-radius:0 0 0 4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-moz-border-radius-bottomleft:4px;}
-.table-bordered thead:last-child tr:last-child th:last-child,.table-bordered tbody:last-child tr:last-child td:last-child{-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-bottomright:4px;}
-.table-striped tbody tr:nth-child(odd) td,.table-striped tbody tr:nth-child(odd) th{background-color:#f9f9f9;}
-.table tbody tr:hover td,.table tbody tr:hover th{background-color:#f5f5f5;}
-table .span1{float:none;width:44px;margin-left:0;}
-table .span2{float:none;width:124px;margin-left:0;}
-table .span3{float:none;width:204px;margin-left:0;}
-table .span4{float:none;width:284px;margin-left:0;}
-table .span5{float:none;width:364px;margin-left:0;}
-table .span6{float:none;width:444px;margin-left:0;}
-table .span7{float:none;width:524px;margin-left:0;}
-table .span8{float:none;width:604px;margin-left:0;}
-table .span9{float:none;width:684px;margin-left:0;}
-table .span10{float:none;width:764px;margin-left:0;}
-table .span11{float:none;width:844px;margin-left:0;}
-table .span12{float:none;width:924px;margin-left:0;}
-table .span13{float:none;width:1004px;margin-left:0;}
-table .span14{float:none;width:1084px;margin-left:0;}
-table .span15{float:none;width:1164px;margin-left:0;}
-table .span16{float:none;width:1244px;margin-left:0;}
-table .span17{float:none;width:1324px;margin-left:0;}
-table .span18{float:none;width:1404px;margin-left:0;}
-table .span19{float:none;width:1484px;margin-left:0;}
-table .span20{float:none;width:1564px;margin-left:0;}
-table .span21{float:none;width:1644px;margin-left:0;}
-table .span22{float:none;width:1724px;margin-left:0;}
-table .span23{float:none;width:1804px;margin-left:0;}
-table .span24{float:none;width:1884px;margin-left:0;}
-[class^="icon-"],[class*=" icon-"]{display:inline-block;width:14px;height:14px;*margin-right:.3em;line-height:14px;vertical-align:text-top;background-image:url("../img/glyphicons-halflings.png");background-position:14px 14px;background-repeat:no-repeat;}
-[class^="icon-"]:last-child,[class*=" icon-"]:last-child{*margin-left:0;}
-.icon-white{background-image:url("../img/glyphicons-halflings-white.png");}
-.icon-glass{background-position:0 0;}
-.icon-music{background-position:-24px 0;}
-.icon-search{background-position:-48px 0;}
-.icon-envelope{background-position:-72px 0;}
-.icon-heart{background-position:-96px 0;}
-.icon-star{background-position:-120px 0;}
-.icon-star-empty{background-position:-144px 0;}
-.icon-user{background-position:-168px 0;}
-.icon-film{background-position:-192px 0;}
-.icon-th-large{background-position:-216px 0;}
-.icon-th{background-position:-240px 0;}
-.icon-th-list{background-position:-264px 0;}
-.icon-ok{background-position:-288px 0;}
-.icon-remove{background-position:-312px 0;}
-.icon-zoom-in{background-position:-336px 0;}
-.icon-zoom-out{background-position:-360px 0;}
-.icon-off{background-position:-384px 0;}
-.icon-signal{background-position:-408px 0;}
-.icon-cog{background-position:-432px 0;}
-.icon-trash{background-position:-456px 0;}
-.icon-home{background-position:0 -24px;}
-.icon-file{background-position:-24px -24px;}
-.icon-time{background-position:-48px -24px;}
-.icon-road{background-position:-72px -24px;}
-.icon-download-alt{background-position:-96px -24px;}
-.icon-download{background-position:-120px -24px;}
-.icon-upload{background-position:-144px -24px;}
-.icon-inbox{background-position:-168px -24px;}
-.icon-play-circle{background-position:-192px -24px;}
-.icon-repeat{background-position:-216px -24px;}
-.icon-refresh{background-position:-240px -24px;}
-.icon-list-alt{background-position:-264px -24px;}
-.icon-lock{background-position:-287px -24px;}
-.icon-flag{background-position:-312px -24px;}
-.icon-headphones{background-position:-336px -24px;}
-.icon-volume-off{background-position:-360px -24px;}
-.icon-volume-down{background-position:-384px -24px;}
-.icon-volume-up{background-position:-408px -24px;}
-.icon-qrcode{background-position:-432px -24px;}
-.icon-barcode{background-position:-456px -24px;}
-.icon-tag{background-position:0 -48px;}
-.icon-tags{background-position:-25px -48px;}
-.icon-book{background-position:-48px -48px;}
-.icon-bookmark{background-position:-72px -48px;}
-.icon-print{background-position:-96px -48px;}
-.icon-camera{background-position:-120px -48px;}
-.icon-font{background-position:-144px -48px;}
-.icon-bold{background-position:-167px -48px;}
-.icon-italic{background-position:-192px -48px;}
-.icon-text-height{background-position:-216px -48px;}
-.icon-text-width{background-position:-240px -48px;}
-.icon-align-left{background-position:-264px -48px;}
-.icon-align-center{background-position:-288px -48px;}
-.icon-align-right{background-position:-312px -48px;}
-.icon-align-justify{background-position:-336px -48px;}
-.icon-list{background-position:-360px -48px;}
-.icon-indent-left{background-position:-384px -48px;}
-.icon-indent-right{background-position:-408px -48px;}
-.icon-facetime-video{background-position:-432px -48px;}
-.icon-picture{background-position:-456px -48px;}
-.icon-pencil{background-position:0 -72px;}
-.icon-map-marker{background-position:-24px -72px;}
-.icon-adjust{background-position:-48px -72px;}
-.icon-tint{background-position:-72px -72px;}
-.icon-edit{background-position:-96px -72px;}
-.icon-share{background-position:-120px -72px;}
-.icon-check{background-position:-144px -72px;}
-.icon-move{background-position:-168px -72px;}
-.icon-step-backward{background-position:-192px -72px;}
-.icon-fast-backward{background-position:-216px -72px;}
-.icon-backward{background-position:-240px -72px;}
-.icon-play{background-position:-264px -72px;}
-.icon-pause{background-position:-288px -72px;}
-.icon-stop{background-position:-312px -72px;}
-.icon-forward{background-position:-336px -72px;}
-.icon-fast-forward{background-position:-360px -72px;}
-.icon-step-forward{background-position:-384px -72px;}
-.icon-eject{background-position:-408px -72px;}
-.icon-chevron-left{background-position:-432px -72px;}
-.icon-chevron-right{background-position:-456px -72px;}
-.icon-plus-sign{background-position:0 -96px;}
-.icon-minus-sign{background-position:-24px -96px;}
-.icon-remove-sign{background-position:-48px -96px;}
-.icon-ok-sign{background-position:-72px -96px;}
-.icon-question-sign{background-position:-96px -96px;}
-.icon-info-sign{background-position:-120px -96px;}
-.icon-screenshot{background-position:-144px -96px;}
-.icon-remove-circle{background-position:-168px -96px;}
-.icon-ok-circle{background-position:-192px -96px;}
-.icon-ban-circle{background-position:-216px -96px;}
-.icon-arrow-left{background-position:-240px -96px;}
-.icon-arrow-right{background-position:-264px -96px;}
-.icon-arrow-up{background-position:-289px -96px;}
-.icon-arrow-down{background-position:-312px -96px;}
-.icon-share-alt{background-position:-336px -96px;}
-.icon-resize-full{background-position:-360px -96px;}
-.icon-resize-small{background-position:-384px -96px;}
-.icon-plus{background-position:-408px -96px;}
-.icon-minus{background-position:-433px -96px;}
-.icon-asterisk{background-position:-456px -96px;}
-.icon-exclamation-sign{background-position:0 -120px;}
-.icon-gift{background-position:-24px -120px;}
-.icon-leaf{background-position:-48px -120px;}
-.icon-fire{background-position:-72px -120px;}
-.icon-eye-open{background-position:-96px -120px;}
-.icon-eye-close{background-position:-120px -120px;}
-.icon-warning-sign{background-position:-144px -120px;}
-.icon-plane{background-position:-168px -120px;}
-.icon-calendar{background-position:-192px -120px;}
-.icon-random{background-position:-216px -120px;}
-.icon-comment{background-position:-240px -120px;}
-.icon-magnet{background-position:-264px -120px;}
-.icon-chevron-up{background-position:-288px -120px;}
-.icon-chevron-down{background-position:-313px -119px;}
-.icon-retweet{background-position:-336px -120px;}
-.icon-shopping-cart{background-position:-360px -120px;}
-.icon-folder-close{background-position:-384px -120px;}
-.icon-folder-open{background-position:-408px -120px;}
-.icon-resize-vertical{background-position:-432px -119px;}
-.icon-resize-horizontal{background-position:-456px -118px;}
-.icon-hdd{background-position:0 -144px;}
-.icon-bullhorn{background-position:-24px -144px;}
-.icon-bell{background-position:-48px -144px;}
-.icon-certificate{background-position:-72px -144px;}
-.icon-thumbs-up{background-position:-96px -144px;}
-.icon-thumbs-down{background-position:-120px -144px;}
-.icon-hand-right{background-position:-144px -144px;}
-.icon-hand-left{background-position:-168px -144px;}
-.icon-hand-up{background-position:-192px -144px;}
-.icon-hand-down{background-position:-216px -144px;}
-.icon-circle-arrow-right{background-position:-240px -144px;}
-.icon-circle-arrow-left{background-position:-264px -144px;}
-.icon-circle-arrow-up{background-position:-288px -144px;}
-.icon-circle-arrow-down{background-position:-312px -144px;}
-.icon-globe{background-position:-336px -144px;}
-.icon-wrench{background-position:-360px -144px;}
-.icon-tasks{background-position:-384px -144px;}
-.icon-filter{background-position:-408px -144px;}
-.icon-briefcase{background-position:-432px -144px;}
-.icon-fullscreen{background-position:-456px -144px;}
-.dropup,.dropdown{position:relative;}
-.dropdown-toggle{*margin-bottom:-3px;}
-.dropdown-toggle:active,.open .dropdown-toggle{outline:0;}
-.caret{display:inline-block;width:0;height:0;vertical-align:top;border-top:4px solid #000000;border-right:4px solid transparent;border-left:4px solid transparent;content:"";opacity:0.3;filter:alpha(opacity=30);}
-.dropdown .caret{margin-top:8px;margin-left:2px;}
-.dropdown:hover .caret,.open .caret{opacity:1;filter:alpha(opacity=100);}
-.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:4px 0;margin:1px 0 0;list-style:none;background-color:#ffffff;border:1px solid #ccc;border:1px solid rgba(0, 0, 0, 0.2);*border-right-width:2px;*border-bottom-width:2px;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;-webkit-box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);-moz-box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box;}
-.dropdown-menu.pull-right{right:0;left:auto;}
-.dropdown-menu .divider{*width:100%;height:1px;margin:8px 1px;*margin:-5px 0 5px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #ffffff;}
-.dropdown-menu a{display:block;padding:3px 15px;clear:both;font-weight:normal;line-height:18px;color:#333333;white-space:nowrap;}
-.dropdown-menu li>a:hover,.dropdown-menu .active>a,.dropdown-menu .active>a:hover{color:#ffffff;text-decoration:none;background-color:#0088cc;}
-.open{*z-index:1000;}
-.open .dropdown-menu{display:block;}
-.pull-right .dropdown-menu{right:0;left:auto;}
-.dropup .caret,.navbar-fixed-bottom .dropdown .caret{border-top:0;border-bottom:4px solid #000000;content:"\2191";}
-.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:1px;}
-.typeahead{margin-top:2px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}
-.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #eee;border:1px solid rgba(0, 0, 0, 0.05);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05);}
-.well blockquote{border-color:#ddd;border-color:rgba(0, 0, 0, 0.15);}
-.well-large{padding:24px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;}
-.well-small{padding:9px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;}
-.fade{opacity:0;-webkit-transition:opacity 0.15s linear;-moz-transition:opacity 0.15s linear;-ms-transition:opacity 0.15s linear;-o-transition:opacity 0.15s linear;transition:opacity 0.15s linear;}
-.fade.in{opacity:1;}
-.collapse{position:relative;height:0;overflow:hidden;-webkit-transition:height 0.35s ease;-moz-transition:height 0.35s ease;-ms-transition:height 0.35s ease;-o-transition:height 0.35s ease;transition:height 0.35s ease;}
-.collapse.in{height:auto;}
-.close{float:right;font-size:20px;font-weight:bold;line-height:18px;color:#000000;text-shadow:0 1px 0 #ffffff;opacity:0.2;filter:alpha(opacity=20);}
-.close:hover{color:#000000;text-decoration:none;cursor:pointer;opacity:0.4;filter:alpha(opacity=40);}
-button.close{padding:0;cursor:pointer;background-color:transparent;border:0;-webkit-appearance:none;}
-.btn{display:inline-block;*display:inline;padding:4px 10px 4px;margin-bottom:0;*margin-left:.3em;font-size:13px;line-height:18px;*line-height:20px;color:#333333;text-align:center;text-shadow:0 1px 1px rgba(255, 255, 255, 0.75);vertical-align:middle;cursor:pointer;background-color:#f5f5f5;*background-color:#e6e6e6;background-image:-ms-linear-gradient(top, #ffffff, #e6e6e6);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#e6e6e6));background-image:-webkit-linear-gradient(top, #ffffff, #e6e6e6);background-image:-o-linear-gradient(top, #ffffff, #e6e6e6);background-image:linear-gradient(top, #ffffff, #e6e6e6);background-image:-moz-linear-gradient(top, #ffffff, #e6e6e6);background-repeat:repeat-x;border:1px solid #cccccc;*border:0;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);border-color:#e6e6e6 #e6e6e6 #bfbfbf;border-bottom-color:#b3b3b3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ffffff', endColorstr='#e6e6e6', GradientType=0);filter:progid:dximagetransform.microsoft.gradient(enabled=false);*zoom:1;-webkit-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);}
-.btn:hover,.btn:active,.btn.active,.btn.disabled,.btn[disabled]{background-color:#e6e6e6;*background-color:#d9d9d9;}
-.btn:active,.btn.active{background-color:#cccccc \9;}
-.btn:first-child{*margin-left:0;}
-.btn:hover{color:#333333;text-decoration:none;background-color:#e6e6e6;*background-color:#d9d9d9;background-position:0 -15px;-webkit-transition:background-position 0.1s linear;-moz-transition:background-position 0.1s linear;-ms-transition:background-position 0.1s linear;-o-transition:background-position 0.1s linear;transition:background-position 0.1s linear;}
-.btn:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;}
-.btn.active,.btn:active{background-color:#e6e6e6;background-color:#d9d9d9 \9;background-image:none;outline:0;-webkit-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);}
-.btn.disabled,.btn[disabled]{cursor:default;background-color:#e6e6e6;background-image:none;opacity:0.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;}
-.btn-large{padding:9px 14px;font-size:15px;line-height:normal;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;}
-.btn-large [class^="icon-"]{margin-top:1px;}
-.btn-small{padding:5px 9px;font-size:11px;line-height:16px;}
-.btn-small [class^="icon-"]{margin-top:-1px;}
-.btn-mini{padding:2px 6px;font-size:11px;line-height:14px;}
-.btn-primary,.btn-primary:hover,.btn-warning,.btn-warning:hover,.btn-danger,.btn-danger:hover,.btn-success,.btn-success:hover,.btn-info,.btn-info:hover,.btn-inverse,.btn-inverse:hover{color:#ffffff;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);}
-.btn-primary.active,.btn-warning.active,.btn-danger.active,.btn-success.active,.btn-info.active,.btn-inverse.active{color:rgba(255, 255, 255, 0.75);}
-.btn{border-color:#ccc;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);}
-.btn-primary{background-color:#0074cc;*background-color:#0055cc;background-image:-ms-linear-gradient(top, #0088cc, #0055cc);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0055cc));background-image:-webkit-linear-gradient(top, #0088cc, #0055cc);background-image:-o-linear-gradient(top, #0088cc, #0055cc);background-image:-moz-linear-gradient(top, #0088cc, #0055cc);background-image:linear-gradient(top, #0088cc, #0055cc);background-repeat:repeat-x;border-color:#0055cc #0055cc #003580;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#0088cc', endColorstr='#0055cc', GradientType=0);filter:progid:dximagetransform.microsoft.gradient(enabled=false);}
-.btn-primary:hover,.btn-primary:active,.btn-primary.active,.btn-primary.disabled,.btn-primary[disabled]{background-color:#0055cc;*background-color:#004ab3;}
-.btn-primary:active,.btn-primary.active{background-color:#004099 \9;}
-.btn-warning{background-color:#faa732;*background-color:#f89406;background-image:-ms-linear-gradient(top, #fbb450, #f89406);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406));background-image:-webkit-linear-gradient(top, #fbb450, #f89406);background-image:-o-linear-gradient(top, #fbb450, #f89406);background-image:-moz-linear-gradient(top, #fbb450, #f89406);background-image:linear-gradient(top, #fbb450, #f89406);background-repeat:repeat-x;border-color:#f89406 #f89406 #ad6704;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#fbb450', endColorstr='#f89406', GradientType=0);filter:progid:dximagetransform.microsoft.gradient(enabled=false);}
-.btn-warning:hover,.btn-warning:active,.btn-warning.active,.btn-warning.disabled,.btn-warning[disabled]{background-color:#f89406;*background-color:#df8505;}
-.btn-warning:active,.btn-warning.active{background-color:#c67605 \9;}
-.btn-danger{background-color:#da4f49;*background-color:#bd362f;background-image:-ms-linear-gradient(top, #ee5f5b, #bd362f);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#bd362f));background-image:-webkit-linear-gradient(top, #ee5f5b, #bd362f);background-image:-o-linear-gradient(top, #ee5f5b, #bd362f);background-image:-moz-linear-gradient(top, #ee5f5b, #bd362f);background-image:linear-gradient(top, #ee5f5b, #bd362f);background-repeat:repeat-x;border-color:#bd362f #bd362f #802420;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ee5f5b', endColorstr='#bd362f', GradientType=0);filter:progid:dximagetransform.microsoft.gradient(enabled=false);}
-.btn-danger:hover,.btn-danger:active,.btn-danger.active,.btn-danger.disabled,.btn-danger[disabled]{background-color:#bd362f;*background-color:#a9302a;}
-.btn-danger:active,.btn-danger.active{background-color:#942a25 \9;}
-.btn-success{background-color:#5bb75b;*background-color:#51a351;background-image:-ms-linear-gradient(top, #62c462, #51a351);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#51a351));background-image:-webkit-linear-gradient(top, #62c462, #51a351);background-image:-o-linear-gradient(top, #62c462, #51a351);background-image:-moz-linear-gradient(top, #62c462, #51a351);background-image:linear-gradient(top, #62c462, #51a351);background-repeat:repeat-x;border-color:#51a351 #51a351 #387038;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#62c462', endColorstr='#51a351', GradientType=0);filter:progid:dximagetransform.microsoft.gradient(enabled=false);}
-.btn-success:hover,.btn-success:active,.btn-success.active,.btn-success.disabled,.btn-success[disabled]{background-color:#51a351;*background-color:#499249;}
-.btn-success:active,.btn-success.active{background-color:#408140 \9;}
-.btn-info{background-color:#49afcd;*background-color:#2f96b4;background-image:-ms-linear-gradient(top, #5bc0de, #2f96b4);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#2f96b4));background-image:-webkit-linear-gradient(top, #5bc0de, #2f96b4);background-image:-o-linear-gradient(top, #5bc0de, #2f96b4);background-image:-moz-linear-gradient(top, #5bc0de, #2f96b4);background-image:linear-gradient(top, #5bc0de, #2f96b4);background-repeat:repeat-x;border-color:#2f96b4 #2f96b4 #1f6377;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#5bc0de', endColorstr='#2f96b4', GradientType=0);filter:progid:dximagetransform.microsoft.gradient(enabled=false);}
-.btn-info:hover,.btn-info:active,.btn-info.active,.btn-info.disabled,.btn-info[disabled]{background-color:#2f96b4;*background-color:#2a85a0;}
-.btn-info:active,.btn-info.active{background-color:#24748c \9;}
-.btn-inverse{background-color:#414141;*background-color:#222222;background-image:-ms-linear-gradient(top, #555555, #222222);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#555555), to(#222222));background-image:-webkit-linear-gradient(top, #555555, #222222);background-image:-o-linear-gradient(top, #555555, #222222);background-image:-moz-linear-gradient(top, #555555, #222222);background-image:linear-gradient(top, #555555, #222222);background-repeat:repeat-x;border-color:#222222 #222222 #000000;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#555555', endColorstr='#222222', GradientType=0);filter:progid:dximagetransform.microsoft.gradient(enabled=false);}
-.btn-inverse:hover,.btn-inverse:active,.btn-inverse.active,.btn-inverse.disabled,.btn-inverse[disabled]{background-color:#222222;*background-color:#151515;}
-.btn-inverse:active,.btn-inverse.active{background-color:#080808 \9;}
-button.btn,input[type="submit"].btn{*padding-top:2px;*padding-bottom:2px;}
-button.btn::-moz-focus-inner,input[type="submit"].btn::-moz-focus-inner{padding:0;border:0;}
-button.btn.btn-large,input[type="submit"].btn.btn-large{*padding-top:7px;*padding-bottom:7px;}
-button.btn.btn-small,input[type="submit"].btn.btn-small{*padding-top:3px;*padding-bottom:3px;}
-button.btn.btn-mini,input[type="submit"].btn.btn-mini{*padding-top:1px;*padding-bottom:1px;}
-.btn-group{position:relative;*margin-left:.3em;*zoom:1;}
-.btn-group:before,.btn-group:after{display:table;content:"";}
-.btn-group:after{clear:both;}
-.btn-group:first-child{*margin-left:0;}
-.btn-group+.btn-group{margin-left:5px;}
-.btn-toolbar{margin-top:9px;margin-bottom:9px;}
-.btn-toolbar .btn-group{display:inline-block;*display:inline;*zoom:1;}
-.btn-group>.btn{position:relative;float:left;margin-left:-1px;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}
-.btn-group>.btn:first-child{margin-left:0;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-bottomleft:4px;-moz-border-radius-topleft:4px;}
-.btn-group>.btn:last-child,.btn-group>.dropdown-toggle{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-topright:4px;-moz-border-radius-bottomright:4px;}
-.btn-group>.btn.large:first-child{margin-left:0;-webkit-border-bottom-left-radius:6px;border-bottom-left-radius:6px;-webkit-border-top-left-radius:6px;border-top-left-radius:6px;-moz-border-radius-bottomleft:6px;-moz-border-radius-topleft:6px;}
-.btn-group>.btn.large:last-child,.btn-group>.large.dropdown-toggle{-webkit-border-top-right-radius:6px;border-top-right-radius:6px;-webkit-border-bottom-right-radius:6px;border-bottom-right-radius:6px;-moz-border-radius-topright:6px;-moz-border-radius-bottomright:6px;}
-.btn-group>.btn:hover,.btn-group>.btn:focus,.btn-group>.btn:active,.btn-group>.btn.active{z-index:2;}
-.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0;}
-.btn-group>.dropdown-toggle{*padding-top:4px;padding-right:8px;*padding-bottom:4px;padding-left:8px;-webkit-box-shadow:inset 1px 0 0 rgba(255, 255, 255, 0.125),inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 1px 0 0 rgba(255, 255, 255, 0.125),inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 1px 0 0 rgba(255, 255, 255, 0.125),inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);}
-.btn-group>.btn-mini.dropdown-toggle{padding-right:5px;padding-left:5px;}
-.btn-group>.btn-small.dropdown-toggle{*padding-top:4px;*padding-bottom:4px;}
-.btn-group>.btn-large.dropdown-toggle{padding-right:12px;padding-left:12px;}
-.btn-group.open .dropdown-toggle{background-image:none;-webkit-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);}
-.btn-group.open .btn.dropdown-toggle{background-color:#e6e6e6;}
-.btn-group.open .btn-primary.dropdown-toggle{background-color:#0055cc;}
-.btn-group.open .btn-warning.dropdown-toggle{background-color:#f89406;}
-.btn-group.open .btn-danger.dropdown-toggle{background-color:#bd362f;}
-.btn-group.open .btn-success.dropdown-toggle{background-color:#51a351;}
-.btn-group.open .btn-info.dropdown-toggle{background-color:#2f96b4;}
-.btn-group.open .btn-inverse.dropdown-toggle{background-color:#222222;}
-.btn .caret{margin-top:7px;margin-left:0;}
-.btn:hover .caret,.open.btn-group .caret{opacity:1;filter:alpha(opacity=100);}
-.btn-mini .caret{margin-top:5px;}
-.btn-small .caret{margin-top:6px;}
-.btn-large .caret{margin-top:6px;border-top-width:5px;border-right-width:5px;border-left-width:5px;}
-.dropup .btn-large .caret{border-top:0;border-bottom:5px solid #000000;}
-.btn-primary .caret,.btn-warning .caret,.btn-danger .caret,.btn-info .caret,.btn-success .caret,.btn-inverse .caret{border-top-color:#ffffff;border-bottom-color:#ffffff;opacity:0.75;filter:alpha(opacity=75);}
-.alert{padding:8px 35px 8px 14px;margin-bottom:18px;color:#c09853;text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);background-color:#fcf8e3;border:1px solid #fbeed5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}
-.alert-heading{color:inherit;}
-.alert .close{position:relative;top:-2px;right:-21px;line-height:18px;}
-.alert-success{color:#468847;background-color:#dff0d8;border-color:#d6e9c6;}
-.alert-danger,.alert-error{color:#b94a48;background-color:#f2dede;border-color:#eed3d7;}
-.alert-info{color:#3a87ad;background-color:#d9edf7;border-color:#bce8f1;}
-.alert-block{padding-top:14px;padding-bottom:14px;}
-.alert-block>p,.alert-block>ul{margin-bottom:0;}
-.alert-block p+p{margin-top:5px;}
-.nav{margin-bottom:18px;margin-left:0;list-style:none;}
-.nav>li>a{display:block;}
-.nav>li>a:hover{text-decoration:none;background-color:#eeeeee;}
-.nav>.pull-right{float:right;}
-.nav .nav-header{display:block;padding:3px 15px;font-size:11px;font-weight:bold;line-height:18px;color:#999999;text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);text-transform:uppercase;}
-.nav li+.nav-header{margin-top:9px;}
-.nav-list{padding-right:15px;padding-left:15px;margin-bottom:0;}
-.nav-list>li>a,.nav-list .nav-header{margin-right:-15px;margin-left:-15px;text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);}
-.nav-list>li>a{padding:3px 15px;}
-.nav-list>.active>a,.nav-list>.active>a:hover{color:#ffffff;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.2);background-color:#0088cc;}
-.nav-list [class^="icon-"]{margin-right:2px;}
-.nav-list .divider{*width:100%;height:1px;margin:8px 1px;*margin:-5px 0 5px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #ffffff;}
-.nav-tabs,.nav-pills{*zoom:1;}
-.nav-tabs:before,.nav-pills:before,.nav-tabs:after,.nav-pills:after{display:table;content:"";}
-.nav-tabs:after,.nav-pills:after{clear:both;}
-.nav-tabs>li,.nav-pills>li{float:left;}
-.nav-tabs>li>a,.nav-pills>li>a{padding-right:12px;padding-left:12px;margin-right:2px;line-height:14px;}
-.nav-tabs{border-bottom:1px solid #ddd;}
-.nav-tabs>li{margin-bottom:-1px;}
-.nav-tabs>li>a{padding-top:8px;padding-bottom:8px;line-height:18px;border:1px solid transparent;-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0;}
-.nav-tabs>li>a:hover{border-color:#eeeeee #eeeeee #dddddd;}
-.nav-tabs>.active>a,.nav-tabs>.active>a:hover{color:#555555;cursor:default;background-color:#ffffff;border:1px solid #ddd;border-bottom-color:transparent;}
-.nav-pills>li>a{padding-top:8px;padding-bottom:8px;margin-top:2px;margin-bottom:2px;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;}
-.nav-pills>.active>a,.nav-pills>.active>a:hover{color:#ffffff;background-color:#0088cc;}
-.nav-stacked>li{float:none;}
-.nav-stacked>li>a{margin-right:0;}
-.nav-tabs.nav-stacked{border-bottom:0;}
-.nav-tabs.nav-stacked>li>a{border:1px solid #ddd;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}
-.nav-tabs.nav-stacked>li:first-child>a{-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0;}
-.nav-tabs.nav-stacked>li:last-child>a{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px;}
-.nav-tabs.nav-stacked>li>a:hover{z-index:2;border-color:#ddd;}
-.nav-pills.nav-stacked>li>a{margin-bottom:3px;}
-.nav-pills.nav-stacked>li:last-child>a{margin-bottom:1px;}
-.nav-tabs .dropdown-menu{-webkit-border-radius:0 0 5px 5px;-moz-border-radius:0 0 5px 5px;border-radius:0 0 5px 5px;}
-.nav-pills .dropdown-menu{-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}
-.nav-tabs .dropdown-toggle .caret,.nav-pills .dropdown-toggle .caret{margin-top:6px;border-top-color:#0088cc;border-bottom-color:#0088cc;}
-.nav-tabs .dropdown-toggle:hover .caret,.nav-pills .dropdown-toggle:hover .caret{border-top-color:#005580;border-bottom-color:#005580;}
-.nav-tabs .active .dropdown-toggle .caret,.nav-pills .active .dropdown-toggle .caret{border-top-color:#333333;border-bottom-color:#333333;}
-.nav>.dropdown.active>a:hover{color:#000000;cursor:pointer;}
-.nav-tabs .open .dropdown-toggle,.nav-pills .open .dropdown-toggle,.nav>li.dropdown.open.active>a:hover{color:#ffffff;background-color:#999999;border-color:#999999;}
-.nav li.dropdown.open .caret,.nav li.dropdown.open.active .caret,.nav li.dropdown.open a:hover .caret{border-top-color:#ffffff;border-bottom-color:#ffffff;opacity:1;filter:alpha(opacity=100);}
-.tabs-stacked .open>a:hover{border-color:#999999;}
-.tabbable{*zoom:1;}
-.tabbable:before,.tabbable:after{display:table;content:"";}
-.tabbable:after{clear:both;}
-.tab-content{overflow:auto;}
-.tabs-below>.nav-tabs,.tabs-right>.nav-tabs,.tabs-left>.nav-tabs{border-bottom:0;}
-.tab-content>.tab-pane,.pill-content>.pill-pane{display:none;}
-.tab-content>.active,.pill-content>.active{display:block;}
-.tabs-below>.nav-tabs{border-top:1px solid #ddd;}
-.tabs-below>.nav-tabs>li{margin-top:-1px;margin-bottom:0;}
-.tabs-below>.nav-tabs>li>a{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px;}
-.tabs-below>.nav-tabs>li>a:hover{border-top-color:#ddd;border-bottom-color:transparent;}
-.tabs-below>.nav-tabs>.active>a,.tabs-below>.nav-tabs>.active>a:hover{border-color:transparent #ddd #ddd #ddd;}
-.tabs-left>.nav-tabs>li,.tabs-right>.nav-tabs>li{float:none;}
-.tabs-left>.nav-tabs>li>a,.tabs-right>.nav-tabs>li>a{min-width:74px;margin-right:0;margin-bottom:3px;}
-.tabs-left>.nav-tabs{float:left;margin-right:19px;border-right:1px solid #ddd;}
-.tabs-left>.nav-tabs>li>a{margin-right:-1px;-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px;}
-.tabs-left>.nav-tabs>li>a:hover{border-color:#eeeeee #dddddd #eeeeee #eeeeee;}
-.tabs-left>.nav-tabs .active>a,.tabs-left>.nav-tabs .active>a:hover{border-color:#ddd transparent #ddd #ddd;*border-right-color:#ffffff;}
-.tabs-right>.nav-tabs{float:right;margin-left:19px;border-left:1px solid #ddd;}
-.tabs-right>.nav-tabs>li>a{margin-left:-1px;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0;}
-.tabs-right>.nav-tabs>li>a:hover{border-color:#eeeeee #eeeeee #eeeeee #dddddd;}
-.tabs-right>.nav-tabs .active>a,.tabs-right>.nav-tabs .active>a:hover{border-color:#ddd #ddd #ddd transparent;*border-left-color:#ffffff;}
-.navbar{*position:relative;*z-index:2;margin-bottom:18px;overflow:visible;}
-.navbar-inner{min-height:40px;padding-right:20px;padding-left:20px;background-color:#2c2c2c;background-image:-moz-linear-gradient(top, #333333, #222222);background-image:-ms-linear-gradient(top, #333333, #222222);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#333333), to(#222222));background-image:-webkit-linear-gradient(top, #333333, #222222);background-image:-o-linear-gradient(top, #333333, #222222);background-image:linear-gradient(top, #333333, #222222);background-repeat:repeat-x;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#333333', endColorstr='#222222', GradientType=0);-webkit-box-shadow:0 1px 3px rgba(0, 0, 0, 0.25),inset 0 -1px 0 rgba(0, 0, 0, 0.1);-moz-box-shadow:0 1px 3px rgba(0, 0, 0, 0.25),inset 0 -1px 0 rgba(0, 0, 0, 0.1);box-shadow:0 1px 3px rgba(0, 0, 0, 0.25),inset 0 -1px 0 rgba(0, 0, 0, 0.1);}
-.navbar .container{width:auto;}
-.nav-collapse.collapse{height:auto;}
-.navbar{color:#999999;}
-.navbar .brand:hover{text-decoration:none;}
-.navbar .brand{display:block;float:left;padding:8px 20px 12px;margin-left:-20px;font-size:20px;font-weight:200;line-height:1;color:#999999;}
-.navbar .navbar-text{margin-bottom:0;line-height:40px;}
-.navbar .navbar-link{color:#999999;}
-.navbar .navbar-link:hover{color:#ffffff;}
-.navbar .btn,.navbar .btn-group{margin-top:5px;}
-.navbar .btn-group .btn{margin:0;}
-.navbar-form{margin-bottom:0;*zoom:1;}
-.navbar-form:before,.navbar-form:after{display:table;content:"";}
-.navbar-form:after{clear:both;}
-.navbar-form input,.navbar-form select,.navbar-form .radio,.navbar-form .checkbox{margin-top:5px;}
-.navbar-form input,.navbar-form select{display:inline-block;margin-bottom:0;}
-.navbar-form input[type="image"],.navbar-form input[type="checkbox"],.navbar-form input[type="radio"]{margin-top:3px;}
-.navbar-form .input-append,.navbar-form .input-prepend{margin-top:6px;white-space:nowrap;}
-.navbar-form .input-append input,.navbar-form .input-prepend input{margin-top:0;}
-.navbar-search{position:relative;float:left;margin-top:6px;margin-bottom:0;}
-.navbar-search .search-query{padding:4px 9px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;font-weight:normal;line-height:1;color:#ffffff;background-color:#626262;border:1px solid #151515;-webkit-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1),0 1px 0 rgba(255, 255, 255, 0.15);-moz-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1),0 1px 0 rgba(255, 255, 255, 0.15);box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1),0 1px 0 rgba(255, 255, 255, 0.15);-webkit-transition:none;-moz-transition:none;-ms-transition:none;-o-transition:none;transition:none;}
-.navbar-search .search-query:-moz-placeholder{color:#cccccc;}
-.navbar-search .search-query::-webkit-input-placeholder{color:#cccccc;}
-.navbar-search .search-query:focus,.navbar-search .search-query.focused{padding:5px 10px;color:#333333;text-shadow:0 1px 0 #ffffff;background-color:#ffffff;border:0;outline:0;-webkit-box-shadow:0 0 3px rgba(0, 0, 0, 0.15);-moz-box-shadow:0 0 3px rgba(0, 0, 0, 0.15);box-shadow:0 0 3px rgba(0, 0, 0, 0.15);}
-.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030;margin-bottom:0;}
-.navbar-fixed-top .navbar-inner,.navbar-fixed-bottom .navbar-inner{padding-right:0;padding-left:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}
-.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px;}
-.navbar-fixed-top{top:0;}
-.navbar-fixed-bottom{bottom:0;}
-.navbar .nav{position:relative;left:0;display:block;float:left;margin:0 10px 0 0;}
-.navbar .nav.pull-right{float:right;}
-.navbar .nav>li{display:block;float:left;}
-.navbar .nav>li>a{float:none;padding:9px 10px 11px;line-height:19px;color:#999999;text-decoration:none;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);}
-.navbar .btn{display:inline-block;padding:4px 10px 4px;margin:5px 5px 6px;line-height:18px;}
-.navbar .btn-group{padding:5px 5px 6px;margin:0;}
-.navbar .nav>li>a:hover{color:#ffffff;text-decoration:none;background-color:transparent;}
-.navbar .nav .active>a,.navbar .nav .active>a:hover{color:#ffffff;text-decoration:none;background-color:#222222;}
-.navbar .divider-vertical{width:1px;height:40px;margin:0 9px;overflow:hidden;background-color:#222222;border-right:1px solid #333333;}
-.navbar .nav.pull-right{margin-right:0;margin-left:10px;}
-.navbar .btn-navbar{display:none;float:right;padding:7px 10px;margin-right:5px;margin-left:5px;background-color:#2c2c2c;*background-color:#222222;background-image:-ms-linear-gradient(top, #333333, #222222);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#333333), to(#222222));background-image:-webkit-linear-gradient(top, #333333, #222222);background-image:-o-linear-gradient(top, #333333, #222222);background-image:linear-gradient(top, #333333, #222222);background-image:-moz-linear-gradient(top, #333333, #222222);background-repeat:repeat-x;border-color:#222222 #222222 #000000;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#333333', endColorstr='#222222', GradientType=0);filter:progid:dximagetransform.microsoft.gradient(enabled=false);-webkit-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.075);-moz-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.075);box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.075);}
-.navbar .btn-navbar:hover,.navbar .btn-navbar:active,.navbar .btn-navbar.active,.navbar .btn-navbar.disabled,.navbar .btn-navbar[disabled]{background-color:#222222;*background-color:#151515;}
-.navbar .btn-navbar:active,.navbar .btn-navbar.active{background-color:#080808 \9;}
-.navbar .btn-navbar .icon-bar{display:block;width:18px;height:2px;background-color:#f5f5f5;-webkit-border-radius:1px;-moz-border-radius:1px;border-radius:1px;-webkit-box-shadow:0 1px 0 rgba(0, 0, 0, 0.25);-moz-box-shadow:0 1px 0 rgba(0, 0, 0, 0.25);box-shadow:0 1px 0 rgba(0, 0, 0, 0.25);}
-.btn-navbar .icon-bar+.icon-bar{margin-top:3px;}
-.navbar .dropdown-menu:before{position:absolute;top:-7px;left:9px;display:inline-block;border-right:7px solid transparent;border-bottom:7px solid #ccc;border-left:7px solid transparent;border-bottom-color:rgba(0, 0, 0, 0.2);content:'';}
-.navbar .dropdown-menu:after{position:absolute;top:-6px;left:10px;display:inline-block;border-right:6px solid transparent;border-bottom:6px solid #ffffff;border-left:6px solid transparent;content:'';}
-.navbar-fixed-bottom .dropdown-menu:before{top:auto;bottom:-7px;border-top:7px solid #ccc;border-bottom:0;border-top-color:rgba(0, 0, 0, 0.2);}
-.navbar-fixed-bottom .dropdown-menu:after{top:auto;bottom:-6px;border-top:6px solid #ffffff;border-bottom:0;}
-.navbar .nav li.dropdown .dropdown-toggle .caret,.navbar .nav li.dropdown.open .caret{border-top-color:#ffffff;border-bottom-color:#ffffff;}
-.navbar .nav li.dropdown.active .caret{opacity:1;filter:alpha(opacity=100);}
-.navbar .nav li.dropdown.open>.dropdown-toggle,.navbar .nav li.dropdown.active>.dropdown-toggle,.navbar .nav li.dropdown.open.active>.dropdown-toggle{background-color:transparent;}
-.navbar .nav li.dropdown.active>.dropdown-toggle:hover{color:#ffffff;}
-.navbar .pull-right .dropdown-menu,.navbar .dropdown-menu.pull-right{right:0;left:auto;}
-.navbar .pull-right .dropdown-menu:before,.navbar .dropdown-menu.pull-right:before{right:12px;left:auto;}
-.navbar .pull-right .dropdown-menu:after,.navbar .dropdown-menu.pull-right:after{right:13px;left:auto;}
-.breadcrumb{padding:7px 14px;margin:0 0 18px;list-style:none;background-color:#fbfbfb;background-image:-moz-linear-gradient(top, #ffffff, #f5f5f5);background-image:-ms-linear-gradient(top, #ffffff, #f5f5f5);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#f5f5f5));background-image:-webkit-linear-gradient(top, #ffffff, #f5f5f5);background-image:-o-linear-gradient(top, #ffffff, #f5f5f5);background-image:linear-gradient(top, #ffffff, #f5f5f5);background-repeat:repeat-x;border:1px solid #ddd;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ffffff', endColorstr='#f5f5f5', GradientType=0);-webkit-box-shadow:inset 0 1px 0 #ffffff;-moz-box-shadow:inset 0 1px 0 #ffffff;box-shadow:inset 0 1px 0 #ffffff;}
-.breadcrumb li{display:inline-block;*display:inline;text-shadow:0 1px 0 #ffffff;*zoom:1;}
-.breadcrumb .divider{padding:0 5px;color:#999999;}
-.breadcrumb .active a{color:#333333;}
-.pagination{height:36px;margin:18px 0;}
-.pagination ul{display:inline-block;*display:inline;margin-bottom:0;margin-left:0;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;*zoom:1;-webkit-box-shadow:0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:0 1px 2px rgba(0, 0, 0, 0.05);}
-.pagination li{display:inline;}
-.pagination a{float:left;padding:0 14px;line-height:34px;text-decoration:none;border:1px solid #ddd;border-left-width:0;}
-.pagination a:hover,.pagination .active a{background-color:#f5f5f5;}
-.pagination .active a{color:#999999;cursor:default;}
-.pagination .disabled span,.pagination .disabled a,.pagination .disabled a:hover{color:#999999;cursor:default;background-color:transparent;}
-.pagination li:first-child a{border-left-width:1px;-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;}
-.pagination li:last-child a{-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0;}
-.pagination-centered{text-align:center;}
-.pagination-right{text-align:right;}
-.pager{margin-bottom:18px;margin-left:0;text-align:center;list-style:none;*zoom:1;}
-.pager:before,.pager:after{display:table;content:"";}
-.pager:after{clear:both;}
-.pager li{display:inline;}
-.pager a{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px;}
-.pager a:hover{text-decoration:none;background-color:#f5f5f5;}
-.pager .next a{float:right;}
-.pager .previous a{float:left;}
-.pager .disabled a,.pager .disabled a:hover{color:#999999;cursor:default;background-color:#fff;}
-.modal-open .dropdown-menu{z-index:2050;}
-.modal-open .dropdown.open{*z-index:2050;}
-.modal-open .popover{z-index:2060;}
-.modal-open .tooltip{z-index:2070;}
-.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000000;}
-.modal-backdrop.fade{opacity:0;}
-.modal-backdrop,.modal-backdrop.fade.in{opacity:0.8;filter:alpha(opacity=80);}
-.modal{position:fixed;top:50%;left:50%;z-index:1050;width:560px;margin:-250px 0 0 -280px;overflow:auto;background-color:#ffffff;border:1px solid #999;border:1px solid rgba(0, 0, 0, 0.3);*border:1px solid #999;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);-moz-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box;}
-.modal.fade{top:-25%;-webkit-transition:opacity 0.3s linear,top 0.3s ease-out;-moz-transition:opacity 0.3s linear,top 0.3s ease-out;-ms-transition:opacity 0.3s linear,top 0.3s ease-out;-o-transition:opacity 0.3s linear,top 0.3s ease-out;transition:opacity 0.3s linear,top 0.3s ease-out;}
-.modal.fade.in{top:50%;}
-.modal-header{padding:9px 15px;border-bottom:1px solid #eee;}
-.modal-header .close{margin-top:2px;}
-.modal-body{max-height:400px;padding:15px;overflow-y:auto;}
-.modal-form{margin-bottom:0;}
-.modal-footer{padding:14px 15px 15px;margin-bottom:0;text-align:right;background-color:#f5f5f5;border-top:1px solid #ddd;-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px;*zoom:1;-webkit-box-shadow:inset 0 1px 0 #ffffff;-moz-box-shadow:inset 0 1px 0 #ffffff;box-shadow:inset 0 1px 0 #ffffff;}
-.modal-footer:before,.modal-footer:after{display:table;content:"";}
-.modal-footer:after{clear:both;}
-.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px;}
-.modal-footer .btn-group .btn+.btn{margin-left:-1px;}
-.tooltip{position:absolute;z-index:1020;display:block;padding:5px;font-size:11px;opacity:0;filter:alpha(opacity=0);visibility:visible;}
-.tooltip.in{opacity:0.8;filter:alpha(opacity=80);}
-.tooltip.top{margin-top:-2px;}
-.tooltip.right{margin-left:2px;}
-.tooltip.bottom{margin-top:2px;}
-.tooltip.left{margin-left:-2px;}
-.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-top:5px solid #000000;border-right:5px solid transparent;border-left:5px solid transparent;}
-.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-left:5px solid #000000;}
-.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-right:5px solid transparent;border-bottom:5px solid #000000;border-left:5px solid transparent;}
-.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-top:5px solid transparent;border-right:5px solid #000000;border-bottom:5px solid transparent;}
-.tooltip-inner{max-width:200px;padding:3px 8px;color:#ffffff;text-align:center;text-decoration:none;background-color:#000000;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}
-.tooltip-arrow{position:absolute;width:0;height:0;}
-.popover{position:absolute;top:0;left:0;z-index:1010;display:none;padding:5px;}
-.popover.top{margin-top:-5px;}
-.popover.right{margin-left:5px;}
-.popover.bottom{margin-top:5px;}
-.popover.left{margin-left:-5px;}
-.popover.top .arrow{bottom:0;left:50%;margin-left:-5px;border-top:5px solid #000000;border-right:5px solid transparent;border-left:5px solid transparent;}
-.popover.right .arrow{top:50%;left:0;margin-top:-5px;border-top:5px solid transparent;border-right:5px solid #000000;border-bottom:5px solid transparent;}
-.popover.bottom .arrow{top:0;left:50%;margin-left:-5px;border-right:5px solid transparent;border-bottom:5px solid #000000;border-left:5px solid transparent;}
-.popover.left .arrow{top:50%;right:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-left:5px solid #000000;}
-.popover .arrow{position:absolute;width:0;height:0;}
-.popover-inner{width:280px;padding:3px;overflow:hidden;background:#000000;background:rgba(0, 0, 0, 0.8);-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);-moz-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);}
-.popover-title{padding:9px 15px;line-height:1;background-color:#f5f5f5;border-bottom:1px solid #eee;-webkit-border-radius:3px 3px 0 0;-moz-border-radius:3px 3px 0 0;border-radius:3px 3px 0 0;}
-.popover-content{padding:14px;background-color:#ffffff;-webkit-border-radius:0 0 3px 3px;-moz-border-radius:0 0 3px 3px;border-radius:0 0 3px 3px;-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box;}
-.popover-content p,.popover-content ul,.popover-content ol{margin-bottom:0;}
-.thumbnails{margin-left:-20px;list-style:none;*zoom:1;}
-.thumbnails:before,.thumbnails:after{display:table;content:"";}
-.thumbnails:after{clear:both;}
-.row-fluid .thumbnails{margin-left:0;}
-.thumbnails>li{margin-bottom:18px;}
-.thumbnail{display:block;padding:4px;line-height:1;border:1px solid #ddd;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0, 0, 0, 0.075);-moz-box-shadow:0 1px 1px rgba(0, 0, 0, 0.075);box-shadow:0 1px 1px rgba(0, 0, 0, 0.075);}
-a.thumbnail:hover{border-color:#0088cc;-webkit-box-shadow:0 1px 4px rgba(0, 105, 214, 0.25);-moz-box-shadow:0 1px 4px rgba(0, 105, 214, 0.25);box-shadow:0 1px 4px rgba(0, 105, 214, 0.25);}
-.thumbnail>img{display:block;max-width:100%;margin-right:auto;margin-left:auto;}
-.thumbnail .caption{padding:9px;}
-.label,.badge{font-size:10.998px;font-weight:bold;line-height:14px;color:#ffffff;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);white-space:nowrap;vertical-align:baseline;background-color:#999999;}
-.label{padding:1px 4px 2px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;}
-.badge{padding:1px 9px 2px;-webkit-border-radius:9px;-moz-border-radius:9px;border-radius:9px;}
-a.label:hover,a.badge:hover{color:#ffffff;text-decoration:none;cursor:pointer;}
-.label-important,.badge-important{background-color:#b94a48;}
-.label-important[href],.badge-important[href]{background-color:#953b39;}
-.label-warning,.badge-warning{background-color:#f89406;}
-.label-warning[href],.badge-warning[href]{background-color:#c67605;}
-.label-success,.badge-success{background-color:#468847;}
-.label-success[href],.badge-success[href]{background-color:#356635;}
-.label-info,.badge-info{background-color:#3a87ad;}
-.label-info[href],.badge-info[href]{background-color:#2d6987;}
-.label-inverse,.badge-inverse{background-color:#333333;}
-.label-inverse[href],.badge-inverse[href]{background-color:#1a1a1a;}
-@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0;} to{background-position:0 0;}}@-moz-keyframes progress-bar-stripes{from{background-position:40px 0;} to{background-position:0 0;}}@-ms-keyframes progress-bar-stripes{from{background-position:40px 0;} to{background-position:0 0;}}@-o-keyframes progress-bar-stripes{from{background-position:0 0;} to{background-position:40px 0;}}@keyframes progress-bar-stripes{from{background-position:40px 0;} to{background-position:0 0;}}.progress{height:18px;margin-bottom:18px;overflow:hidden;background-color:#f7f7f7;background-image:-moz-linear-gradient(top, #f5f5f5, #f9f9f9);background-image:-ms-linear-gradient(top, #f5f5f5, #f9f9f9);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#f5f5f5), to(#f9f9f9));background-image:-webkit-linear-gradient(top, #f5f5f5, #f9f9f9);background-image:-o-linear-gradient(top, #f5f5f5, #f9f9f9);background-image:linear-gradient(top, #f5f5f5, #f9f9f9);background-repeat:repeat-x;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#f5f5f5', endColorstr='#f9f9f9', GradientType=0);-webkit-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1);-moz-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1);box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1);}
-.progress .bar{width:0;height:18px;font-size:12px;color:#ffffff;text-align:center;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);background-color:#0e90d2;background-image:-moz-linear-gradient(top, #149bdf, #0480be);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#149bdf), to(#0480be));background-image:-webkit-linear-gradient(top, #149bdf, #0480be);background-image:-o-linear-gradient(top, #149bdf, #0480be);background-image:linear-gradient(top, #149bdf, #0480be);background-image:-ms-linear-gradient(top, #149bdf, #0480be);background-repeat:repeat-x;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#149bdf', endColorstr='#0480be', GradientType=0);-webkit-box-shadow:inset 0 -1px 0 rgba(0, 0, 0, 0.15);-moz-box-shadow:inset 0 -1px 0 rgba(0, 0, 0, 0.15);box-shadow:inset 0 -1px 0 rgba(0, 0, 0, 0.15);-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;-webkit-transition:width 0.6s ease;-moz-transition:width 0.6s ease;-ms-transition:width 0.6s ease;-o-transition:width 0.6s ease;transition:width 0.6s ease;}
-.progress-striped .bar{background-color:#149bdf;background-image:-o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);-webkit-background-size:40px 40px;-moz-background-size:40px 40px;-o-background-size:40px 40px;background-size:40px 40px;}
-.progress.active .bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-moz-animation:progress-bar-stripes 2s linear infinite;-ms-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite;}
-.progress-danger .bar{background-color:#dd514c;background-image:-moz-linear-gradient(top, #ee5f5b, #c43c35);background-image:-ms-linear-gradient(top, #ee5f5b, #c43c35);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#c43c35));background-image:-webkit-linear-gradient(top, #ee5f5b, #c43c35);background-image:-o-linear-gradient(top, #ee5f5b, #c43c35);background-image:linear-gradient(top, #ee5f5b, #c43c35);background-repeat:repeat-x;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ee5f5b', endColorstr='#c43c35', GradientType=0);}
-.progress-danger.progress-striped .bar{background-color:#ee5f5b;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);}
-.progress-success .bar{background-color:#5eb95e;background-image:-moz-linear-gradient(top, #62c462, #57a957);background-image:-ms-linear-gradient(top, #62c462, #57a957);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#57a957));background-image:-webkit-linear-gradient(top, #62c462, #57a957);background-image:-o-linear-gradient(top, #62c462, #57a957);background-image:linear-gradient(top, #62c462, #57a957);background-repeat:repeat-x;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#62c462', endColorstr='#57a957', GradientType=0);}
-.progress-success.progress-striped .bar{background-color:#62c462;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);}
-.progress-info .bar{background-color:#4bb1cf;background-image:-moz-linear-gradient(top, #5bc0de, #339bb9);background-image:-ms-linear-gradient(top, #5bc0de, #339bb9);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#339bb9));background-image:-webkit-linear-gradient(top, #5bc0de, #339bb9);background-image:-o-linear-gradient(top, #5bc0de, #339bb9);background-image:linear-gradient(top, #5bc0de, #339bb9);background-repeat:repeat-x;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#5bc0de', endColorstr='#339bb9', GradientType=0);}
-.progress-info.progress-striped .bar{background-color:#5bc0de;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);}
-.progress-warning .bar{background-color:#faa732;background-image:-moz-linear-gradient(top, #fbb450, #f89406);background-image:-ms-linear-gradient(top, #fbb450, #f89406);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406));background-image:-webkit-linear-gradient(top, #fbb450, #f89406);background-image:-o-linear-gradient(top, #fbb450, #f89406);background-image:linear-gradient(top, #fbb450, #f89406);background-repeat:repeat-x;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#fbb450', endColorstr='#f89406', GradientType=0);}
-.progress-warning.progress-striped .bar{background-color:#fbb450;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);}
-.accordion{margin-bottom:18px;}
-.accordion-group{margin-bottom:2px;border:1px solid #e5e5e5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}
-.accordion-heading{border-bottom:0;}
-.accordion-heading .accordion-toggle{display:block;padding:8px 15px;}
-.accordion-toggle{cursor:pointer;}
-.accordion-inner{padding:9px 15px;border-top:1px solid #e5e5e5;}
-.carousel{position:relative;margin-bottom:18px;line-height:1;}
-.carousel-inner{position:relative;width:100%;overflow:hidden;}
-.carousel .item{position:relative;display:none;-webkit-transition:0.6s ease-in-out left;-moz-transition:0.6s ease-in-out left;-ms-transition:0.6s ease-in-out left;-o-transition:0.6s ease-in-out left;transition:0.6s ease-in-out left;}
-.carousel .item>img{display:block;line-height:1;}
-.carousel .active,.carousel .next,.carousel .prev{display:block;}
-.carousel .active{left:0;}
-.carousel .next,.carousel .prev{position:absolute;top:0;width:100%;}
-.carousel .next{left:100%;}
-.carousel .prev{left:-100%;}
-.carousel .next.left,.carousel .prev.right{left:0;}
-.carousel .active.left{left:-100%;}
-.carousel .active.right{left:100%;}
-.carousel-control{position:absolute;top:40%;left:15px;width:40px;height:40px;margin-top:-20px;font-size:60px;font-weight:100;line-height:30px;color:#ffffff;text-align:center;background:#222222;border:3px solid #ffffff;-webkit-border-radius:23px;-moz-border-radius:23px;border-radius:23px;opacity:0.5;filter:alpha(opacity=50);}
-.carousel-control.right{right:15px;left:auto;}
-.carousel-control:hover{color:#ffffff;text-decoration:none;opacity:0.9;filter:alpha(opacity=90);}
-.carousel-caption{position:absolute;right:0;bottom:0;left:0;padding:10px 15px 5px;background:#333333;background:rgba(0, 0, 0, 0.75);}
-.carousel-caption h4,.carousel-caption p{color:#ffffff;}
-.hero-unit{padding:60px;margin-bottom:30px;background-color:#eeeeee;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;}
-.hero-unit h1{margin-bottom:0;font-size:60px;line-height:1;letter-spacing:-1px;color:inherit;}
-.hero-unit p{font-size:18px;font-weight:200;line-height:27px;color:inherit;}
-.pull-right{float:right;}
-.pull-left{float:left;}
-.hide{display:none;}
-.show{display:block;}
-.invisible{visibility:hidden;}
diff --git a/apps/fileupload/static/fileupload/css/jquery.fileupload-ui.css b/apps/fileupload/static/fileupload/css/jquery.fileupload-ui.css
deleted file mode 100644 (file)
index e36a93d..0000000
+++ /dev/null
@@ -1,84 +0,0 @@
-@charset 'UTF-8';
-/*
- * jQuery File Upload UI Plugin CSS 6.3
- * https://github.com/blueimp/jQuery-File-Upload
- *
- * Copyright 2010, Sebastian Tschan
- * https://blueimp.net
- *
- * Licensed under the MIT license:
- * http://www.opensource.org/licenses/MIT
- */
-
-.fileinput-button {
-  position: relative;
-  overflow: hidden;
-  float: left;
-  margin-right: 4px;
-}
-.fileinput-button input {
-  position: absolute;
-  top: 0;
-  right: 0;
-  margin: 0;
-  border: solid transparent;
-  border-width: 0 0 100px 200px;
-  opacity: 0;
-  filter: alpha(opacity=0);
-  -moz-transform: translate(-300px, 0) scale(4);
-  direction: ltr;
-  cursor: pointer;
-}
-.fileupload-buttonbar .btn,
-.fileupload-buttonbar .toggle {
-  margin-bottom: 5px;
-}
-.files .progress {
-  width: 200px;
-}
-.progress-animated .bar {
-  background: url(../img/progressbar.gif) !important;
-  filter: none;
-}
-.fileupload-loading {
-  position: absolute;
-  left: 50%;
-  width: 128px;
-  height: 128px;
-  background: url(../img/loading.gif) center no-repeat;
-  display: none;
-}
-.fileupload-processing .fileupload-loading {
-  display: block;
-}
-
-/* Fix for IE 6: */
-*html .fileinput-button {
-  line-height: 22px;
-  margin: 1px -3px 0 0;
-}
-
-/* Fix for IE 7: */
-*+html .fileinput-button {
-  margin: 1px 0 0 0;
-}
-
-@media (max-width: 480px) {
-  .files .btn span {
-    display: none;
-  }
-  .files .preview * {
-    width: 40px;
-  }
-  .files .name * {
-    width: 80px;
-    display: inline-block;
-    word-wrap: break-word;
-  }
-  .files .progress {
-    width: 20px;
-  }
-  .files .delete {
-    width: 60px;
-  }
-}
diff --git a/apps/fileupload/static/fileupload/css/style.css b/apps/fileupload/static/fileupload/css/style.css
deleted file mode 100644 (file)
index e45d81d..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-.preview img {
-    max-height: 50px;
-}
-
-.delete button[data-type=""] {
-    display: none;
-}
-.delete button[data-type=""] + input {
-    display: none;
-}
diff --git a/apps/fileupload/static/fileupload/img/glyphicons-halflings-white.png b/apps/fileupload/static/fileupload/img/glyphicons-halflings-white.png
deleted file mode 100644 (file)
index 3bf6484..0000000
Binary files a/apps/fileupload/static/fileupload/img/glyphicons-halflings-white.png and /dev/null differ
diff --git a/apps/fileupload/static/fileupload/img/glyphicons-halflings.png b/apps/fileupload/static/fileupload/img/glyphicons-halflings.png
deleted file mode 100644 (file)
index 79bc568..0000000
Binary files a/apps/fileupload/static/fileupload/img/glyphicons-halflings.png and /dev/null differ
diff --git a/apps/fileupload/static/fileupload/img/loading.gif b/apps/fileupload/static/fileupload/img/loading.gif
deleted file mode 100644 (file)
index 90f28cb..0000000
Binary files a/apps/fileupload/static/fileupload/img/loading.gif and /dev/null differ
diff --git a/apps/fileupload/static/fileupload/img/progressbar.gif b/apps/fileupload/static/fileupload/img/progressbar.gif
deleted file mode 100644 (file)
index fbcce6b..0000000
Binary files a/apps/fileupload/static/fileupload/img/progressbar.gif and /dev/null differ
diff --git a/apps/fileupload/static/fileupload/js/bootstrap-image-gallery.min.js b/apps/fileupload/static/fileupload/js/bootstrap-image-gallery.min.js
deleted file mode 100644 (file)
index a749f55..0000000
+++ /dev/null
@@ -1 +0,0 @@
-(function(a){"use strict",typeof define=="function"&&define.amd?define(["jquery","load-image","bootstrap"],a):a(window.jQuery,window.loadImage)})(function(a,b){"use strict",a.extend(a.fn.modal.defaults,{delegate:document,selector:null,filter:"*",index:0,href:null,preloadRange:2,offsetWidth:100,offsetHeight:200,canvas:!1,slideshow:0,imageClickDivision:.5});var c=a.fn.modal.Constructor.prototype.show,d=a.fn.modal.Constructor.prototype.hide;a.extend(a.fn.modal.Constructor.prototype,{initLinks:function(){var b=this,c=this.options,d=c.selector||"a[data-target="+c.target+"]";this.$links=a(c.delegate).find(d).filter(c.filter).each(function(a){b.getUrl(this)===c.href&&(c.index=a)}),this.$links[c.index]||(c.index=0)},getUrl:function(b){return b.href||a(b).data("href")},startSlideShow:function(){var a=this;this.options.slideshow&&(this._slideShow=window.setTimeout(function(){a.next()},this.options.slideshow))},stopSlideShow:function(){window.clearTimeout(this._slideShow)},toggleSlideShow:function(){var a=this.$element.find(".modal-slideshow");this.options.slideshow?(this.options.slideshow=0,this.stopSlideShow()):(this.options.slideshow=a.data("slideshow")||5e3,this.startSlideShow()),a.find("i").toggleClass("icon-play icon-pause")},preloadImages:function(){var b=this.options,c=b.index+b.preloadRange+1,d,e;for(e=b.index-b.preloadRange;e<c;e+=1)d=this.$links[e],d&&e!==b.index&&a("<img>").prop("src",this.getUrl(d))},loadImage:function(){var a=this,c=this.$element,d=this.options.index,e=this.getUrl(this.$links[d]),f;this.abortLoad(),this.stopSlideShow(),c.trigger("beforeLoad"),this._loadingTimeout=window.setTimeout(function(){c.addClass("modal-loading")},100),f=c.find(".modal-image").children().removeClass("in"),window.setTimeout(function(){f.remove()},3e3),c.find(".modal-title").text(this.$links[d].title),c.find(".modal-download").prop("href",e),this._loadingImage=b(e,function(b){a.img=b,window.clearTimeout(a._loadingTimeout),c.removeClass("modal-loading"),c.trigger("load"),a.showImage(b),a.startSlideShow()},this._loadImageOptions),this.preloadImages()},showImage:function(b){var c=this.$element,d=a.support.transition&&c.hasClass("fade"),e=d?c.animate:c.css,f=c.find(".modal-image"),g,h;f.css({width:b.width,height:b.height}),c.find(".modal-title").css({width:Math.max(b.width,380)}),a(window).width()>480&&(d&&(g=c.clone().hide().appendTo(document.body)),e.call(c.stop(),{"margin-top":-((g||c).outerHeight()/2),"margin-left":-((g||c).outerWidth()/2)}),g&&g.remove()),f.append(b),h=b.offsetWidth,c.trigger("display"),d?c.is(":visible")?a(b).on(a.support.transition.end,function(d){d.target===b&&(a(b).off(a.support.transition.end),c.trigger("displayed"))}).addClass("in"):(a(b).addClass("in"),c.one("shown",function(){c.trigger("displayed")})):(a(b).addClass("in"),c.trigger("displayed"))},abortLoad:function(){this._loadingImage&&(this._loadingImage.onload=this._loadingImage.onerror=null),window.clearTimeout(this._loadingTimeout)},prev:function(){var a=this.options;a.index-=1,a.index<0&&(a.index=this.$links.length-1),this.loadImage()},next:function(){var a=this.options;a.index+=1,a.index>this.$links.length-1&&(a.index=0),this.loadImage()},keyHandler:function(a){switch(a.which){case 37:case 38:a.preventDefault(),this.prev();break;case 39:case 40:a.preventDefault(),this.next()}},wheelHandler:function(a){a.preventDefault(),a=a.originalEvent,this._wheelCounter=this._wheelCounter||0,this._wheelCounter+=a.wheelDelta||a.detail||0;if(a.wheelDelta&&this._wheelCounter>=120||!a.wheelDelta&&this._wheelCounter<0)this.prev(),this._wheelCounter=0;else if(a.wheelDelta&&this._wheelCounter<=-120||!a.wheelDelta&&this._wheelCounter>0)this.next(),this._wheelCounter=0},initGalleryEvents:function(){var b=this,c=this.$element;c.find(".modal-image").on("click.modal-gallery",function(c){var d=a(this);b.$links.length===1?b.hide():(c.pageX-d.offset().left)/d.width()<b.options.imageClickDivision?b.prev(c):b.next(c)}),c.find(".modal-prev").on("click.modal-gallery",function(a){b.prev(a)}),c.find(".modal-next").on("click.modal-gallery",function(a){b.next(a)}),c.find(".modal-slideshow").on("click.modal-gallery",function(a){b.toggleSlideShow(a)}),a(document).on("keydown.modal-gallery",function(a){b.keyHandler(a)}).on("mousewheel.modal-gallery, DOMMouseScroll.modal-gallery",function(a){b.wheelHandler(a)})},destroyGalleryEvents:function(){var b=this.$element;this.abortLoad(),this.stopSlideShow(),b.find(".modal-image, .modal-prev, .modal-next, .modal-slideshow").off("click.modal-gallery"),a(document).off("keydown.modal-gallery").off("mousewheel.modal-gallery, DOMMouseScroll.modal-gallery")},show:function(){if(!this.isShown&&this.$element.hasClass("modal-gallery")){var b=this.$element,d=this.options,e=a(window).width(),f=a(window).height();b.hasClass("modal-fullscreen")?(this._loadImageOptions={maxWidth:e,maxHeight:f,canvas:d.canvas},b.hasClass("modal-fullscreen-stretch")&&(this._loadImageOptions.minWidth=e,this._loadImageOptions.minHeight=f)):this._loadImageOptions={maxWidth:e-d.offsetWidth,maxHeight:f-d.offsetHeight,canvas:d.canvas},e>480&&b.css({"margin-top":-(b.outerHeight()/2),"margin-left":-(b.outerWidth()/2)}),this.initGalleryEvents(),this.initLinks(),this.$links.length&&(b.find(".modal-slideshow, .modal-prev, .modal-next").toggle(this.$links.length!==1),b.toggleClass("modal-single",this.$links.length===1),this.loadImage())}c.apply(this,arguments)},hide:function(){this.isShown&&this.$element.hasClass("modal-gallery")&&(this.options.delegate=document,this.options.href=null,this.destroyGalleryEvents()),d.apply(this,arguments)}}),a(function(){a(document.body).on("click.modal-gallery.data-api",'[data-toggle="modal-gallery"]',function(b){var c=a(this),d=c.data(),e=a(d.target),f=e.data("modal"),g;f||(d=a.extend(e.data(),d)),d.selector||(d.selector="a[rel=gallery]"),g=a(b.target).closest(d.selector),g.length&&e.length&&(b.preventDefault(),d.href=g.prop("href")||g.data("href"),d.delegate=g[0]!==this?this:document,f&&a.extend(f.options,d),e.modal(d))})})});
diff --git a/apps/fileupload/static/fileupload/js/bootstrap.min.js b/apps/fileupload/static/fileupload/js/bootstrap.min.js
deleted file mode 100644 (file)
index fcfb38b..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-/**
-* Bootstrap.js by @fat & @mdo
-* Copyright 2012 Twitter, Inc.
-* http://www.apache.org/licenses/LICENSE-2.0.txt
-*/
-!function(a){a(function(){"use strict",a.support.transition=function(){var a=function(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd",msTransition:"MSTransitionEnd",transition:"transitionend"},c;for(c in b)if(a.style[c]!==undefined)return b[c]}();return a&&{end:a}}()})}(window.jQuery),!function(a){"use strict";var b='[data-dismiss="alert"]',c=function(c){a(c).on("click",b,this.close)};c.prototype.close=function(b){function f(){e.trigger("closed").remove()}var c=a(this),d=c.attr("data-target"),e;d||(d=c.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,"")),e=a(d),b&&b.preventDefault(),e.length||(e=c.hasClass("alert")?c:c.parent()),e.trigger(b=a.Event("close"));if(b.isDefaultPrevented())return;e.removeClass("in"),a.support.transition&&e.hasClass("fade")?e.on(a.support.transition.end,f):f()},a.fn.alert=function(b){return this.each(function(){var d=a(this),e=d.data("alert");e||d.data("alert",e=new c(this)),typeof b=="string"&&e[b].call(d)})},a.fn.alert.Constructor=c,a(function(){a("body").on("click.alert.data-api",b,c.prototype.close)})}(window.jQuery),!function(a){"use strict";var b=function(b,c){this.$element=a(b),this.options=a.extend({},a.fn.button.defaults,c)};b.prototype.setState=function(a){var b="disabled",c=this.$element,d=c.data(),e=c.is("input")?"val":"html";a+="Text",d.resetText||c.data("resetText",c[e]()),c[e](d[a]||this.options[a]),setTimeout(function(){a=="loadingText"?c.addClass(b).attr(b,b):c.removeClass(b).removeAttr(b)},0)},b.prototype.toggle=function(){var a=this.$element.parent('[data-toggle="buttons-radio"]');a&&a.find(".active").removeClass("active"),this.$element.toggleClass("active")},a.fn.button=function(c){return this.each(function(){var d=a(this),e=d.data("button"),f=typeof c=="object"&&c;e||d.data("button",e=new b(this,f)),c=="toggle"?e.toggle():c&&e.setState(c)})},a.fn.button.defaults={loadingText:"loading..."},a.fn.button.Constructor=b,a(function(){a("body").on("click.button.data-api","[data-toggle^=button]",function(b){var c=a(b.target);c.hasClass("btn")||(c=c.closest(".btn")),c.button("toggle")})})}(window.jQuery),!function(a){"use strict";var b=function(b,c){this.$element=a(b),this.options=c,this.options.slide&&this.slide(this.options.slide),this.options.pause=="hover"&&this.$element.on("mouseenter",a.proxy(this.pause,this)).on("mouseleave",a.proxy(this.cycle,this))};b.prototype={cycle:function(b){return b||(this.paused=!1),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},to:function(b){var c=this.$element.find(".active"),d=c.parent().children(),e=d.index(c),f=this;if(b>d.length-1||b<0)return;return this.sliding?this.$element.one("slid",function(){f.to(b)}):e==b?this.pause().cycle():this.slide(b>e?"next":"prev",a(d[b]))},pause:function(a){return a||(this.paused=!0),clearInterval(this.interval),this.interval=null,this},next:function(){if(this.sliding)return;return this.slide("next")},prev:function(){if(this.sliding)return;return this.slide("prev")},slide:function(b,c){var d=this.$element.find(".active"),e=c||d[b](),f=this.interval,g=b=="next"?"left":"right",h=b=="next"?"first":"last",i=this,j=a.Event("slide");this.sliding=!0,f&&this.pause(),e=e.length?e:this.$element.find(".item")[h]();if(e.hasClass("active"))return;if(a.support.transition&&this.$element.hasClass("slide")){this.$element.trigger(j);if(j.isDefaultPrevented())return;e.addClass(b),e[0].offsetWidth,d.addClass(g),e.addClass(g),this.$element.one(a.support.transition.end,function(){e.removeClass([b,g].join(" ")).addClass("active"),d.removeClass(["active",g].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger("slid")},0)})}else{this.$element.trigger(j);if(j.isDefaultPrevented())return;d.removeClass("active"),e.addClass("active"),this.sliding=!1,this.$element.trigger("slid")}return f&&this.cycle(),this}},a.fn.carousel=function(c){return this.each(function(){var d=a(this),e=d.data("carousel"),f=a.extend({},a.fn.carousel.defaults,typeof c=="object"&&c);e||d.data("carousel",e=new b(this,f)),typeof c=="number"?e.to(c):typeof c=="string"||(c=f.slide)?e[c]():f.interval&&e.cycle()})},a.fn.carousel.defaults={interval:5e3,pause:"hover"},a.fn.carousel.Constructor=b,a(function(){a("body").on("click.carousel.data-api","[data-slide]",function(b){var c=a(this),d,e=a(c.attr("data-target")||(d=c.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,"")),f=!e.data("modal")&&a.extend({},e.data(),c.data());e.carousel(f),b.preventDefault()})})}(window.jQuery),!function(a){"use strict";var b=function(b,c){this.$element=a(b),this.options=a.extend({},a.fn.collapse.defaults,c),this.options.parent&&(this.$parent=a(this.options.parent)),this.options.toggle&&this.toggle()};b.prototype={constructor:b,dimension:function(){var a=this.$element.hasClass("width");return a?"width":"height"},show:function(){var b,c,d,e;if(this.transitioning)return;b=this.dimension(),c=a.camelCase(["scroll",b].join("-")),d=this.$parent&&this.$parent.find("> .accordion-group > .in");if(d&&d.length){e=d.data("collapse");if(e&&e.transitioning)return;d.collapse("hide"),e||d.data("collapse",null)}this.$element[b](0),this.transition("addClass",a.Event("show"),"shown"),this.$element[b](this.$element[0][c])},hide:function(){var b;if(this.transitioning)return;b=this.dimension(),this.reset(this.$element[b]()),this.transition("removeClass",a.Event("hide"),"hidden"),this.$element[b](0)},reset:function(a){var b=this.dimension();return this.$element.removeClass("collapse")[b](a||"auto")[0].offsetWidth,this.$element[a!==null?"addClass":"removeClass"]("collapse"),this},transition:function(b,c,d){var e=this,f=function(){c=="show"&&e.reset(),e.transitioning=0,e.$element.trigger(d)};this.$element.trigger(c);if(c.isDefaultPrevented())return;this.transitioning=1,this.$element[b]("in"),a.support.transition&&this.$element.hasClass("collapse")?this.$element.one(a.support.transition.end,f):f()},toggle:function(){this[this.$element.hasClass("in")?"hide":"show"]()}},a.fn.collapse=function(c){return this.each(function(){var d=a(this),e=d.data("collapse"),f=typeof c=="object"&&c;e||d.data("collapse",e=new b(this,f)),typeof c=="string"&&e[c]()})},a.fn.collapse.defaults={toggle:!0},a.fn.collapse.Constructor=b,a(function(){a("body").on("click.collapse.data-api","[data-toggle=collapse]",function(b){var c=a(this),d,e=c.attr("data-target")||b.preventDefault()||(d=c.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""),f=a(e).data("collapse")?"toggle":c.data();a(e).collapse(f)})})}(window.jQuery),!function(a){function d(){a(b).parent().removeClass("open")}"use strict";var b='[data-toggle="dropdown"]',c=function(b){var c=a(b).on("click.dropdown.data-api",this.toggle);a("html").on("click.dropdown.data-api",function(){c.parent().removeClass("open")})};c.prototype={constructor:c,toggle:function(b){var c=a(this),e,f,g;if(c.is(".disabled, :disabled"))return;return f=c.attr("data-target"),f||(f=c.attr("href"),f=f&&f.replace(/.*(?=#[^\s]*$)/,"")),e=a(f),e.length||(e=c.parent()),g=e.hasClass("open"),d(),g||e.toggleClass("open"),!1}},a.fn.dropdown=function(b){return this.each(function(){var d=a(this),e=d.data("dropdown");e||d.data("dropdown",e=new c(this)),typeof b=="string"&&e[b].call(d)})},a.fn.dropdown.Constructor=c,a(function(){a("html").on("click.dropdown.data-api",d),a("body").on("click.dropdown",".dropdown form",function(a){a.stopPropagation()}).on("click.dropdown.data-api",b,c.prototype.toggle)})}(window.jQuery),!function(a){function c(){var b=this,c=setTimeout(function(){b.$element.off(a.support.transition.end),d.call(b)},500);this.$element.one(a.support.transition.end,function(){clearTimeout(c),d.call(b)})}function d(a){this.$element.hide().trigger("hidden"),e.call(this)}function e(b){var c=this,d=this.$element.hasClass("fade")?"fade":"";if(this.isShown&&this.options.backdrop){var e=a.support.transition&&d;this.$backdrop=a('<div class="modal-backdrop '+d+'" />').appendTo(document.body),this.options.backdrop!="static"&&this.$backdrop.click(a.proxy(this.hide,this)),e&&this.$backdrop[0].offsetWidth,this.$backdrop.addClass("in"),e?this.$backdrop.one(a.support.transition.end,b):b()}else!this.isShown&&this.$backdrop?(this.$backdrop.removeClass("in"),a.support.transition&&this.$element.hasClass("fade")?this.$backdrop.one(a.support.transition.end,a.proxy(f,this)):f.call(this)):b&&b()}function f(){this.$backdrop.remove(),this.$backdrop=null}function g(){var b=this;this.isShown&&this.options.keyboard?a(document).on("keyup.dismiss.modal",function(a){a.which==27&&b.hide()}):this.isShown||a(document).off("keyup.dismiss.modal")}"use strict";var b=function(b,c){this.options=c,this.$element=a(b).delegate('[data-dismiss="modal"]',"click.dismiss.modal",a.proxy(this.hide,this))};b.prototype={constructor:b,toggle:function(){return this[this.isShown?"hide":"show"]()},show:function(){var b=this,c=a.Event("show");this.$element.trigger(c);if(this.isShown||c.isDefaultPrevented())return;a("body").addClass("modal-open"),this.isShown=!0,g.call(this),e.call(this,function(){var c=a.support.transition&&b.$element.hasClass("fade");b.$element.parent().length||b.$element.appendTo(document.body),b.$element.show(),c&&b.$element[0].offsetWidth,b.$element.addClass("in"),c?b.$element.one(a.support.transition.end,function(){b.$element.trigger("shown")}):b.$element.trigger("shown")})},hide:function(b){b&&b.preventDefault();var e=this;b=a.Event("hide"),this.$element.trigger(b);if(!this.isShown||b.isDefaultPrevented())return;this.isShown=!1,a("body").removeClass("modal-open"),g.call(this),this.$element.removeClass("in"),a.support.transition&&this.$element.hasClass("fade")?c.call(this):d.call(this)}},a.fn.modal=function(c){return this.each(function(){var d=a(this),e=d.data("modal"),f=a.extend({},a.fn.modal.defaults,d.data(),typeof c=="object"&&c);e||d.data("modal",e=new b(this,f)),typeof c=="string"?e[c]():f.show&&e.show()})},a.fn.modal.defaults={backdrop:!0,keyboard:!0,show:!0},a.fn.modal.Constructor=b,a(function(){a("body").on("click.modal.data-api",'[data-toggle="modal"]',function(b){var c=a(this),d,e=a(c.attr("data-target")||(d=c.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,"")),f=e.data("modal")?"toggle":a.extend({},e.data(),c.data());b.preventDefault(),e.modal(f)})})}(window.jQuery),!function(a){"use strict";var b=function(a,b){this.init("tooltip",a,b)};b.prototype={constructor:b,init:function(b,c,d){var e,f;this.type=b,this.$element=a(c),this.options=this.getOptions(d),this.enabled=!0,this.options.trigger!="manual"&&(e=this.options.trigger=="hover"?"mouseenter":"focus",f=this.options.trigger=="hover"?"mouseleave":"blur",this.$element.on(e,this.options.selector,a.proxy(this.enter,this)),this.$element.on(f,this.options.selector,a.proxy(this.leave,this))),this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},getOptions:function(b){return b=a.extend({},a.fn[this.type].defaults,b,this.$element.data()),b.delay&&typeof b.delay=="number"&&(b.delay={show:b.delay,hide:b.delay}),b},enter:function(b){var c=a(b.currentTarget)[this.type](this._options).data(this.type);if(!c.options.delay||!c.options.delay.show)return c.show();clearTimeout(this.timeout),c.hoverState="in",this.timeout=setTimeout(function(){c.hoverState=="in"&&c.show()},c.options.delay.show)},leave:function(b){var c=a(b.currentTarget)[this.type](this._options).data(this.type);if(!c.options.delay||!c.options.delay.hide)return c.hide();clearTimeout(this.timeout),c.hoverState="out",this.timeout=setTimeout(function(){c.hoverState=="out"&&c.hide()},c.options.delay.hide)},show:function(){var a,b,c,d,e,f,g;if(this.hasContent()&&this.enabled){a=this.tip(),this.setContent(),this.options.animation&&a.addClass("fade"),f=typeof this.options.placement=="function"?this.options.placement.call(this,a[0],this.$element[0]):this.options.placement,b=/in/.test(f),a.remove().css({top:0,left:0,display:"block"}).appendTo(b?this.$element:document.body),c=this.getPosition(b),d=a[0].offsetWidth,e=a[0].offsetHeight;switch(b?f.split(" ")[1]:f){case"bottom":g={top:c.top+c.height,left:c.left+c.width/2-d/2};break;case"top":g={top:c.top-e,left:c.left+c.width/2-d/2};break;case"left":g={top:c.top+c.height/2-e/2,left:c.left-d};break;case"right":g={top:c.top+c.height/2-e/2,left:c.left+c.width}}a.css(g).addClass(f).addClass("in")}},isHTML:function(a){return typeof a!="string"||a.charAt(0)==="<"&&a.charAt(a.length-1)===">"&&a.length>=3||/^(?:[^<]*<[\w\W]+>[^>]*$)/.exec(a)},setContent:function(){var a=this.tip(),b=this.getTitle();a.find(".tooltip-inner")[this.isHTML(b)?"html":"text"](b),a.removeClass("fade in top bottom left right")},hide:function(){function d(){var b=setTimeout(function(){c.off(a.support.transition.end).remove()},500);c.one(a.support.transition.end,function(){clearTimeout(b),c.remove()})}var b=this,c=this.tip();c.removeClass("in"),a.support.transition&&this.$tip.hasClass("fade")?d():c.remove()},fixTitle:function(){var a=this.$element;(a.attr("title")||typeof a.attr("data-original-title")!="string")&&a.attr("data-original-title",a.attr("title")||"").removeAttr("title")},hasContent:function(){return this.getTitle()},getPosition:function(b){return a.extend({},b?{top:0,left:0}:this.$element.offset(),{width:this.$element[0].offsetWidth,height:this.$element[0].offsetHeight})},getTitle:function(){var a,b=this.$element,c=this.options;return a=b.attr("data-original-title")||(typeof c.title=="function"?c.title.call(b[0]):c.title),a},tip:function(){return this.$tip=this.$tip||a(this.options.template)},validate:function(){this.$element[0].parentNode||(this.hide(),this.$element=null,this.options=null)},enable:function(){this.enabled=!0},disable:function(){this.enabled=!1},toggleEnabled:function(){this.enabled=!this.enabled},toggle:function(){this[this.tip().hasClass("in")?"hide":"show"]()}},a.fn.tooltip=function(c){return this.each(function(){var d=a(this),e=d.data("tooltip"),f=typeof c=="object"&&c;e||d.data("tooltip",e=new b(this,f)),typeof c=="string"&&e[c]()})},a.fn.tooltip.Constructor=b,a.fn.tooltip.defaults={animation:!0,placement:"top",selector:!1,template:'<div class="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',trigger:"hover",title:"",delay:0}}(window.jQuery),!function(a){"use strict";var b=function(a,b){this.init("popover",a,b)};b.prototype=a.extend({},a.fn.tooltip.Constructor.prototype,{constructor:b,setContent:function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.isHTML(b)?"html":"text"](b),a.find(".popover-content > *")[this.isHTML(c)?"html":"text"](c),a.removeClass("fade top bottom left right in")},hasContent:function(){return this.getTitle()||this.getContent()},getContent:function(){var a,b=this.$element,c=this.options;return a=b.attr("data-content")||(typeof c.content=="function"?c.content.call(b[0]):c.content),a},tip:function(){return this.$tip||(this.$tip=a(this.options.template)),this.$tip}}),a.fn.popover=function(c){return this.each(function(){var d=a(this),e=d.data("popover"),f=typeof c=="object"&&c;e||d.data("popover",e=new b(this,f)),typeof c=="string"&&e[c]()})},a.fn.popover.Constructor=b,a.fn.popover.defaults=a.extend({},a.fn.tooltip.defaults,{placement:"right",content:"",template:'<div class="popover"><div class="arrow"></div><div class="popover-inner"><h3 class="popover-title"></h3><div class="popover-content"><p></p></div></div></div>'})}(window.jQuery),!function(a){function b(b,c){var d=a.proxy(this.process,this),e=a(b).is("body")?a(window):a(b),f;this.options=a.extend({},a.fn.scrollspy.defaults,c),this.$scrollElement=e.on("scroll.scroll.data-api",d),this.selector=(this.options.target||(f=a(b).attr("href"))&&f.replace(/.*(?=#[^\s]+$)/,"")||"")+" .nav li > a",this.$body=a("body").on("click.scroll.data-api",this.selector,d),this.refresh(),this.process()}"use strict",b.prototype={constructor:b,refresh:function(){var b=this,c;this.offsets=a([]),this.targets=a([]),c=this.$body.find(this.selector).map(function(){var b=a(this),c=b.data("target")||b.attr("href"),d=/^#\w/.test(c)&&a(c);return d&&c.length&&[[d.position().top,c]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){b.offsets.push(this[0]),b.targets.push(this[1])})},process:function(){var a=this.$scrollElement.scrollTop()+this.options.offset,b=this.$scrollElement[0].scrollHeight||this.$body[0].scrollHeight,c=b-this.$scrollElement.height(),d=this.offsets,e=this.targets,f=this.activeTarget,g;if(a>=c)return f!=(g=e.last()[0])&&this.activate(g);for(g=d.length;g--;)f!=e[g]&&a>=d[g]&&(!d[g+1]||a<=d[g+1])&&this.activate(e[g])},activate:function(b){var c,d;this.activeTarget=b,a(this.selector).parent(".active").removeClass("active"),d=this.selector+'[data-target="'+b+'"],'+this.selector+'[href="'+b+'"]',c=a(d).parent("li").addClass("active"),c.parent(".dropdown-menu")&&(c=c.closest("li.dropdown").addClass("active")),c.trigger("activate")}},a.fn.scrollspy=function(c){return this.each(function(){var d=a(this),e=d.data("scrollspy"),f=typeof c=="object"&&c;e||d.data("scrollspy",e=new b(this,f)),typeof c=="string"&&e[c]()})},a.fn.scrollspy.Constructor=b,a.fn.scrollspy.defaults={offset:10},a(function(){a('[data-spy="scroll"]').each(function(){var b=a(this);b.scrollspy(b.data())})})}(window.jQuery),!function(a){"use strict";var b=function(b){this.element=a(b)};b.prototype={constructor:b,show:function(){var b=this.element,c=b.closest("ul:not(.dropdown-menu)"),d=b.attr("data-target"),e,f,g;d||(d=b.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,""));if(b.parent("li").hasClass("active"))return;e=c.find(".active a").last()[0],g=a.Event("show",{relatedTarget:e}),b.trigger(g);if(g.isDefaultPrevented())return;f=a(d),this.activate(b.parent("li"),c),this.activate(f,f.parent(),function(){b.trigger({type:"shown",relatedTarget:e})})},activate:function(b,c,d){function g(){e.removeClass("active").find("> .dropdown-menu > .active").removeClass("active"),b.addClass("active"),f?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu")&&b.closest("li.dropdown").addClass("active"),d&&d()}var e=c.find("> .active"),f=d&&a.support.transition&&e.hasClass("fade");f?e.one(a.support.transition.end,g):g(),e.removeClass("in")}},a.fn.tab=function(c){return this.each(function(){var d=a(this),e=d.data("tab");e||d.data("tab",e=new b(this)),typeof c=="string"&&e[c]()})},a.fn.tab.Constructor=b,a(function(){a("body").on("click.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"]',function(b){b.preventDefault(),a(this).tab("show")})})}(window.jQuery),!function(a){"use strict";var b=function(b,c){this.$element=a(b),this.options=a.extend({},a.fn.typeahead.defaults,c),this.matcher=this.options.matcher||this.matcher,this.sorter=this.options.sorter||this.sorter,this.highlighter=this.options.highlighter||this.highlighter,this.updater=this.options.updater||this.updater,this.$menu=a(this.options.menu).appendTo("body"),this.source=this.options.source,this.shown=!1,this.listen()};b.prototype={constructor:b,select:function(){var a=this.$menu.find(".active").attr("data-value");return this.$element.val(this.updater(a)).change(),this.hide()},updater:function(a){return a},show:function(){var b=a.extend({},this.$element.position(),{height:this.$element[0].offsetHeight});return this.$menu.css({top:b.top+b.height,left:b.left}),this.$menu.show(),this.shown=!0,this},hide:function(){return this.$menu.hide(),this.shown=!1,this},lookup:function(b){var c=this,d,e;return this.query=this.$element.val(),this.query?(d=a.grep(this.source,function(a){return c.matcher(a)}),d=this.sorter(d),d.length?this.render(d.slice(0,this.options.items)).show():this.shown?this.hide():this):this.shown?this.hide():this},matcher:function(a){return~a.toLowerCase().indexOf(this.query.toLowerCase())},sorter:function(a){var b=[],c=[],d=[],e;while(e=a.shift())e.toLowerCase().indexOf(this.query.toLowerCase())?~e.indexOf(this.query)?c.push(e):d.push(e):b.push(e);return b.concat(c,d)},highlighter:function(a){var b=this.query.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&");return a.replace(new RegExp("("+b+")","ig"),function(a,b){return"<strong>"+b+"</strong>"})},render:function(b){var c=this;return b=a(b).map(function(b,d){return b=a(c.options.item).attr("data-value",d),b.find("a").html(c.highlighter(d)),b[0]}),b.first().addClass("active"),this.$menu.html(b),this},next:function(b){var c=this.$menu.find(".active").removeClass("active"),d=c.next();d.length||(d=a(this.$menu.find("li")[0])),d.addClass("active")},prev:function(a){var b=this.$menu.find(".active").removeClass("active"),c=b.prev();c.length||(c=this.$menu.find("li").last()),c.addClass("active")},listen:function(){this.$element.on("blur",a.proxy(this.blur,this)).on("keypress",a.proxy(this.keypress,this)).on("keyup",a.proxy(this.keyup,this)),(a.browser.webkit||a.browser.msie)&&this.$element.on("keydown",a.proxy(this.keypress,this)),this.$menu.on("click",a.proxy(this.click,this)).on("mouseenter","li",a.proxy(this.mouseenter,this))},keyup:function(a){switch(a.keyCode){case 40:case 38:break;case 9:case 13:if(!this.shown)return;this.select();break;case 27:if(!this.shown)return;this.hide();break;default:this.lookup()}a.stopPropagation(),a.preventDefault()},keypress:function(a){if(!this.shown)return;switch(a.keyCode){case 9:case 13:case 27:a.preventDefault();break;case 38:if(a.type!="keydown")break;a.preventDefault(),this.prev();break;case 40:if(a.type!="keydown")break;a.preventDefault(),this.next()}a.stopPropagation()},blur:function(a){var b=this;setTimeout(function(){b.hide()},150)},click:function(a){a.stopPropagation(),a.preventDefault(),this.select()},mouseenter:function(b){this.$menu.find(".active").removeClass("active"),a(b.currentTarget).addClass("active")}},a.fn.typeahead=function(c){return this.each(function(){var d=a(this),e=d.data("typeahead"),f=typeof c=="object"&&c;e||d.data("typeahead",e=new b(this,f)),typeof c=="string"&&e[c]()})},a.fn.typeahead.defaults={source:[],items:8,menu:'<ul class="typeahead dropdown-menu"></ul>',item:'<li><a href="#"></a></li>'},a.fn.typeahead.Constructor=b,a(function(){a("body").on("focus.typeahead.data-api",'[data-provide="typeahead"]',function(b){var c=a(this);if(c.data("typeahead"))return;b.preventDefault(),c.typeahead(c.data())})})}(window.jQuery);
\ No newline at end of file
diff --git a/apps/fileupload/static/fileupload/js/canvas-to-blob.min.js b/apps/fileupload/static/fileupload/js/canvas-to-blob.min.js
deleted file mode 100644 (file)
index ab9e668..0000000
+++ /dev/null
@@ -1 +0,0 @@
-(function(a){"use strict";var b=a.HTMLCanvasElement&&a.HTMLCanvasElement.prototype,c=a.BlobBuilder||a.WebKitBlobBuilder||a.MozBlobBuilder,d=c&&a.atob&&a.ArrayBuffer&&a.Uint8Array&&function(a){var b,d,e,f,g,h;a.split(",")[0].indexOf("base64")>=0?b=atob(a.split(",")[1]):b=decodeURIComponent(a.split(",")[1]),d=new ArrayBuffer(b.length),e=new Uint8Array(d);for(f=0;f<b.length;f+=1)e[f]=b.charCodeAt(f);return g=new c,g.append(d),h=a.split(",")[0].split(":")[1].split(";")[0],g.getBlob(h)};a.HTMLCanvasElement&&!b.toBlob&&(b.mozGetAsFile?b.toBlob=function(a,b){a(this.mozGetAsFile("blob",b))}:b.toDataURL&&d&&(b.toBlob=function(a,b){a(d(this.toDataURL(b)))})),typeof define!="undefined"&&define.amd?define(function(){return d}):a.dataURLtoBlob=d})(this);
diff --git a/apps/fileupload/static/fileupload/js/csrf.js b/apps/fileupload/static/fileupload/js/csrf.js
deleted file mode 100644 (file)
index aec99cc..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-// modify jquery ajax to add csrtoken when doing "local" requests
-$('html').ajaxSend(function(event, xhr, settings) {
-    function getCookie(name) {
-        var cookieValue = null;
-        if (document.cookie && document.cookie != '') {
-            var cookies = document.cookie.split(';');
-            for (var i = 0; i < cookies.length; i++) {
-                var cookie = jQuery.trim(cookies[i]);
-                // Does this cookie string begin with the name we want?
-                if (cookie.substring(0, name.length + 1) == (name + '=')) {
-                    cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
-                    break;
-                }
-            }
-        }
-        return cookieValue;
-    }
-    if (!(/^http:.*/.test(settings.url) || /^https:.*/.test(settings.url))) {
-        // Only send the token to relative URLs i.e. locally.
-        xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
-    }
-});
diff --git a/apps/fileupload/static/fileupload/js/jquery.fileupload-fp.js b/apps/fileupload/static/fileupload/js/jquery.fileupload-fp.js
deleted file mode 100644 (file)
index 634fb5e..0000000
+++ /dev/null
@@ -1,219 +0,0 @@
-/*
- * jQuery File Upload File Processing Plugin 1.0
- * https://github.com/blueimp/jQuery-File-Upload
- *
- * Copyright 2012, Sebastian Tschan
- * https://blueimp.net
- *
- * Licensed under the MIT license:
- * http://www.opensource.org/licenses/MIT
- */
-
-/*jslint nomen: true, unparam: true, regexp: true */
-/*global define, window, document */
-
-(function (factory) {
-    'use strict';
-    if (typeof define === 'function' && define.amd) {
-        // Register as an anonymous AMD module:
-        define([
-            'jquery',
-            'load-image',
-            'canvas-to-blob',
-            './jquery.fileupload'
-        ], factory);
-    } else {
-        // Browser globals:
-        factory(
-            window.jQuery,
-            window.loadImage
-        );
-    }
-}(function ($, loadImage) {
-    'use strict';
-
-    // The File Upload IP version extends the basic fileupload widget
-    // with file processing functionality:
-    $.widget('blueimpFP.fileupload', $.blueimp.fileupload, {
-
-        options: {
-            // The list of file processing actions:
-            process: [
-            /*
-                {
-                    action: 'load',
-                    fileTypes: /^image\/(gif|jpeg|png)$/,
-                    maxFileSize: 20000000 // 20MB
-                },
-                {
-                    action: 'resize',
-                    maxWidth: 1920,
-                    maxHeight: 1200,
-                    minWidth: 800,
-                    minHeight: 600
-                },
-                {
-                    action: 'save'
-                }
-            */
-            ],
-
-            // The add callback is invoked as soon as files are added to the
-            // fileupload widget (via file input selection, drag & drop or add
-            // API call). See the basic file upload widget for more information:
-            add: function (e, data) {
-                $(this).fileupload('process', data).done(function () {
-                    data.submit();
-                });
-            }
-        },
-
-        processActions: {
-            // Loads the image given via data.files and data.index
-            // as canvas element.
-            // Accepts the options fileTypes (regular expression)
-            // and maxFileSize (integer) to limit the files to load:
-            load: function (data, options) {
-                var that = this,
-                    file = data.files[data.index],
-                    dfd = $.Deferred();
-                if (window.HTMLCanvasElement &&
-                        window.HTMLCanvasElement.prototype.toBlob &&
-                        ($.type(options.maxFileSize) !== 'number' ||
-                            file.size < options.maxFileSize) &&
-                        (!options.fileTypes ||
-                            options.fileTypes.test(file.type))) {
-                    loadImage(
-                        file,
-                        function (canvas) {
-                            data.canvas = canvas;
-                            dfd.resolveWith(that, [data]);
-                        },
-                        {canvas: true}
-                    );
-                } else {
-                    dfd.rejectWith(that, [data]);
-                }
-                return dfd.promise();
-            },
-            // Resizes the image given as data.canvas and updates
-            // data.canvas with the resized image.
-            // Accepts the options maxWidth, maxHeight, minWidth and
-            // minHeight to scale the given image:
-            resize: function (data, options) {
-                if (data.canvas) {
-                    var canvas = loadImage.scale(data.canvas, options);
-                    if (canvas.width !== data.canvas.width ||
-                            canvas.height !== data.canvas.height) {
-                        data.canvas = canvas;
-                        data.processed = true;
-                    }
-                }
-                return data;
-            },
-            // Saves the processed image given as data.canvas
-            // inplace at data.index of data.files:
-            save: function (data, options) {
-                // Do nothing if no processing has happened:
-                if (!data.canvas || !data.processed) {
-                    return data;
-                }
-                var that = this,
-                    file = data.files[data.index],
-                    name = file.name,
-                    dfd = $.Deferred(),
-                    callback = function (blob) {
-                        if (!blob.name) {
-                            if (file.type === blob.type) {
-                                blob.name = file.name;
-                            } else if (file.name) {
-                                blob.name = file.name.replace(
-                                    /\..+$/,
-                                    '.' + blob.type.substr(6)
-                                );
-                            }
-                        }
-                        // Store the created blob at the position
-                        // of the original file in the files list:
-                        data.files[data.index] = blob;
-                        dfd.resolveWith(that, [data]);
-                    };
-                // Use canvas.mozGetAsFile directly, to retain the filename, as
-                // Gecko doesn't support the filename option for FormData.append:
-                if (data.canvas.mozGetAsFile) {
-                    callback(data.canvas.mozGetAsFile(
-                        (/^image\/(jpeg|png)$/.test(file.type) && name) ||
-                            ((name && name.replace(/\..+$/, '')) ||
-                                'blob') + '.png',
-                        file.type
-                    ));
-                } else {
-                    data.canvas.toBlob(callback, file.type);
-                }
-                return dfd.promise();
-            }
-        },
-
-        // Resizes the file at the given index and stores the created blob at
-        // the original position of the files list, returns a Promise object:
-        _processFile: function (files, index, options) {
-            var that = this,
-                dfd = $.Deferred().resolveWith(that, [{
-                    files: files,
-                    index: index
-                }]),
-                chain = dfd.promise();
-            that._processing += 1;
-            $.each(options.process, function (i, settings) {
-                chain = chain.pipe(function (data) {
-                    return that.processActions[settings.action]
-                        .call(this, data, settings);
-                });
-            });
-            chain.always(function () {
-                that._processing -= 1;
-                if (that._processing === 0) {
-                    that.element
-                        .removeClass('fileupload-processing');
-                }
-            });
-            if (that._processing === 1) {
-                that.element.addClass('fileupload-processing');
-            }
-            return chain;
-        },
-
-        // Processes the files given as files property of the data parameter,
-        // returns a Promise object that allows to bind a done handler, which
-        // will be invoked after processing all files (inplace) is done:
-        process: function (data) {
-            var that = this,
-                options = $.extend({}, this.options, data);
-            if (options.process && options.process.length &&
-                    this._isXHRUpload(options)) {
-                $.each(data.files, function (index, file) {
-                    that._processingQueue = that._processingQueue.pipe(
-                        function () {
-                            var dfd = $.Deferred();
-                            that._processFile(data.files, index, options)
-                                .always(function () {
-                                    dfd.resolveWith(that);
-                                });
-                            return dfd.promise();
-                        }
-                    );
-                });
-            }
-            return this._processingQueue;
-        },
-
-        _create: function () {
-            $.blueimp.fileupload.prototype._create.call(this);
-            this._processing = 0;
-            this._processingQueue = $.Deferred().resolveWith(this)
-                .promise();
-        }
-
-    });
-
-}));
diff --git a/apps/fileupload/static/fileupload/js/jquery.fileupload-ui.js b/apps/fileupload/static/fileupload/js/jquery.fileupload-ui.js
deleted file mode 100644 (file)
index f7fc3bf..0000000
+++ /dev/null
@@ -1,691 +0,0 @@
-/*
- * jQuery File Upload User Interface Plugin 6.8.1
- * https://github.com/blueimp/jQuery-File-Upload
- *
- * Copyright 2010, Sebastian Tschan
- * https://blueimp.net
- *
- * Licensed under the MIT license:
- * http://www.opensource.org/licenses/MIT
- */
-
-/*jslint nomen: true, unparam: true, regexp: true */
-/*global define, window, document, URL, webkitURL, FileReader */
-
-(function (factory) {
-    'use strict';
-    if (typeof define === 'function' && define.amd) {
-        // Register as an anonymous AMD module:
-        define([
-            'jquery',
-            'tmpl',
-            'load-image',
-            './jquery.fileupload-fp'
-        ], factory);
-    } else {
-        // Browser globals:
-        factory(
-            window.jQuery,
-            window.tmpl,
-            window.loadImage
-        );
-    }
-}(function ($, tmpl, loadImage) {
-    'use strict';
-
-    // The UI version extends the FP (file processing) version or the basic
-    // file upload widget and adds complete user interface interaction:
-    var parentWidget = ($.blueimpFP || $.blueimp).fileupload;
-    $.widget('blueimpUI.fileupload', parentWidget, {
-
-        options: {
-            // By default, files added to the widget are uploaded as soon
-            // as the user clicks on the start buttons. To enable automatic
-            // uploads, set the following option to true:
-            autoUpload: false,
-            // The following option limits the number of files that are
-            // allowed to be uploaded using this widget:
-            maxNumberOfFiles: undefined,
-            // The maximum allowed file size:
-            maxFileSize: undefined,
-            // The minimum allowed file size:
-            minFileSize: undefined,
-            // The regular expression for allowed file types, matches
-            // against either file type or file name:
-            acceptFileTypes:  /.+$/i,
-            // The regular expression to define for which files a preview
-            // image is shown, matched against the file type:
-            previewSourceFileTypes: /^image\/(gif|jpeg|png)$/,
-            // The maximum file size of images that are to be displayed as preview:
-            previewSourceMaxFileSize: 5000000, // 5MB
-            // The maximum width of the preview images:
-            previewMaxWidth: 80,
-            // The maximum height of the preview images:
-            previewMaxHeight: 80,
-            // By default, preview images are displayed as canvas elements
-            // if supported by the browser. Set the following option to false
-            // to always display preview images as img elements:
-            previewAsCanvas: true,
-            // The ID of the upload template:
-            uploadTemplateId: 'template-upload',
-            // The ID of the download template:
-            downloadTemplateId: 'template-download',
-            // The container for the list of files. If undefined, it is set to
-            // an element with class "files" inside of the widget element:
-            filesContainer: undefined,
-            // By default, files are appended to the files container.
-            // Set the following option to true, to prepend files instead:
-            prependFiles: false,
-            // The expected data type of the upload response, sets the dataType
-            // option of the $.ajax upload requests:
-            dataType: 'json',
-
-            // The add callback is invoked as soon as files are added to the fileupload
-            // widget (via file input selection, drag & drop or add API call).
-            // See the basic file upload widget for more information:
-            add: function (e, data) {
-                var that = $(this).data('fileupload'),
-                    options = that.options,
-                    files = data.files;
-                $(this).fileupload('process', data).done(function () {
-                    that._adjustMaxNumberOfFiles(-files.length);
-                    data.isAdjusted = true;
-                    data.files.valid = data.isValidated = that._validate(files);
-                    data.context = that._renderUpload(files).data('data', data);
-                    options.filesContainer[
-                        options.prependFiles ? 'prepend' : 'append'
-                    ](data.context);
-                    that._renderPreviews(files, data.context);
-                    that._forceReflow(data.context);
-                    that._transition(data.context).done(
-                        function () {
-                            if ((that._trigger('added', e, data) !== false) &&
-                                    (options.autoUpload || data.autoUpload) &&
-                                    data.autoUpload !== false && data.isValidated) {
-                                data.submit();
-                            }
-                        }
-                    );
-                });
-            },
-            // Callback for the start of each file upload request:
-            send: function (e, data) {
-                var that = $(this).data('fileupload');
-                if (!data.isValidated) {
-                    if (!data.isAdjusted) {
-                        that._adjustMaxNumberOfFiles(-data.files.length);
-                    }
-                    if (!that._validate(data.files)) {
-                        return false;
-                    }
-                }
-                if (data.context && data.dataType &&
-                        data.dataType.substr(0, 6) === 'iframe') {
-                    // Iframe Transport does not support progress events.
-                    // In lack of an indeterminate progress bar, we set
-                    // the progress to 100%, showing the full animated bar:
-                    data.context
-                        .find('.progress').addClass(
-                            !$.support.transition && 'progress-animated'
-                        )
-                        .find('.bar').css(
-                            'width',
-                            parseInt(100, 10) + '%'
-                        );
-                }
-                return that._trigger('sent', e, data);
-            },
-            // Callback for successful uploads:
-            done: function (e, data) {
-                var that = $(this).data('fileupload'),
-                    template;
-                if (data.context) {
-                    data.context.each(function (index) {
-                        var file = ($.isArray(data.result) &&
-                                data.result[index]) || {error: 'emptyResult'};
-                        if (file.error) {
-                            that._adjustMaxNumberOfFiles(1);
-                        }
-                        that._transition($(this)).done(
-                            function () {
-                                var node = $(this);
-                                template = that._renderDownload([file])
-                                    .css('height', node.height())
-                                    .replaceAll(node);
-                                that._forceReflow(template);
-                                that._transition(template).done(
-                                    function () {
-                                        data.context = $(this);
-                                        that._trigger('completed', e, data);
-                                    }
-                                );
-                            }
-                        );
-                    });
-                } else {
-                    template = that._renderDownload(data.result)
-                        .appendTo(that.options.filesContainer);
-                    that._forceReflow(template);
-                    that._transition(template).done(
-                        function () {
-                            data.context = $(this);
-                            that._trigger('completed', e, data);
-                        }
-                    );
-                }
-            },
-            // Callback for failed (abort or error) uploads:
-            fail: function (e, data) {
-                var that = $(this).data('fileupload'),
-                    template;
-                that._adjustMaxNumberOfFiles(data.files.length);
-                if (data.context) {
-                    data.context.each(function (index) {
-                        if (data.errorThrown !== 'abort') {
-                            var file = data.files[index];
-                            file.error = file.error || data.errorThrown ||
-                                true;
-                            that._transition($(this)).done(
-                                function () {
-                                    var node = $(this);
-                                    template = that._renderDownload([file])
-                                        .replaceAll(node);
-                                    that._forceReflow(template);
-                                    that._transition(template).done(
-                                        function () {
-                                            data.context = $(this);
-                                            that._trigger('failed', e, data);
-                                        }
-                                    );
-                                }
-                            );
-                        } else {
-                            that._transition($(this)).done(
-                                function () {
-                                    $(this).remove();
-                                    that._trigger('failed', e, data);
-                                }
-                            );
-                        }
-                    });
-                } else if (data.errorThrown !== 'abort') {
-                    that._adjustMaxNumberOfFiles(-data.files.length);
-                    data.context = that._renderUpload(data.files)
-                        .appendTo(that.options.filesContainer)
-                        .data('data', data);
-                    that._forceReflow(data.context);
-                    that._transition(data.context).done(
-                        function () {
-                            data.context = $(this);
-                            that._trigger('failed', e, data);
-                        }
-                    );
-                } else {
-                    that._trigger('failed', e, data);
-                }
-            },
-            // Callback for upload progress events:
-            progress: function (e, data) {
-                if (data.context) {
-                    data.context.find('.bar').css(
-                        'width',
-                        parseInt(data.loaded / data.total * 100, 10) + '%'
-                    );
-                }
-            },
-            // Callback for global upload progress events:
-            progressall: function (e, data) {
-                var $this = $(this);
-                $this.find('.fileupload-progress')
-                    .find('.bar').css(
-                        'width',
-                        parseInt(data.loaded / data.total * 100, 10) + '%'
-                    ).end()
-                    .find('.progress-extended').each(function () {
-                        $(this).html(
-                            $this.data('fileupload')
-                                ._renderExtendedProgress(data)
-                        );
-                    });
-            },
-            // Callback for uploads start, equivalent to the global ajaxStart event:
-            start: function (e) {
-                var that = $(this).data('fileupload');
-                that._transition($(this).find('.fileupload-progress')).done(
-                    function () {
-                        that._trigger('started', e);
-                    }
-                );
-            },
-            // Callback for uploads stop, equivalent to the global ajaxStop event:
-            stop: function (e) {
-                var that = $(this).data('fileupload');
-                that._transition($(this).find('.fileupload-progress')).done(
-                    function () {
-                        $(this).find('.bar').css('width', '0%');
-                        $(this).find('.progress-extended').html('&nbsp;');
-                        that._trigger('stopped', e);
-                    }
-                );
-            },
-            // Callback for file deletion:
-            destroy: function (e, data) {
-                var that = $(this).data('fileupload');
-                if (data.url) {
-                    $.ajax(data);
-                }
-                that._adjustMaxNumberOfFiles(1);
-                that._transition(data.context).done(
-                    function () {
-                        $(this).remove();
-                        that._trigger('destroyed', e, data);
-                    }
-                );
-            }
-        },
-
-        // Link handler, that allows to download files
-        // by drag & drop of the links to the desktop:
-        _enableDragToDesktop: function () {
-            var link = $(this),
-                url = link.prop('href'),
-                name = link.prop('download'),
-                type = 'application/octet-stream';
-            link.bind('dragstart', function (e) {
-                try {
-                    e.originalEvent.dataTransfer.setData(
-                        'DownloadURL',
-                        [type, name, url].join(':')
-                    );
-                } catch (err) {}
-            });
-        },
-
-        _adjustMaxNumberOfFiles: function (operand) {
-            if (typeof this.options.maxNumberOfFiles === 'number') {
-                this.options.maxNumberOfFiles += operand;
-                if (this.options.maxNumberOfFiles < 1) {
-                    this._disableFileInputButton();
-                } else {
-                    this._enableFileInputButton();
-                }
-            }
-        },
-
-        _formatFileSize: function (bytes) {
-            if (typeof bytes !== 'number') {
-                return '';
-            }
-            if (bytes >= 1000000000) {
-                return (bytes / 1000000000).toFixed(2) + ' GB';
-            }
-            if (bytes >= 1000000) {
-                return (bytes / 1000000).toFixed(2) + ' MB';
-            }
-            return (bytes / 1000).toFixed(2) + ' KB';
-        },
-
-        _formatBitrate: function (bits) {
-            if (typeof bits !== 'number') {
-                return '';
-            }
-            if (bits >= 1000000000) {
-                return (bits / 1000000000).toFixed(2) + ' Gbit/s';
-            }
-            if (bits >= 1000000) {
-                return (bits / 1000000).toFixed(2) + ' Mbit/s';
-            }
-            if (bits >= 1000) {
-                return (bits / 1000).toFixed(2) + ' kbit/s';
-            }
-            return bits + ' bit/s';
-        },
-
-        _formatTime: function (seconds) {
-            var date = new Date(seconds * 1000),
-                days = parseInt(seconds / 86400, 10);
-            days = days ? days + 'd ' : '';
-            return days +
-                ('0' + date.getUTCHours()).slice(-2) + ':' +
-                ('0' + date.getUTCMinutes()).slice(-2) + ':' +
-                ('0' + date.getUTCSeconds()).slice(-2);
-        },
-
-        _formatPercentage: function (floatValue) {
-            return (floatValue * 100).toFixed(2) + ' %';
-        },
-
-        _renderExtendedProgress: function (data) {
-            return this._formatBitrate(data.bitrate) + ' | ' +
-                this._formatTime(
-                    (data.total - data.loaded) * 8 / data.bitrate
-                ) + ' | ' +
-                this._formatPercentage(
-                    data.loaded / data.total
-                ) + ' | ' +
-                this._formatFileSize(data.loaded) + ' / ' +
-                this._formatFileSize(data.total);
-        },
-
-        _hasError: function (file) {
-            if (file.error) {
-                return file.error;
-            }
-            // The number of added files is subtracted from
-            // maxNumberOfFiles before validation, so we check if
-            // maxNumberOfFiles is below 0 (instead of below 1):
-            if (this.options.maxNumberOfFiles < 0) {
-                return 'maxNumberOfFiles';
-            }
-            // Files are accepted if either the file type or the file name
-            // matches against the acceptFileTypes regular expression, as
-            // only browsers with support for the File API report the type:
-            if (!(this.options.acceptFileTypes.test(file.type) ||
-                    this.options.acceptFileTypes.test(file.name))) {
-                return 'acceptFileTypes';
-            }
-            if (this.options.maxFileSize &&
-                    file.size > this.options.maxFileSize) {
-                return 'maxFileSize';
-            }
-            if (typeof file.size === 'number' &&
-                    file.size < this.options.minFileSize) {
-                return 'minFileSize';
-            }
-            return null;
-        },
-
-        _validate: function (files) {
-            var that = this,
-                valid = !!files.length;
-            $.each(files, function (index, file) {
-                file.error = that._hasError(file);
-                if (file.error) {
-                    valid = false;
-                }
-            });
-            return valid;
-        },
-
-        _renderTemplate: function (func, files) {
-            if (!func) {
-                return $();
-            }
-            var result = func({
-                files: files,
-                formatFileSize: this._formatFileSize,
-                options: this.options
-            });
-            if (result instanceof $) {
-                return result;
-            }
-            return $(this.options.templatesContainer).html(result).children();
-        },
-
-        _renderPreview: function (file, node) {
-            var that = this,
-                options = this.options,
-                dfd = $.Deferred();
-            return ((loadImage && loadImage(
-                file,
-                function (img) {
-                    node.append(img);
-                    that._forceReflow(node);
-                    that._transition(node).done(function () {
-                        dfd.resolveWith(node);
-                    });
-                    if (!$.contains(document.body, node[0])) {
-                        // If the element is not part of the DOM,
-                        // transition events are not triggered,
-                        // so we have to resolve manually:
-                        dfd.resolveWith(node);
-                    }
-                },
-                {
-                    maxWidth: options.previewMaxWidth,
-                    maxHeight: options.previewMaxHeight,
-                    canvas: options.previewAsCanvas
-                }
-            )) || dfd.resolveWith(node)) && dfd;
-        },
-
-        _renderPreviews: function (files, nodes) {
-            var that = this,
-                options = this.options;
-            nodes.find('.preview span').each(function (index, element) {
-                var file = files[index];
-                if (options.previewSourceFileTypes.test(file.type) &&
-                        ($.type(options.previewSourceMaxFileSize) !== 'number' ||
-                        file.size < options.previewSourceMaxFileSize)) {
-                    that._processingQueue = that._processingQueue.pipe(function () {
-                        var dfd = $.Deferred();
-                        that._renderPreview(file, $(element)).done(
-                            function () {
-                                dfd.resolveWith(that);
-                            }
-                        );
-                        return dfd.promise();
-                    });
-                }
-            });
-            return this._processingQueue;
-        },
-
-        _renderUpload: function (files) {
-            return this._renderTemplate(
-                this.options.uploadTemplate,
-                files
-            );
-        },
-
-        _renderDownload: function (files) {
-            return this._renderTemplate(
-                this.options.downloadTemplate,
-                files
-            ).find('a[download]').each(this._enableDragToDesktop).end();
-        },
-
-        _startHandler: function (e) {
-            e.preventDefault();
-            var button = $(this),
-                template = button.closest('.template-upload'),
-                data = template.data('data');
-            if (data && data.submit && !data.jqXHR && data.submit()) {
-                button.prop('disabled', true);
-            }
-        },
-
-        _cancelHandler: function (e) {
-            e.preventDefault();
-            var template = $(this).closest('.template-upload'),
-                data = template.data('data') || {};
-            if (!data.jqXHR) {
-                data.errorThrown = 'abort';
-                e.data.fileupload._trigger('fail', e, data);
-            } else {
-                data.jqXHR.abort();
-            }
-        },
-
-        _deleteHandler: function (e) {
-            e.preventDefault();
-            var button = $(this);
-            e.data.fileupload._trigger('destroy', e, {
-                context: button.closest('.template-download'),
-                url: button.attr('data-url'),
-                type: button.attr('data-type') || 'DELETE',
-                dataType: e.data.fileupload.options.dataType
-            });
-        },
-
-        _forceReflow: function (node) {
-            this._reflow = $.support.transition &&
-                node.length && node[0].offsetWidth;
-        },
-
-        _transition: function (node) {
-            var dfd = $.Deferred();
-            if ($.support.transition && node.hasClass('fade')) {
-                node.bind(
-                    $.support.transition.end,
-                    function (e) {
-                        // Make sure we don't respond to other transitions events
-                        // in the container element, e.g. from button elements:
-                        if (e.target === node[0]) {
-                            node.unbind($.support.transition.end);
-                            dfd.resolveWith(node);
-                        }
-                    }
-                ).toggleClass('in');
-            } else {
-                node.toggleClass('in');
-                dfd.resolveWith(node);
-            }
-            return dfd;
-        },
-
-        _initButtonBarEventHandlers: function () {
-            var fileUploadButtonBar = this.element.find('.fileupload-buttonbar'),
-                filesList = this.options.filesContainer,
-                ns = this.options.namespace;
-            fileUploadButtonBar.find('.start')
-                .bind('click.' + ns, function (e) {
-                    e.preventDefault();
-                    filesList.find('.start button').click();
-                });
-            fileUploadButtonBar.find('.cancel')
-                .bind('click.' + ns, function (e) {
-                    e.preventDefault();
-                    filesList.find('.cancel button').click();
-                });
-            fileUploadButtonBar.find('.delete')
-                .bind('click.' + ns, function (e) {
-                    e.preventDefault();
-                    filesList.find('.delete input:checked')
-                        .siblings('button').click();
-                    fileUploadButtonBar.find('.toggle')
-                        .prop('checked', false);
-                });
-            fileUploadButtonBar.find('.toggle')
-                .bind('change.' + ns, function (e) {
-                    filesList.find('.delete input').prop(
-                        'checked',
-                        $(this).is(':checked')
-                    );
-                });
-        },
-
-        _destroyButtonBarEventHandlers: function () {
-            this.element.find('.fileupload-buttonbar button')
-                .unbind('click.' + this.options.namespace);
-            this.element.find('.fileupload-buttonbar .toggle')
-                .unbind('change.' + this.options.namespace);
-        },
-
-        _initEventHandlers: function () {
-            parentWidget.prototype._initEventHandlers.call(this);
-            var eventData = {fileupload: this};
-            this.options.filesContainer
-                .delegate(
-                    '.start button',
-                    'click.' + this.options.namespace,
-                    eventData,
-                    this._startHandler
-                )
-                .delegate(
-                    '.cancel button',
-                    'click.' + this.options.namespace,
-                    eventData,
-                    this._cancelHandler
-                )
-                .delegate(
-                    '.delete button',
-                    'click.' + this.options.namespace,
-                    eventData,
-                    this._deleteHandler
-                );
-            this._initButtonBarEventHandlers();
-        },
-
-        _destroyEventHandlers: function () {
-            var options = this.options;
-            this._destroyButtonBarEventHandlers();
-            options.filesContainer
-                .undelegate('.start button', 'click.' + options.namespace)
-                .undelegate('.cancel button', 'click.' + options.namespace)
-                .undelegate('.delete button', 'click.' + options.namespace);
-            parentWidget.prototype._destroyEventHandlers.call(this);
-        },
-
-        _enableFileInputButton: function () {
-            this.element.find('.fileinput-button input')
-                .prop('disabled', false)
-                .parent().removeClass('disabled');
-        },
-
-        _disableFileInputButton: function () {
-            this.element.find('.fileinput-button input')
-                .prop('disabled', true)
-                .parent().addClass('disabled');
-        },
-
-        _initTemplates: function () {
-            var options = this.options;
-            options.templatesContainer = document.createElement(
-                options.filesContainer.prop('nodeName')
-            );
-            if (tmpl) {
-                if (options.uploadTemplateId) {
-                    options.uploadTemplate = tmpl(options.uploadTemplateId);
-                }
-                if (options.downloadTemplateId) {
-                    options.downloadTemplate = tmpl(options.downloadTemplateId);
-                }
-            }
-        },
-
-        _initFilesContainer: function () {
-            var options = this.options;
-            if (options.filesContainer === undefined) {
-                options.filesContainer = this.element.find('.files');
-            } else if (!(options.filesContainer instanceof $)) {
-                options.filesContainer = $(options.filesContainer);
-            }
-        },
-
-        _initSpecialOptions: function () {
-            parentWidget.prototype._initSpecialOptions.call(this);
-            this._initFilesContainer();
-            this._initTemplates();
-        },
-
-        _create: function () {
-            parentWidget.prototype._create.call(this);
-            this._refreshOptionsList.push(
-                'filesContainer',
-                'uploadTemplateId',
-                'downloadTemplateId'
-            );
-            if (!$.blueimpFP) {
-                this._processingQueue = $.Deferred().resolveWith(this).promise();
-                this.process = function () {
-                    return this._processingQueue;
-                };
-            }
-        },
-
-        enable: function () {
-            parentWidget.prototype.enable.call(this);
-            this.element.find('input, button').prop('disabled', false);
-            this._enableFileInputButton();
-        },
-
-        disable: function () {
-            this.element.find('input, button').prop('disabled', true);
-            this._disableFileInputButton();
-            parentWidget.prototype.disable.call(this);
-        }
-
-    });
-
-}));
diff --git a/apps/fileupload/static/fileupload/js/jquery.fileupload.js b/apps/fileupload/static/fileupload/js/jquery.fileupload.js
deleted file mode 100644 (file)
index 05a654b..0000000
+++ /dev/null
@@ -1,949 +0,0 @@
-/*
- * jQuery File Upload Plugin 5.11.2
- * https://github.com/blueimp/jQuery-File-Upload
- *
- * Copyright 2010, Sebastian Tschan
- * https://blueimp.net
- *
- * Licensed under the MIT license:
- * http://www.opensource.org/licenses/MIT
- */
-
-/*jslint nomen: true, unparam: true, regexp: true */
-/*global define, window, document, Blob, FormData, location */
-
-(function (factory) {
-    'use strict';
-    if (typeof define === 'function' && define.amd) {
-        // Register as an anonymous AMD module:
-        define([
-            'jquery',
-            'jquery.ui.widget'
-        ], factory);
-    } else {
-        // Browser globals:
-        factory(window.jQuery);
-    }
-}(function ($) {
-    'use strict';
-
-    // The FileReader API is not actually used, but works as feature detection,
-    // as e.g. Safari supports XHR file uploads via the FormData API,
-    // but not non-multipart XHR file uploads:
-    $.support.xhrFileUpload = !!(window.XMLHttpRequestUpload && window.FileReader);
-    $.support.xhrFormDataFileUpload = !!window.FormData;
-
-    // The fileupload widget listens for change events on file input fields defined
-    // via fileInput setting and paste or drop events of the given dropZone.
-    // In addition to the default jQuery Widget methods, the fileupload widget
-    // exposes the "add" and "send" methods, to add or directly send files using
-    // the fileupload API.
-    // By default, files added via file input selection, paste, drag & drop or
-    // "add" method are uploaded immediately, but it is possible to override
-    // the "add" callback option to queue file uploads.
-    $.widget('blueimp.fileupload', {
-
-        options: {
-            // The namespace used for event handler binding on the dropZone and
-            // fileInput collections.
-            // If not set, the name of the widget ("fileupload") is used.
-            namespace: undefined,
-            // The drop target collection, by the default the complete document.
-            // Set to null or an empty collection to disable drag & drop support:
-            dropZone: $(document),
-            // The file input field collection, that is listened for change events.
-            // If undefined, it is set to the file input fields inside
-            // of the widget element on plugin initialization.
-            // Set to null or an empty collection to disable the change listener.
-            fileInput: undefined,
-            // By default, the file input field is replaced with a clone after
-            // each input field change event. This is required for iframe transport
-            // queues and allows change events to be fired for the same file
-            // selection, but can be disabled by setting the following option to false:
-            replaceFileInput: true,
-            // The parameter name for the file form data (the request argument name).
-            // If undefined or empty, the name property of the file input field is
-            // used, or "files[]" if the file input name property is also empty,
-            // can be a string or an array of strings:
-            paramName: undefined,
-            // By default, each file of a selection is uploaded using an individual
-            // request for XHR type uploads. Set to false to upload file
-            // selections in one request each:
-            singleFileUploads: true,
-            // To limit the number of files uploaded with one XHR request,
-            // set the following option to an integer greater than 0:
-            limitMultiFileUploads: undefined,
-            // Set the following option to true to issue all file upload requests
-            // in a sequential order:
-            sequentialUploads: false,
-            // To limit the number of concurrent uploads,
-            // set the following option to an integer greater than 0:
-            limitConcurrentUploads: undefined,
-            // Set the following option to true to force iframe transport uploads:
-            forceIframeTransport: false,
-            // Set the following option to the location of a redirect url on the
-            // origin server, for cross-domain iframe transport uploads:
-            redirect: undefined,
-            // The parameter name for the redirect url, sent as part of the form
-            // data and set to 'redirect' if this option is empty:
-            redirectParamName: undefined,
-            // Set the following option to the location of a postMessage window,
-            // to enable postMessage transport uploads:
-            postMessage: undefined,
-            // By default, XHR file uploads are sent as multipart/form-data.
-            // The iframe transport is always using multipart/form-data.
-            // Set to false to enable non-multipart XHR uploads:
-            multipart: true,
-            // To upload large files in smaller chunks, set the following option
-            // to a preferred maximum chunk size. If set to 0, null or undefined,
-            // or the browser does not support the required Blob API, files will
-            // be uploaded as a whole.
-            maxChunkSize: undefined,
-            // When a non-multipart upload or a chunked multipart upload has been
-            // aborted, this option can be used to resume the upload by setting
-            // it to the size of the already uploaded bytes. This option is most
-            // useful when modifying the options object inside of the "add" or
-            // "send" callbacks, as the options are cloned for each file upload.
-            uploadedBytes: undefined,
-            // By default, failed (abort or error) file uploads are removed from the
-            // global progress calculation. Set the following option to false to
-            // prevent recalculating the global progress data:
-            recalculateProgress: true,
-            // Interval in milliseconds to calculate and trigger progress events:
-            progressInterval: 100,
-            // Interval in milliseconds to calculate progress bitrate:
-            bitrateInterval: 500,
-
-            // Additional form data to be sent along with the file uploads can be set
-            // using this option, which accepts an array of objects with name and
-            // value properties, a function returning such an array, a FormData
-            // object (for XHR file uploads), or a simple object.
-            // The form of the first fileInput is given as parameter to the function:
-            formData: function (form) {
-                return form.serializeArray();
-            },
-
-            // The add callback is invoked as soon as files are added to the fileupload
-            // widget (via file input selection, drag & drop, paste or add API call).
-            // If the singleFileUploads option is enabled, this callback will be
-            // called once for each file in the selection for XHR file uplaods, else
-            // once for each file selection.
-            // The upload starts when the submit method is invoked on the data parameter.
-            // The data object contains a files property holding the added files
-            // and allows to override plugin options as well as define ajax settings.
-            // Listeners for this callback can also be bound the following way:
-            // .bind('fileuploadadd', func);
-            // data.submit() returns a Promise object and allows to attach additional
-            // handlers using jQuery's Deferred callbacks:
-            // data.submit().done(func).fail(func).always(func);
-            add: function (e, data) {
-                data.submit();
-            },
-
-            // Other callbacks:
-            // Callback for the submit event of each file upload:
-            // submit: function (e, data) {}, // .bind('fileuploadsubmit', func);
-            // Callback for the start of each file upload request:
-            // send: function (e, data) {}, // .bind('fileuploadsend', func);
-            // Callback for successful uploads:
-            // done: function (e, data) {}, // .bind('fileuploaddone', func);
-            // Callback for failed (abort or error) uploads:
-            // fail: function (e, data) {}, // .bind('fileuploadfail', func);
-            // Callback for completed (success, abort or error) requests:
-            // always: function (e, data) {}, // .bind('fileuploadalways', func);
-            // Callback for upload progress events:
-            // progress: function (e, data) {}, // .bind('fileuploadprogress', func);
-            // Callback for global upload progress events:
-            // progressall: function (e, data) {}, // .bind('fileuploadprogressall', func);
-            // Callback for uploads start, equivalent to the global ajaxStart event:
-            // start: function (e) {}, // .bind('fileuploadstart', func);
-            // Callback for uploads stop, equivalent to the global ajaxStop event:
-            // stop: function (e) {}, // .bind('fileuploadstop', func);
-            // Callback for change events of the fileInput collection:
-            // change: function (e, data) {}, // .bind('fileuploadchange', func);
-            // Callback for paste events to the dropZone collection:
-            // paste: function (e, data) {}, // .bind('fileuploadpaste', func);
-            // Callback for drop events of the dropZone collection:
-            // drop: function (e, data) {}, // .bind('fileuploaddrop', func);
-            // Callback for dragover events of the dropZone collection:
-            // dragover: function (e) {}, // .bind('fileuploaddragover', func);
-
-            // The plugin options are used as settings object for the ajax calls.
-            // The following are jQuery ajax settings required for the file uploads:
-            processData: false,
-            contentType: false,
-            cache: false
-        },
-
-        // A list of options that require a refresh after assigning a new value:
-        _refreshOptionsList: [
-            'namespace',
-            'dropZone',
-            'fileInput',
-            'multipart',
-            'forceIframeTransport'
-        ],
-
-        _BitrateTimer: function () {
-            this.timestamp = +(new Date());
-            this.loaded = 0;
-            this.bitrate = 0;
-            this.getBitrate = function (now, loaded, interval) {
-                var timeDiff = now - this.timestamp;
-                if (!this.bitrate || !interval || timeDiff > interval) {
-                    this.bitrate = (loaded - this.loaded) * (1000 / timeDiff) * 8;
-                    this.loaded = loaded;
-                    this.timestamp = now;
-                }
-                return this.bitrate;
-            };
-        },
-
-        _isXHRUpload: function (options) {
-            return !options.forceIframeTransport &&
-                ((!options.multipart && $.support.xhrFileUpload) ||
-                $.support.xhrFormDataFileUpload);
-        },
-
-        _getFormData: function (options) {
-            var formData;
-            if (typeof options.formData === 'function') {
-                return options.formData(options.form);
-            }
-                       if ($.isArray(options.formData)) {
-                return options.formData;
-            }
-                       if (options.formData) {
-                formData = [];
-                $.each(options.formData, function (name, value) {
-                    formData.push({name: name, value: value});
-                });
-                return formData;
-            }
-            return [];
-        },
-
-        _getTotal: function (files) {
-            var total = 0;
-            $.each(files, function (index, file) {
-                total += file.size || 1;
-            });
-            return total;
-        },
-
-        _onProgress: function (e, data) {
-            if (e.lengthComputable) {
-                var now = +(new Date()),
-                    total,
-                    loaded;
-                if (data._time && data.progressInterval &&
-                        (now - data._time < data.progressInterval) &&
-                        e.loaded !== e.total) {
-                    return;
-                }
-                data._time = now;
-                total = data.total || this._getTotal(data.files);
-                loaded = parseInt(
-                    e.loaded / e.total * (data.chunkSize || total),
-                    10
-                ) + (data.uploadedBytes || 0);
-                this._loaded += loaded - (data.loaded || data.uploadedBytes || 0);
-                data.lengthComputable = true;
-                data.loaded = loaded;
-                data.total = total;
-                data.bitrate = data._bitrateTimer.getBitrate(
-                    now,
-                    loaded,
-                    data.bitrateInterval
-                );
-                // Trigger a custom progress event with a total data property set
-                // to the file size(s) of the current upload and a loaded data
-                // property calculated accordingly:
-                this._trigger('progress', e, data);
-                // Trigger a global progress event for all current file uploads,
-                // including ajax calls queued for sequential file uploads:
-                this._trigger('progressall', e, {
-                    lengthComputable: true,
-                    loaded: this._loaded,
-                    total: this._total,
-                    bitrate: this._bitrateTimer.getBitrate(
-                        now,
-                        this._loaded,
-                        data.bitrateInterval
-                    )
-                });
-            }
-        },
-
-        _initProgressListener: function (options) {
-            var that = this,
-                xhr = options.xhr ? options.xhr() : $.ajaxSettings.xhr();
-            // Accesss to the native XHR object is required to add event listeners
-            // for the upload progress event:
-            if (xhr.upload) {
-                $(xhr.upload).bind('progress', function (e) {
-                    var oe = e.originalEvent;
-                    // Make sure the progress event properties get copied over:
-                    e.lengthComputable = oe.lengthComputable;
-                    e.loaded = oe.loaded;
-                    e.total = oe.total;
-                    that._onProgress(e, options);
-                });
-                options.xhr = function () {
-                    return xhr;
-                };
-            }
-        },
-
-        _initXHRData: function (options) {
-            var formData,
-                file = options.files[0],
-                // Ignore non-multipart setting if not supported:
-                multipart = options.multipart || !$.support.xhrFileUpload,
-                paramName = options.paramName[0];
-            if (!multipart || options.blob) {
-                // For non-multipart uploads and chunked uploads,
-                // file meta data is not part of the request body,
-                // so we transmit this data as part of the HTTP headers.
-                // For cross domain requests, these headers must be allowed
-                // via Access-Control-Allow-Headers or removed using
-                // the beforeSend callback:
-                options.headers = $.extend(options.headers, {
-                    'X-File-Name': file.name,
-                    'X-File-Type': file.type,
-                    'X-File-Size': file.size
-                });
-                if (!options.blob) {
-                    // Non-chunked non-multipart upload:
-                    options.contentType = file.type;
-                    options.data = file;
-                } else if (!multipart) {
-                    // Chunked non-multipart upload:
-                    options.contentType = 'application/octet-stream';
-                    options.data = options.blob;
-                }
-            }
-            if (multipart && $.support.xhrFormDataFileUpload) {
-                if (options.postMessage) {
-                    // window.postMessage does not allow sending FormData
-                    // objects, so we just add the File/Blob objects to
-                    // the formData array and let the postMessage window
-                    // create the FormData object out of this array:
-                    formData = this._getFormData(options);
-                    if (options.blob) {
-                        formData.push({
-                            name: paramName,
-                            value: options.blob
-                        });
-                    } else {
-                        $.each(options.files, function (index, file) {
-                            formData.push({
-                                name: options.paramName[index] || paramName,
-                                value: file
-                            });
-                        });
-                    }
-                } else {
-                    if (options.formData instanceof FormData) {
-                        formData = options.formData;
-                    } else {
-                        formData = new FormData();
-                        $.each(this._getFormData(options), function (index, field) {
-                            formData.append(field.name, field.value);
-                        });
-                    }
-                    if (options.blob) {
-                        formData.append(paramName, options.blob, file.name);
-                    } else {
-                        $.each(options.files, function (index, file) {
-                            // File objects are also Blob instances.
-                            // This check allows the tests to run with
-                            // dummy objects:
-                            if (file instanceof Blob) {
-                                formData.append(
-                                    options.paramName[index] || paramName,
-                                    file,
-                                    file.name
-                                );
-                            }
-                        });
-                    }
-                }
-                options.data = formData;
-            }
-            // Blob reference is not needed anymore, free memory:
-            options.blob = null;
-        },
-
-        _initIframeSettings: function (options) {
-            // Setting the dataType to iframe enables the iframe transport:
-            options.dataType = 'iframe ' + (options.dataType || '');
-            // The iframe transport accepts a serialized array as form data:
-            options.formData = this._getFormData(options);
-            // Add redirect url to form data on cross-domain uploads:
-            if (options.redirect && $('<a></a>').prop('href', options.url)
-                    .prop('host') !== location.host) {
-                options.formData.push({
-                    name: options.redirectParamName || 'redirect',
-                    value: options.redirect
-                });
-            }
-        },
-
-        _initDataSettings: function (options) {
-            if (this._isXHRUpload(options)) {
-                if (!this._chunkedUpload(options, true)) {
-                    if (!options.data) {
-                        this._initXHRData(options);
-                    }
-                    this._initProgressListener(options);
-                }
-                if (options.postMessage) {
-                    // Setting the dataType to postmessage enables the
-                    // postMessage transport:
-                    options.dataType = 'postmessage ' + (options.dataType || '');
-                }
-            } else {
-                this._initIframeSettings(options, 'iframe');
-            }
-        },
-
-        _getParamName: function (options) {
-            var fileInput = $(options.fileInput),
-                paramName = options.paramName;
-            if (!paramName) {
-                paramName = [];
-                fileInput.each(function () {
-                    var input = $(this),
-                        name = input.prop('name') || 'files[]',
-                        i = (input.prop('files') || [1]).length;
-                    while (i) {
-                        paramName.push(name);
-                        i -= 1;
-                    }
-                });
-                if (!paramName.length) {
-                    paramName = [fileInput.prop('name') || 'files[]'];
-                }
-            } else if (!$.isArray(paramName)) {
-                paramName = [paramName];
-            }
-            return paramName;
-        },
-
-        _initFormSettings: function (options) {
-            // Retrieve missing options from the input field and the
-            // associated form, if available:
-            if (!options.form || !options.form.length) {
-                options.form = $(options.fileInput.prop('form'));
-            }
-            options.paramName = this._getParamName(options);
-            if (!options.url) {
-                options.url = options.form.prop('action') || location.href;
-            }
-            // The HTTP request method must be "POST" or "PUT":
-            options.type = (options.type || options.form.prop('method') || '')
-                .toUpperCase();
-            if (options.type !== 'POST' && options.type !== 'PUT') {
-                options.type = 'POST';
-            }
-        },
-
-        _getAJAXSettings: function (data) {
-            var options = $.extend({}, this.options, data);
-            this._initFormSettings(options);
-            this._initDataSettings(options);
-            return options;
-        },
-
-        // Maps jqXHR callbacks to the equivalent
-        // methods of the given Promise object:
-        _enhancePromise: function (promise) {
-            promise.success = promise.done;
-            promise.error = promise.fail;
-            promise.complete = promise.always;
-            return promise;
-        },
-
-        // Creates and returns a Promise object enhanced with
-        // the jqXHR methods abort, success, error and complete:
-        _getXHRPromise: function (resolveOrReject, context, args) {
-            var dfd = $.Deferred(),
-                promise = dfd.promise();
-            context = context || this.options.context || promise;
-            if (resolveOrReject === true) {
-                dfd.resolveWith(context, args);
-            } else if (resolveOrReject === false) {
-                dfd.rejectWith(context, args);
-            }
-            promise.abort = dfd.promise;
-            return this._enhancePromise(promise);
-        },
-
-        // Uploads a file in multiple, sequential requests
-        // by splitting the file up in multiple blob chunks.
-        // If the second parameter is true, only tests if the file
-        // should be uploaded in chunks, but does not invoke any
-        // upload requests:
-        _chunkedUpload: function (options, testOnly) {
-            var that = this,
-                file = options.files[0],
-                fs = file.size,
-                ub = options.uploadedBytes = options.uploadedBytes || 0,
-                mcs = options.maxChunkSize || fs,
-                // Use the Blob methods with the slice implementation
-                // according to the W3C Blob API specification:
-                slice = file.webkitSlice || file.mozSlice || file.slice,
-                upload,
-                n,
-                jqXHR,
-                pipe;
-            if (!(this._isXHRUpload(options) && slice && (ub || mcs < fs)) ||
-                    options.data) {
-                return false;
-            }
-            if (testOnly) {
-                return true;
-            }
-            if (ub >= fs) {
-                file.error = 'uploadedBytes';
-                return this._getXHRPromise(
-                    false,
-                    options.context,
-                    [null, 'error', file.error]
-                );
-            }
-            // n is the number of blobs to upload,
-            // calculated via filesize, uploaded bytes and max chunk size:
-            n = Math.ceil((fs - ub) / mcs);
-            // The chunk upload method accepting the chunk number as parameter:
-            upload = function (i) {
-                if (!i) {
-                    return that._getXHRPromise(true, options.context);
-                }
-                // Upload the blobs in sequential order:
-                return upload(i -= 1).pipe(function () {
-                    // Clone the options object for each chunk upload:
-                    var o = $.extend({}, options);
-                    o.blob = slice.call(
-                        file,
-                        ub + i * mcs,
-                        ub + (i + 1) * mcs
-                    );
-                    // Store the current chunk size, as the blob itself
-                    // will be dereferenced after data processing:
-                    o.chunkSize = o.blob.size;
-                    // Process the upload data (the blob and potential form data):
-                    that._initXHRData(o);
-                    // Add progress listeners for this chunk upload:
-                    that._initProgressListener(o);
-                    jqXHR = ($.ajax(o) || that._getXHRPromise(false, o.context))
-                        .done(function () {
-                            // Create a progress event if upload is done and
-                            // no progress event has been invoked for this chunk:
-                            if (!o.loaded) {
-                                that._onProgress($.Event('progress', {
-                                    lengthComputable: true,
-                                    loaded: o.chunkSize,
-                                    total: o.chunkSize
-                                }), o);
-                            }
-                            options.uploadedBytes = o.uploadedBytes +=
-                                o.chunkSize;
-                        });
-                    return jqXHR;
-                });
-            };
-            // Return the piped Promise object, enhanced with an abort method,
-            // which is delegated to the jqXHR object of the current upload,
-            // and jqXHR callbacks mapped to the equivalent Promise methods:
-            pipe = upload(n);
-            pipe.abort = function () {
-                return jqXHR.abort();
-            };
-            return this._enhancePromise(pipe);
-        },
-
-        _beforeSend: function (e, data) {
-            if (this._active === 0) {
-                // the start callback is triggered when an upload starts
-                // and no other uploads are currently running,
-                // equivalent to the global ajaxStart event:
-                this._trigger('start');
-                // Set timer for global bitrate progress calculation:
-                this._bitrateTimer = new this._BitrateTimer();
-            }
-            this._active += 1;
-            // Initialize the global progress values:
-            this._loaded += data.uploadedBytes || 0;
-            this._total += this._getTotal(data.files);
-        },
-
-        _onDone: function (result, textStatus, jqXHR, options) {
-            if (!this._isXHRUpload(options)) {
-                // Create a progress event for each iframe load:
-                this._onProgress($.Event('progress', {
-                    lengthComputable: true,
-                    loaded: 1,
-                    total: 1
-                }), options);
-            }
-            options.result = result;
-            options.textStatus = textStatus;
-            options.jqXHR = jqXHR;
-            this._trigger('done', null, options);
-        },
-
-        _onFail: function (jqXHR, textStatus, errorThrown, options) {
-            options.jqXHR = jqXHR;
-            options.textStatus = textStatus;
-            options.errorThrown = errorThrown;
-            this._trigger('fail', null, options);
-            if (options.recalculateProgress) {
-                // Remove the failed (error or abort) file upload from
-                // the global progress calculation:
-                this._loaded -= options.loaded || options.uploadedBytes || 0;
-                this._total -= options.total || this._getTotal(options.files);
-            }
-        },
-
-        _onAlways: function (jqXHRorResult, textStatus, jqXHRorError, options) {
-            this._active -= 1;
-            options.textStatus = textStatus;
-            if (jqXHRorError && jqXHRorError.always) {
-                options.jqXHR = jqXHRorError;
-                options.result = jqXHRorResult;
-            } else {
-                options.jqXHR = jqXHRorResult;
-                options.errorThrown = jqXHRorError;
-            }
-            this._trigger('always', null, options);
-            if (this._active === 0) {
-                // The stop callback is triggered when all uploads have
-                // been completed, equivalent to the global ajaxStop event:
-                this._trigger('stop');
-                // Reset the global progress values:
-                this._loaded = this._total = 0;
-                this._bitrateTimer = null;
-            }
-        },
-
-        _onSend: function (e, data) {
-            var that = this,
-                jqXHR,
-                slot,
-                pipe,
-                options = that._getAJAXSettings(data),
-                send = function (resolve, args) {
-                    that._sending += 1;
-                    // Set timer for bitrate progress calculation:
-                    options._bitrateTimer = new that._BitrateTimer();
-                    jqXHR = jqXHR || (
-                        (resolve !== false &&
-                        that._trigger('send', e, options) !== false &&
-                        (that._chunkedUpload(options) || $.ajax(options))) ||
-                        that._getXHRPromise(false, options.context, args)
-                    ).done(function (result, textStatus, jqXHR) {
-                        that._onDone(result, textStatus, jqXHR, options);
-                    }).fail(function (jqXHR, textStatus, errorThrown) {
-                        that._onFail(jqXHR, textStatus, errorThrown, options);
-                    }).always(function (jqXHRorResult, textStatus, jqXHRorError) {
-                        that._sending -= 1;
-                        that._onAlways(
-                            jqXHRorResult,
-                            textStatus,
-                            jqXHRorError,
-                            options
-                        );
-                        if (options.limitConcurrentUploads &&
-                                options.limitConcurrentUploads > that._sending) {
-                            // Start the next queued upload,
-                            // that has not been aborted:
-                            var nextSlot = that._slots.shift();
-                            while (nextSlot) {
-                                if (!nextSlot.isRejected()) {
-                                    nextSlot.resolve();
-                                    break;
-                                }
-                                nextSlot = that._slots.shift();
-                            }
-                        }
-                    });
-                    return jqXHR;
-                };
-            this._beforeSend(e, options);
-            if (this.options.sequentialUploads ||
-                    (this.options.limitConcurrentUploads &&
-                    this.options.limitConcurrentUploads <= this._sending)) {
-                if (this.options.limitConcurrentUploads > 1) {
-                    slot = $.Deferred();
-                    this._slots.push(slot);
-                    pipe = slot.pipe(send);
-                } else {
-                    pipe = (this._sequence = this._sequence.pipe(send, send));
-                }
-                // Return the piped Promise object, enhanced with an abort method,
-                // which is delegated to the jqXHR object of the current upload,
-                // and jqXHR callbacks mapped to the equivalent Promise methods:
-                pipe.abort = function () {
-                    var args = [undefined, 'abort', 'abort'];
-                    if (!jqXHR) {
-                        if (slot) {
-                            slot.rejectWith(args);
-                        }
-                        return send(false, args);
-                    }
-                    return jqXHR.abort();
-                };
-                return this._enhancePromise(pipe);
-            }
-            return send();
-        },
-
-        _onAdd: function (e, data) {
-            var that = this,
-                result = true,
-                options = $.extend({}, this.options, data),
-                limit = options.limitMultiFileUploads,
-                paramName = this._getParamName(options),
-                paramNameSet,
-                paramNameSlice,
-                fileSet,
-                i;
-            if (!(options.singleFileUploads || limit) ||
-                    !this._isXHRUpload(options)) {
-                fileSet = [data.files];
-                paramNameSet = [paramName];
-            } else if (!options.singleFileUploads && limit) {
-                fileSet = [];
-                paramNameSet = [];
-                for (i = 0; i < data.files.length; i += limit) {
-                    fileSet.push(data.files.slice(i, i + limit));
-                    paramNameSlice = paramName.slice(i, i + limit);
-                    if (!paramNameSlice.length) {
-                        paramNameSlice = paramName;
-                    }
-                    paramNameSet.push(paramNameSlice);
-                }
-            } else {
-                paramNameSet = paramName;
-            }
-            data.originalFiles = data.files;
-            $.each(fileSet || data.files, function (index, element) {
-                var newData = $.extend({}, data);
-                newData.files = fileSet ? element : [element];
-                newData.paramName = paramNameSet[index];
-                newData.submit = function () {
-                    newData.jqXHR = this.jqXHR =
-                        (that._trigger('submit', e, this) !== false) &&
-                        that._onSend(e, this);
-                    return this.jqXHR;
-                };
-                return (result = that._trigger('add', e, newData));
-            });
-            return result;
-        },
-
-        // File Normalization for Gecko 1.9.1 (Firefox 3.5) support:
-        _normalizeFile: function (index, file) {
-            if (file.name === undefined && file.size === undefined) {
-                file.name = file.fileName;
-                file.size = file.fileSize;
-            }
-        },
-
-        _replaceFileInput: function (input) {
-            var inputClone = input.clone(true);
-            $('<form></form>').append(inputClone)[0].reset();
-            // Detaching allows to insert the fileInput on another form
-            // without loosing the file input value:
-            input.after(inputClone).detach();
-            // Avoid memory leaks with the detached file input:
-            $.cleanData(input.unbind('remove'));
-            // Replace the original file input element in the fileInput
-            // collection with the clone, which has been copied including
-            // event handlers:
-            this.options.fileInput = this.options.fileInput.map(function (i, el) {
-                if (el === input[0]) {
-                    return inputClone[0];
-                }
-                return el;
-            });
-            // If the widget has been initialized on the file input itself,
-            // override this.element with the file input clone:
-            if (input[0] === this.element[0]) {
-                this.element = inputClone;
-            }
-        },
-
-        _onChange: function (e) {
-            var that = e.data.fileupload,
-                data = {
-                    files: $.each($.makeArray(e.target.files), that._normalizeFile),
-                    fileInput: $(e.target),
-                    form: $(e.target.form)
-                };
-            if (!data.files.length) {
-                // If the files property is not available, the browser does not
-                // support the File API and we add a pseudo File object with
-                // the input value as name with path information removed:
-                data.files = [{name: e.target.value.replace(/^.*\\/, '')}];
-            }
-            if (that.options.replaceFileInput) {
-                that._replaceFileInput(data.fileInput);
-            }
-            if (that._trigger('change', e, data) === false ||
-                    that._onAdd(e, data) === false) {
-                return false;
-            }
-        },
-
-        _onPaste: function (e) {
-            var that = e.data.fileupload,
-                cbd = e.originalEvent.clipboardData,
-                items = (cbd && cbd.items) || [],
-                data = {files: []};
-            $.each(items, function (index, item) {
-                var file = item.getAsFile && item.getAsFile();
-                if (file) {
-                    data.files.push(file);
-                }
-            });
-            if (that._trigger('paste', e, data) === false ||
-                    that._onAdd(e, data) === false) {
-                return false;
-            }
-        },
-
-        _onDrop: function (e) {
-            var that = e.data.fileupload,
-                dataTransfer = e.dataTransfer = e.originalEvent.dataTransfer,
-                data = {
-                    files: $.each(
-                        $.makeArray(dataTransfer && dataTransfer.files),
-                        that._normalizeFile
-                    )
-                };
-            if (that._trigger('drop', e, data) === false ||
-                    that._onAdd(e, data) === false) {
-                return false;
-            }
-            e.preventDefault();
-        },
-
-        _onDragOver: function (e) {
-            var that = e.data.fileupload,
-                dataTransfer = e.dataTransfer = e.originalEvent.dataTransfer;
-            if (that._trigger('dragover', e) === false) {
-                return false;
-            }
-            if (dataTransfer) {
-                dataTransfer.dropEffect = dataTransfer.effectAllowed = 'copy';
-            }
-            e.preventDefault();
-        },
-
-        _initEventHandlers: function () {
-            var ns = this.options.namespace;
-            if (this._isXHRUpload(this.options)) {
-                this.options.dropZone
-                    .bind('dragover.' + ns, {fileupload: this}, this._onDragOver)
-                    .bind('drop.' + ns, {fileupload: this}, this._onDrop)
-                    .bind('paste.' + ns, {fileupload: this}, this._onPaste);
-            }
-            this.options.fileInput
-                .bind('change.' + ns, {fileupload: this}, this._onChange);
-        },
-
-        _destroyEventHandlers: function () {
-            var ns = this.options.namespace;
-            this.options.dropZone
-                .unbind('dragover.' + ns, this._onDragOver)
-                .unbind('drop.' + ns, this._onDrop)
-                .unbind('paste.' + ns, this._onPaste);
-            this.options.fileInput
-                .unbind('change.' + ns, this._onChange);
-        },
-
-        _setOption: function (key, value) {
-            var refresh = $.inArray(key, this._refreshOptionsList) !== -1;
-            if (refresh) {
-                this._destroyEventHandlers();
-            }
-            $.Widget.prototype._setOption.call(this, key, value);
-            if (refresh) {
-                this._initSpecialOptions();
-                this._initEventHandlers();
-            }
-        },
-
-        _initSpecialOptions: function () {
-            var options = this.options;
-            if (options.fileInput === undefined) {
-                options.fileInput = this.element.is('input:file') ?
-                        this.element : this.element.find('input:file');
-            } else if (!(options.fileInput instanceof $)) {
-                options.fileInput = $(options.fileInput);
-            }
-            if (!(options.dropZone instanceof $)) {
-                options.dropZone = $(options.dropZone);
-            }
-        },
-
-        _create: function () {
-            var options = this.options;
-            // Initialize options set via HTML5 data-attributes:
-            $.extend(options, $(this.element[0].cloneNode(false)).data());
-            options.namespace = options.namespace || this.widgetName;
-            this._initSpecialOptions();
-            this._slots = [];
-            this._sequence = this._getXHRPromise(true);
-            this._sending = this._active = this._loaded = this._total = 0;
-            this._initEventHandlers();
-        },
-
-        destroy: function () {
-            this._destroyEventHandlers();
-            $.Widget.prototype.destroy.call(this);
-        },
-
-        enable: function () {
-            $.Widget.prototype.enable.call(this);
-            this._initEventHandlers();
-        },
-
-        disable: function () {
-            this._destroyEventHandlers();
-            $.Widget.prototype.disable.call(this);
-        },
-
-        // This method is exposed to the widget API and allows adding files
-        // using the fileupload API. The data parameter accepts an object which
-        // must have a files property and can contain additional options:
-        // .fileupload('add', {files: filesList});
-        add: function (data) {
-            if (!data || this.options.disabled) {
-                return;
-            }
-            data.files = $.each($.makeArray(data.files), this._normalizeFile);
-            this._onAdd(null, data);
-        },
-
-        // This method is exposed to the widget API and allows sending files
-        // using the fileupload API. The data parameter accepts an object which
-        // must have a files property and can contain additional options:
-        // .fileupload('send', {files: filesList});
-        // The method returns a Promise object for the file upload call.
-        send: function (data) {
-            if (data && !this.options.disabled) {
-                data.files = $.each($.makeArray(data.files), this._normalizeFile);
-                if (data.files.length) {
-                    return this._onSend(null, data);
-                }
-            }
-            return this._getXHRPromise(false, data && data.context);
-        }
-
-    });
-
-}));
diff --git a/apps/fileupload/static/fileupload/js/jquery.iframe-transport.js b/apps/fileupload/static/fileupload/js/jquery.iframe-transport.js
deleted file mode 100644 (file)
index 04a5662..0000000
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- * jQuery Iframe Transport Plugin 1.4
- * https://github.com/blueimp/jQuery-File-Upload
- *
- * Copyright 2011, Sebastian Tschan
- * https://blueimp.net
- *
- * Licensed under the MIT license:
- * http://www.opensource.org/licenses/MIT
- */
-
-/*jslint unparam: true, nomen: true */
-/*global define, window, document */
-
-(function (factory) {
-    'use strict';
-    if (typeof define === 'function' && define.amd) {
-        // Register as an anonymous AMD module:
-        define(['jquery'], factory);
-    } else {
-        // Browser globals:
-        factory(window.jQuery);
-    }
-}(function ($) {
-    'use strict';
-
-    // Helper variable to create unique names for the transport iframes:
-    var counter = 0;
-
-    // The iframe transport accepts three additional options:
-    // options.fileInput: a jQuery collection of file input fields
-    // options.paramName: the parameter name for the file form data,
-    //  overrides the name property of the file input field(s),
-    //  can be a string or an array of strings.
-    // options.formData: an array of objects with name and value properties,
-    //  equivalent to the return data of .serializeArray(), e.g.:
-    //  [{name: 'a', value: 1}, {name: 'b', value: 2}]
-    $.ajaxTransport('iframe', function (options) {
-        if (options.async && (options.type === 'POST' || options.type === 'GET')) {
-            var form,
-                iframe;
-            return {
-                send: function (_, completeCallback) {
-                    form = $('<form style="display:none;"></form>');
-                    // javascript:false as initial iframe src
-                    // prevents warning popups on HTTPS in IE6.
-                    // IE versions below IE8 cannot set the name property of
-                    // elements that have already been added to the DOM,
-                    // so we set the name along with the iframe HTML markup:
-                    iframe = $(
-                        '<iframe src="javascript:false;" name="iframe-transport-' +
-                            (counter += 1) + '"></iframe>'
-                    ).bind('load', function () {
-                        var fileInputClones,
-                            paramNames = $.isArray(options.paramName) ?
-                                    options.paramName : [options.paramName];
-                        iframe
-                            .unbind('load')
-                            .bind('load', function () {
-                                var response;
-                                // Wrap in a try/catch block to catch exceptions thrown
-                                // when trying to access cross-domain iframe contents:
-                                try {
-                                    response = iframe.contents();
-                                    // Google Chrome and Firefox do not throw an
-                                    // exception when calling iframe.contents() on
-                                    // cross-domain requests, so we unify the response:
-                                    if (!response.length || !response[0].firstChild) {
-                                        throw new Error();
-                                    }
-                                } catch (e) {
-                                    response = undefined;
-                                }
-                                // The complete callback returns the
-                                // iframe content document as response object:
-                                completeCallback(
-                                    200,
-                                    'success',
-                                    {'iframe': response}
-                                );
-                                // Fix for IE endless progress bar activity bug
-                                // (happens on form submits to iframe targets):
-                                $('<iframe src="javascript:false;"></iframe>')
-                                    .appendTo(form);
-                                form.remove();
-                            });
-                        form
-                            .prop('target', iframe.prop('name'))
-                            .prop('action', options.url)
-                            .prop('method', options.type);
-                        if (options.formData) {
-                            $.each(options.formData, function (index, field) {
-                                $('<input type="hidden"/>')
-                                    .prop('name', field.name)
-                                    .val(field.value)
-                                    .appendTo(form);
-                            });
-                        }
-                        if (options.fileInput && options.fileInput.length &&
-                                options.type === 'POST') {
-                            fileInputClones = options.fileInput.clone();
-                            // Insert a clone for each file input field:
-                            options.fileInput.after(function (index) {
-                                return fileInputClones[index];
-                            });
-                            if (options.paramName) {
-                                options.fileInput.each(function (index) {
-                                    $(this).prop(
-                                        'name',
-                                        paramNames[index] || options.paramName
-                                    );
-                                });
-                            }
-                            // Appending the file input fields to the hidden form
-                            // removes them from their original location:
-                            form
-                                .append(options.fileInput)
-                                .prop('enctype', 'multipart/form-data')
-                                // enctype must be set as encoding for IE:
-                                .prop('encoding', 'multipart/form-data');
-                        }
-                        form.submit();
-                        // Insert the file input fields at their original location
-                        // by replacing the clones with the originals:
-                        if (fileInputClones && fileInputClones.length) {
-                            options.fileInput.each(function (index, input) {
-                                var clone = $(fileInputClones[index]);
-                                $(input).prop('name', clone.prop('name'));
-                                clone.replaceWith(input);
-                            });
-                        }
-                    });
-                    form.append(iframe).appendTo(document.body);
-                },
-                abort: function () {
-                    if (iframe) {
-                        // javascript:false as iframe src aborts the request
-                        // and prevents warning popups on HTTPS in IE6.
-                        // concat is used to avoid the "Script URL" JSLint error:
-                        iframe
-                            .unbind('load')
-                            .prop('src', 'javascript'.concat(':false;'));
-                    }
-                    if (form) {
-                        form.remove();
-                    }
-                }
-            };
-        }
-    });
-
-    // The iframe transport returns the iframe content document as response.
-    // The following adds converters from iframe to text, json, html, and script:
-    $.ajaxSetup({
-        converters: {
-            'iframe text': function (iframe) {
-                return $(iframe[0].body).text();
-            },
-            'iframe json': function (iframe) {
-                return $.parseJSON($(iframe[0].body).text());
-            },
-            'iframe html': function (iframe) {
-                return $(iframe[0].body).html();
-            },
-            'iframe script': function (iframe) {
-                return $.globalEval($(iframe[0].body).text());
-            }
-        }
-    });
-
-}));
diff --git a/apps/fileupload/static/fileupload/js/jquery.ui.widget.js b/apps/fileupload/static/fileupload/js/jquery.ui.widget.js
deleted file mode 100644 (file)
index 9da8673..0000000
+++ /dev/null
@@ -1,282 +0,0 @@
-/*
- * jQuery UI Widget 1.8.18+amd
- * https://github.com/blueimp/jQuery-File-Upload
- *
- * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
- * Dual licensed under the MIT or GPL Version 2 licenses.
- * http://jquery.org/license
- *
- * http://docs.jquery.com/UI/Widget
- */
-
-(function (factory) {
-    if (typeof define === "function" && define.amd) {
-        // Register as an anonymous AMD module:
-        define(["jquery"], factory);
-    } else {
-        // Browser globals:
-        factory(jQuery);
-    }
-}(function( $, undefined ) {
-
-// jQuery 1.4+
-if ( $.cleanData ) {
-       var _cleanData = $.cleanData;
-       $.cleanData = function( elems ) {
-               for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
-                       try {
-                               $( elem ).triggerHandler( "remove" );
-                       // http://bugs.jquery.com/ticket/8235
-                       } catch( e ) {}
-               }
-               _cleanData( elems );
-       };
-} else {
-       var _remove = $.fn.remove;
-       $.fn.remove = function( selector, keepData ) {
-               return this.each(function() {
-                       if ( !keepData ) {
-                               if ( !selector || $.filter( selector, [ this ] ).length ) {
-                                       $( "*", this ).add( [ this ] ).each(function() {
-                                               try {
-                                                       $( this ).triggerHandler( "remove" );
-                                               // http://bugs.jquery.com/ticket/8235
-                                               } catch( e ) {}
-                                       });
-                               }
-                       }
-                       return _remove.call( $(this), selector, keepData );
-               });
-       };
-}
-
-$.widget = function( name, base, prototype ) {
-       var namespace = name.split( "." )[ 0 ],
-               fullName;
-       name = name.split( "." )[ 1 ];
-       fullName = namespace + "-" + name;
-
-       if ( !prototype ) {
-               prototype = base;
-               base = $.Widget;
-       }
-
-       // create selector for plugin
-       $.expr[ ":" ][ fullName ] = function( elem ) {
-               return !!$.data( elem, name );
-       };
-
-       $[ namespace ] = $[ namespace ] || {};
-       $[ namespace ][ name ] = function( options, element ) {
-               // allow instantiation without initializing for simple inheritance
-               if ( arguments.length ) {
-                       this._createWidget( options, element );
-               }
-       };
-
-       var basePrototype = new base();
-       // we need to make the options hash a property directly on the new instance
-       // otherwise we'll modify the options hash on the prototype that we're
-       // inheriting from
-//     $.each( basePrototype, function( key, val ) {
-//             if ( $.isPlainObject(val) ) {
-//                     basePrototype[ key ] = $.extend( {}, val );
-//             }
-//     });
-       basePrototype.options = $.extend( true, {}, basePrototype.options );
-       $[ namespace ][ name ].prototype = $.extend( true, basePrototype, {
-               namespace: namespace,
-               widgetName: name,
-               widgetEventPrefix: $[ namespace ][ name ].prototype.widgetEventPrefix || name,
-               widgetBaseClass: fullName
-       }, prototype );
-
-       $.widget.bridge( name, $[ namespace ][ name ] );
-};
-
-$.widget.bridge = function( name, object ) {
-       $.fn[ name ] = function( options ) {
-               var isMethodCall = typeof options === "string",
-                       args = Array.prototype.slice.call( arguments, 1 ),
-                       returnValue = this;
-
-               // allow multiple hashes to be passed on init
-               options = !isMethodCall && args.length ?
-                       $.extend.apply( null, [ true, options ].concat(args) ) :
-                       options;
-
-               // prevent calls to internal methods
-               if ( isMethodCall && options.charAt( 0 ) === "_" ) {
-                       return returnValue;
-               }
-
-               if ( isMethodCall ) {
-                       this.each(function() {
-                               var instance = $.data( this, name ),
-                                       methodValue = instance && $.isFunction( instance[options] ) ?
-                                               instance[ options ].apply( instance, args ) :
-                                               instance;
-                               // TODO: add this back in 1.9 and use $.error() (see #5972)
-//                             if ( !instance ) {
-//                                     throw "cannot call methods on " + name + " prior to initialization; " +
-//                                             "attempted to call method '" + options + "'";
-//                             }
-//                             if ( !$.isFunction( instance[options] ) ) {
-//                                     throw "no such method '" + options + "' for " + name + " widget instance";
-//                             }
-//                             var methodValue = instance[ options ].apply( instance, args );
-                               if ( methodValue !== instance && methodValue !== undefined ) {
-                                       returnValue = methodValue;
-                                       return false;
-                               }
-                       });
-               } else {
-                       this.each(function() {
-                               var instance = $.data( this, name );
-                               if ( instance ) {
-                                       instance.option( options || {} )._init();
-                               } else {
-                                       $.data( this, name, new object( options, this ) );
-                               }
-                       });
-               }
-
-               return returnValue;
-       };
-};
-
-$.Widget = function( options, element ) {
-       // allow instantiation without initializing for simple inheritance
-       if ( arguments.length ) {
-               this._createWidget( options, element );
-       }
-};
-
-$.Widget.prototype = {
-       widgetName: "widget",
-       widgetEventPrefix: "",
-       options: {
-               disabled: false
-       },
-       _createWidget: function( options, element ) {
-               // $.widget.bridge stores the plugin instance, but we do it anyway
-               // so that it's stored even before the _create function runs
-               $.data( element, this.widgetName, this );
-               this.element = $( element );
-               this.options = $.extend( true, {},
-                       this.options,
-                       this._getCreateOptions(),
-                       options );
-
-               var self = this;
-               this.element.bind( "remove." + this.widgetName, function() {
-                       self.destroy();
-               });
-
-               this._create();
-               this._trigger( "create" );
-               this._init();
-       },
-       _getCreateOptions: function() {
-               return $.metadata && $.metadata.get( this.element[0] )[ this.widgetName ];
-       },
-       _create: function() {},
-       _init: function() {},
-
-       destroy: function() {
-               this.element
-                       .unbind( "." + this.widgetName )
-                       .removeData( this.widgetName );
-               this.widget()
-                       .unbind( "." + this.widgetName )
-                       .removeAttr( "aria-disabled" )
-                       .removeClass(
-                               this.widgetBaseClass + "-disabled " +
-                               "ui-state-disabled" );
-       },
-
-       widget: function() {
-               return this.element;
-       },
-
-       option: function( key, value ) {
-               var options = key;
-
-               if ( arguments.length === 0 ) {
-                       // don't return a reference to the internal hash
-                       return $.extend( {}, this.options );
-               }
-
-               if  (typeof key === "string" ) {
-                       if ( value === undefined ) {
-                               return this.options[ key ];
-                       }
-                       options = {};
-                       options[ key ] = value;
-               }
-
-               this._setOptions( options );
-
-               return this;
-       },
-       _setOptions: function( options ) {
-               var self = this;
-               $.each( options, function( key, value ) {
-                       self._setOption( key, value );
-               });
-
-               return this;
-       },
-       _setOption: function( key, value ) {
-               this.options[ key ] = value;
-
-               if ( key === "disabled" ) {
-                       this.widget()
-                               [ value ? "addClass" : "removeClass"](
-                                       this.widgetBaseClass + "-disabled" + " " +
-                                       "ui-state-disabled" )
-                               .attr( "aria-disabled", value );
-               }
-
-               return this;
-       },
-
-       enable: function() {
-               return this._setOption( "disabled", false );
-       },
-       disable: function() {
-               return this._setOption( "disabled", true );
-       },
-
-       _trigger: function( type, event, data ) {
-               var prop, orig,
-                       callback = this.options[ type ];
-
-               data = data || {};
-               event = $.Event( event );
-               event.type = ( type === this.widgetEventPrefix ?
-                       type :
-                       this.widgetEventPrefix + type ).toLowerCase();
-               // the original event may come from any element
-               // so we need to reset the target on the new event
-               event.target = this.element[ 0 ];
-
-               // copy original event properties over to the new event
-               orig = event.originalEvent;
-               if ( orig ) {
-                       for ( prop in orig ) {
-                               if ( !( prop in event ) ) {
-                                       event[ prop ] = orig[ prop ];
-                               }
-                       }
-               }
-
-               this.element.trigger( event, data );
-
-               return !( $.isFunction(callback) &&
-                       callback.call( this.element[0], event, data ) === false ||
-                       event.isDefaultPrevented() );
-       }
-};
-
-}));
diff --git a/apps/fileupload/static/fileupload/js/load-image.min.js b/apps/fileupload/static/fileupload/js/load-image.min.js
deleted file mode 100644 (file)
index faac82f..0000000
+++ /dev/null
@@ -1 +0,0 @@
-(function(a){"use strict";var b=function(a,c,d){var e=document.createElement("img"),f,g;return e.onerror=c,e.onload=function(){g&&b.revokeObjectURL(g),c(b.scale(e,d))},window.Blob&&a instanceof Blob||window.File&&a instanceof File?f=g=b.createObjectURL(a):f=a,f?(e.src=f,e):b.readFile(a,function(a){e.src=a})},c=window.createObjectURL&&window||window.URL&&URL||window.webkitURL&&webkitURL;b.scale=function(a,b){b=b||{};var c=document.createElement("canvas"),d=a.width,e=a.height,f=Math.max((b.minWidth||d)/d,(b.minHeight||e)/e);return f>1&&(d=parseInt(d*f,10),e=parseInt(e*f,10)),f=Math.min((b.maxWidth||d)/d,(b.maxHeight||e)/e),f<1&&(d=parseInt(d*f,10),e=parseInt(e*f,10)),a.getContext||b.canvas&&c.getContext?(c.width=d,c.height=e,c.getContext("2d").drawImage(a,0,0,d,e),c):(a.width=d,a.height=e,a)},b.createObjectURL=function(a){return c?c.createObjectURL(a):!1},b.revokeObjectURL=function(a){return c?c.revokeObjectURL(a):!1},b.readFile=function(a,b){if(window.FileReader&&FileReader.prototype.readAsDataURL){var c=new FileReader;return c.onload=function(a){b(a.target.result)},c.readAsDataURL(a),c}return!1},typeof define!="undefined"&&define.amd?define(function(){return b}):a.loadImage=b})(this);
diff --git a/apps/fileupload/static/fileupload/js/locale-en.js b/apps/fileupload/static/fileupload/js/locale-en.js
deleted file mode 100644 (file)
index ea64b0a..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * jQuery File Upload Plugin Localization Example 6.5.1
- * https://github.com/blueimp/jQuery-File-Upload
- *
- * Copyright 2012, Sebastian Tschan
- * https://blueimp.net
- *
- * Licensed under the MIT license:
- * http://www.opensource.org/licenses/MIT
- */
-
-/*global window */
-
-window.locale = {
-    "fileupload": {
-        "errors": {
-            "maxFileSize": "File is too big",
-            "minFileSize": "File is too small",
-            "acceptFileTypes": "Filetype not allowed",
-            "maxNumberOfFiles": "Max number of files exceeded",
-            "uploadedBytes": "Uploaded bytes exceed file size",
-            "emptyResult": "Empty file upload result"
-        },
-        "error": "Error",
-        "start": "Start",
-        "cancel": "Cancel",
-        "destroy": "Delete"
-    }
-};
diff --git a/apps/fileupload/static/fileupload/js/locale-pl.js b/apps/fileupload/static/fileupload/js/locale-pl.js
deleted file mode 100755 (executable)
index 9d05227..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-window.locale = {
-    "fileupload": {
-        "errors": {
-            "maxFileSize": "Plik jest zbyt duży",
-            "minFileSize": "Plik jest zbyt mały",
-            "acceptFileTypes": "Niedozwolony typ pliku",
-            "maxNumberOfFiles": "Zbyt wiele plików",
-            "uploadedBytes": "Przesłane dane przekraczają rozmiar pliku",
-            "emptyResult": "Wysyłanie plików zwróciło pusty wynik"
-        },
-        "error": "Błąd",
-        "start": "Start",
-        "cancel": "Anuluj",
-        "destroy": "Usuń"
-    }
-};
diff --git a/apps/fileupload/static/fileupload/js/main.js b/apps/fileupload/static/fileupload/js/main.js
deleted file mode 100644 (file)
index 64c3064..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-$(function () {
-    // Initialize the jQuery File Upload widget:
-    $('#fileupload').fileupload();
-
-    // Load existing files:
-    $('#fileupload').each(function () {
-        var that = this;
-        $.getJSON(this.action, function (result) {
-            if (result && result.length) {
-                $(that).fileupload('option', 'done')
-                    .call(that, null, {result: result});
-            }
-        });
-    });
-});
diff --git a/apps/fileupload/static/fileupload/js/tmpl.min.js b/apps/fileupload/static/fileupload/js/tmpl.min.js
deleted file mode 100644 (file)
index c6e4922..0000000
+++ /dev/null
@@ -1 +0,0 @@
-(function(a){"use strict";var b=function(a,c){var d=/[^\w\-\.:]/.test(a)?new Function(b.arg+",tmpl","var _e=tmpl.encode"+b.helper+",_s='"+a.replace(b.regexp,b.func)+"';return _s;"):b.cache[a]=b.cache[a]||b(b.load(a));return c?d(c,b):function(a){return d(a,b)}};b.cache={},b.load=function(a){return document.getElementById(a).innerHTML},b.regexp=/([\s'\\])(?![^%]*%\})|(?:\{%(=|#)([\s\S]+?)%\})|(\{%)|(%\})/g,b.func=function(a,b,c,d,e,f){if(b)return{"\n":"\\n","\r":"\\r","\t":"\\t"," ":" "}[a]||"\\"+a;if(c)return c==="="?"'+_e("+d+")+'":"'+("+d+"||'')+'";if(e)return"';";if(f)return"_s+='"},b.encReg=/[<>&"'\x00]/g,b.encMap={"<":"&lt;",">":"&gt;","&":"&amp;",'"':"&quot;","'":"&#39;"},b.encode=function(a){return String(a||"").replace(b.encReg,function(a){return b.encMap[a]||""})},b.arg="o",b.helper=",print=function(s,e){_s+=e&&(s||'')||_e(s);},include=function(s,d){_s+=tmpl(s,d);}",typeof define=="function"&&define.amd?define(function(){return b}):a.tmpl=b})(this);
diff --git a/apps/fileupload/templates/fileupload/picture_form.html b/apps/fileupload/templates/fileupload/picture_form.html
deleted file mode 100644 (file)
index 44c37db..0000000
+++ /dev/null
@@ -1,86 +0,0 @@
-{% extends "catalogue/base.html" %}
-{% load i18n %}
-{% load upload_tags %}
-
-{% block add_css %}
-    <link rel="stylesheet" href="{{ STATIC_URL }}fileupload/css/bootstrap.min.css">
-    <link rel="stylesheet" href="{{ STATIC_URL }}fileupload/css/style.css">
-    <link rel="stylesheet" href="{{ STATIC_URL }}fileupload/css/bootstrap-image-gallery.min.css">
-    <link rel="stylesheet" href="{{ STATIC_URL }}fileupload/css/jquery.fileupload-ui.css">
-    <!-- Shim to make HTML5 elements usable in older Internet Explorer versions -->
-    <!--[if lt IE 9]><script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script><![endif]-->
-{% endblock %}
-
-
-{% block content %}
-
-<h1>
-{% trans "Browse:" %}
-{% for crumb in view.breadcrumbs %}
-    {% if crumb.1 %}
-        <a href="{{ crumb.1 }}">{{ crumb.0 }}</a>
-    {% else %}
-        {{ crumb.0 }}
-    {% endif %}
-    {% if not forloop.last %}/{% endif %}
-{% endfor %}
-</h1>
-
-
-    <form id="fileupload" method="post" action="." enctype="multipart/form-data">{% csrf_token %}
-        <div class="row fileupload-buttonbar">
-            <div class="span7">
-                <span class="btn btn-success fileinput-button">
-                    <i class="icon-plus icon-white"></i>
-                    <span>{% trans "Add files..." %}</span>
-                    <input type="file" multiple="" name="files">
-                </span>
-                <button class="btn btn-primary start" type="submit">
-                    <i class="icon-upload icon-white"></i>
-                    <span>{% trans "Start upload" %}</span>
-                </button>
-                <button class="btn btn-warning cancel" type="reset">
-                    <i class="icon-ban-circle icon-white"></i>
-                    <span>{% trans "Cancel upload" %}</span>
-                </button>
-                <button class="btn btn-danger delete" type="button">
-                    <i class="icon-trash icon-white"></i>
-                    <span>{% trans "Delete" %}</span>
-                </button>
-                <input type="checkbox" class="toggle">
-            </div>
-            <div class="span5 fileupload-progress fade">
-                <div class="progress progress-success progres-striped active">
-                    <div class="bar" style="width:0%"></div>
-                </div>
-                <div class="progress-extended">&nbsp;</div>
-            </div>
-        </div>
-        <div class="fileupload-loading"></div>
-        <table class="table table-striped"><tbody class="files" data-toggle="modal-gallery" data-target="#modal-gallery"></tbody></table>
-    </form>
-    <div class="fileupload-content">
-        <table class="files"></table>
-        <div class="fileupload-progressbar"></div>
-    </div>
-
-{% endblock %}
-
-{% block add_js %}
-
-{% upload_js %}
-<script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
-<script src="{{ STATIC_URL }}fileupload/js/jquery.ui.widget.js"></script>
-<script src="{{ STATIC_URL }}fileupload/js/tmpl.min.js"></script>
-<script src="{{ STATIC_URL }}fileupload/js/load-image.min.js"></script>
-<script src="{{ STATIC_URL }}fileupload/js/canvas-to-blob.min.js"></script>
-<script src="{{ STATIC_URL }}fileupload/js/bootstrap.min.js"></script>
-<script src="{{ STATIC_URL }}fileupload/js/bootstrap-image-gallery.min.js"></script>
-<script src="{{ STATIC_URL }}fileupload/js/jquery.iframe-transport.js"></script>
-<script src="{{ STATIC_URL }}fileupload/js/jquery.fileupload.js"></script>
-<script src="{{ STATIC_URL }}fileupload/js/jquery.fileupload-fp.js"></script>
-<script src="{{ STATIC_URL }}fileupload/js/jquery.fileupload-ui.js"></script>
-<script src="{{ STATIC_URL }}fileupload/js/locale-{{ LANGUAGE_CODE }}.js"></script>
-<script src="{{ STATIC_URL }}fileupload/js/main.js"></script>
-<script src="{{ STATIC_URL }}fileupload/js/csrf.js"></script>
-{% endblock %}
diff --git a/apps/fileupload/templatetags/__init__.py b/apps/fileupload/templatetags/__init__.py
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/apps/fileupload/templatetags/upload_tags.py b/apps/fileupload/templatetags/upload_tags.py
deleted file mode 100644 (file)
index aefce2e..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-from django import template
-
-register = template.Library()
-
-@register.simple_tag
-def upload_js():
-    return """
-<!-- The template to display files available for upload -->
-<script id="template-upload" type="text/x-tmpl">
-{% for (var i=0, file; file=o.files[i]; i++) { %}
-    <tr class="template-upload fade">
-        <td class="preview"><span class="fade"></span></td>
-        <td class="name"><span>{%=file.name%}</span></td>
-        <td class="size"><span>{%=o.formatFileSize(file.size)%}</span></td>
-        {% if (file.error) { %}
-            <td class="error" colspan="2"><span class="label label-important">{%=locale.fileupload.error%}</span> {%=locale.fileupload.errors[file.error] || file.error%}</td>
-        {% } else if (o.files.valid && !i) { %}
-            <td>
-                <div class="progress progress-success progress-striped active"><div class="bar" style="width:0%;"></div></div>
-            </td>
-            <td class="start">{% if (!o.options.autoUpload) { %}
-                <button class="btn btn-success">
-                    <i class="icon-upload icon-white"></i>
-                    <span>{%=locale.fileupload.start%}</span>
-                </button>
-            {% } %}</td>
-        {% } else { %}
-            <td colspan="2"></td>
-        {% } %}
-        <td class="cancel">{% if (!i) { %}
-            <button class="btn btn-warning">
-                <i class="icon-ban-circle icon-white"></i>
-                <span>{%=locale.fileupload.cancel%}</span>
-            </button>
-        {% } %}</td>
-    </tr>
-{% } %}
-</script>
-<!-- The template to display files available for download -->
-<script id="template-download" type="text/x-tmpl">
-{% for (var i=0, file; file=o.files[i]; i++) { %}
-    <tr class="template-download fade">
-        {% if (file.error) { %}
-            <td></td>
-            <td class="name"><span>{%=file.name%}</span></td>
-            <td class="size"><span>{%=o.formatFileSize(file.size)%}</span></td>
-            <td class="error" colspan="2"><span class="label label-important">{%=locale.fileupload.error%}</span> {%=locale.fileupload.errors[file.error] || file.error%}</td>
-        {% } else { %}
-            <td class="preview">{% if (file.thumbnail_url) { %}
-                <a href="{%=file.url%}" title="{%=file.name%}" rel="gallery" download="{%=file.name%}"><img src="{%=file.thumbnail_url%}"></a>
-            {% } %}</td>
-            <td class="name">
-                <a href="{%=file.url%}" title="{%=file.name%}" rel="{%=file.thumbnail_url&&'gallery'%}" download="{%=file.name%}">{%=file.name%}</a>
-            </td>
-            <td class="size"><span>{%=o.formatFileSize(file.size)%}</span></td>
-            <td colspan="2"></td>
-        {% } %}
-        <td class="delete">
-            <button class="btn btn-danger" data-type="{%=file.delete_type%}" data-url="{%=file.delete_url%}">
-                <i class="icon-trash icon-white"></i>
-                <span>{%=locale.fileupload.destroy%}</span>
-            </button>
-            <input type="checkbox" name="delete" value="1">
-        </td>
-    </tr>
-{% } %}
-</script>
-"""
diff --git a/apps/fileupload/urls.py b/apps/fileupload/urls.py
deleted file mode 100644 (file)
index cd4f46c..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-from django.conf.urls import patterns, url
-from fileupload.views import UploadView
-
-urlpatterns = patterns('',
-    url(r'^(?P<path>(?:.*/)?)$', UploadView.as_view(), name='fileupload'),
-)
-
diff --git a/apps/fileupload/views.py b/apps/fileupload/views.py
deleted file mode 100644 (file)
index a2025fe..0000000
+++ /dev/null
@@ -1,154 +0,0 @@
-import json
-import os
-from urllib import quote
-from django.conf import settings
-from django.http import HttpResponse, HttpResponseRedirect, HttpResponseForbidden, Http404
-from django.utils.decorators import method_decorator
-from django.utils.encoding import smart_bytes
-from django.views.decorators.vary import vary_on_headers
-from django.views.generic import FormView, View
-from .forms import UploadForm
-
-
-# Use sorl.thumbnail if available.
-try:
-    from sorl.thumbnail import default
-except ImportError:
-    def thumbnail(relpath):
-        return settings.MEDIA_URL + relpath
-else:
-    def thumbnail(relpath):
-        try:
-            return default.backend.get_thumbnail(relpath, "x50").url
-        except (IOError, ValueError):
-            # That's not an image. No thumb.
-            return None
-
-
-class JSONResponse(HttpResponse):
-    """JSON response class."""
-    def __init__(self, obj='', mimetype="application/json", *args, **kwargs):
-        content = json.dumps(obj)
-        super(JSONResponse, self).__init__(content, mimetype, *args, **kwargs)
-
-
-class UploadView(FormView):
-    template_name = "fileupload/picture_form.html"
-    form_class = UploadForm
-
-    def get_object(self, request, *args, **kwargs):
-        """Get any data for later use."""
-        return None
-
-    def get_directory(self):
-        """Directory relative to MEDIA_ROOT. Must end with a slash."""
-        return self.kwargs['path']
-
-    def breadcrumbs(self):
-        """List of tuples (name, url) or just (name,) for breadcrumbs.
-
-        Probably only the last item (representing currently browsed dir)
-        should lack url.
-
-        """
-        directory = self.get_directory()
-        now_path = os.path.dirname(self.request.get_full_path())
-        directory = os.path.dirname(directory)
-        if directory:
-            crumbs = [
-                (os.path.basename(directory),)
-            ]
-            directory = os.path.dirname(directory)
-            now_path = (os.path.dirname(now_path))
-            while directory:
-                crumbs.insert(0, (os.path.basename(directory), now_path+'/'))
-                directory = os.path.dirname(directory)
-                now_path = os.path.dirname(now_path)
-            crumbs.insert(0, ('media', now_path))
-        else:
-            crumbs = [('media',)]
-        return crumbs
-
-    def get_safe_path(self, filename=""):
-        """Finds absolute filesystem path of the browsed dir of file.
-        
-        Makes sure it's inside MEDIA_ROOT.
-        
-        """
-        filename = smart_bytes(filename)
-        path = os.path.abspath(os.path.join(
-                settings.MEDIA_ROOT,
-                smart_bytes(self.get_directory()),
-                filename))
-        if not path.startswith(settings.MEDIA_ROOT):
-            raise Http404
-        if filename:
-            if not path.startswith(self.get_safe_path()):
-                raise Http404
-        return path
-
-    def get_url(self, filename):
-        """Finds URL of a file in browsed dir."""
-        return settings.MEDIA_URL + self.get_directory() + quote(filename.encode('utf-8'))
-
-    @method_decorator(vary_on_headers('Accept'))
-    def dispatch(self, request, *args, **kwargs):
-        self.object = self.get_object(request, *args, **kwargs)
-        return super(UploadView, self).dispatch(request, *args, **kwargs)
-
-    def get(self, request, *args, **kwargs):
-        if request.is_ajax():
-            files = []
-            path = self.get_safe_path()
-            if os.path.isdir(path):
-                for f in sorted(os.listdir(path)):
-                    file_info = {
-                        "name": f,
-                    }
-                    if os.path.isdir(os.path.join(path, f)):
-                        file_info.update({
-                            "url": "%s%s/" % (request.get_full_path(), f),
-                        })
-                    else:
-                        file_info.update({
-                            "url": self.get_url(f),
-                            'thumbnail_url': thumbnail(self.get_directory() + f),
-                            'delete_url': "%s?file=%s" % (
-                                request.get_full_path(),
-                                quote(f.encode('utf-8'))),
-                            'delete_type': "DELETE"
-                        })
-                        thumbnail_url = thumbnail(self.get_directory() + f),
-                    files.append(file_info)
-            return JSONResponse(files)
-        else:
-            return super(UploadView, self).get(request, *args, **kwargs)
-
-    def form_valid(self, form):
-        flist = self.request.FILES.getlist('files')
-        path = self.get_safe_path()
-        if not os.path.isdir(path):
-            os.makedirs(path)
-        data = []
-        for f in flist:
-            with open(self.get_safe_path(f.name), 'w') as destination:
-                for chunk in f.chunks():
-                    destination.write(chunk)
-            data.append({
-                'name': f.name, 
-                'url': self.get_url(f.name),
-                'thumbnail_url': thumbnail(self.get_directory() + f.name),
-                        'delete_url': "%s?file=%s" % (
-                            self.request.get_full_path(),
-                            quote(f.name.encode('utf-8'))),
-                'delete_type': "DELETE"
-            })
-        response = JSONResponse(data)
-        response['Content-Disposition'] = 'inline; filename=files.json'
-        return response
-
-    def delete(self, request, *args, **kwargs):
-        os.unlink(self.get_safe_path(request.GET.get('file')))
-        response = JSONResponse(True)
-        response['Content-Disposition'] = 'inline; filename=files.json'
-        return response
diff --git a/apps/toolbar/__init__.py b/apps/toolbar/__init__.py
deleted file mode 100644 (file)
index c53f0e7..0000000
+++ /dev/null
@@ -1 +0,0 @@
-  # pragma: no cover
diff --git a/apps/toolbar/admin.py b/apps/toolbar/admin.py
deleted file mode 100644 (file)
index 654480c..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-from django.contrib import admin
-from django.utils.translation import ugettext_lazy as _
-from django import forms
-import json
-
-from toolbar import models
-
-
-class ButtonAdminForm(forms.ModelForm):
-    class Meta:
-        model = models.Button
-        exclude = []
-
-    def clean_params(self):
-        value = self.cleaned_data['params']
-        try:
-            return json.dumps(json.loads(value))
-        except ValueError, e:
-            raise forms.ValidationError(e)
-
-
-class ButtonAdmin(admin.ModelAdmin):
-    form = ButtonAdminForm
-    list_display = ('slug', 'label', 'tooltip', 'accesskey')
-    list_display_links = ('slug',)
-    list_editable = ('label', 'tooltip', 'accesskey')
-    prepopulated_fields = {'slug': ('label',)}
-
-admin.site.register(models.Button, ButtonAdmin)
-admin.site.register(models.ButtonGroup)
-admin.site.register(models.Scriptlet)
diff --git a/apps/toolbar/fixtures/initial_toolbar.yaml b/apps/toolbar/fixtures/initial_toolbar.yaml
deleted file mode 100644 (file)
index be8c339..0000000
+++ /dev/null
@@ -1,1067 +0,0 @@
--   fields: {name: "Nag\u0142\xF3wki", position: 0, slug: naglowki}
-    model: toolbar.buttongroup
-    pk: 1
--   fields: {name: Autokorekta, position: 0, slug: autokorekta}
-    model: toolbar.buttongroup
-    pk: 2
--   fields: {name: Mastery, position: 0, slug: mastery}
-    model: toolbar.buttongroup
-    pk: 11
--   fields: {name: 'Dramat ', position: 0, slug: dramat}
-    model: toolbar.buttongroup
-    pk: 12
--   fields: {name: "Elementy pocz\u0105tkowe", position: 0, slug: elementy-poczatkowe}
-    model: toolbar.buttongroup
-    pk: 13
--   fields: {name: Akapity, position: 0, slug: akapity}
-    model: toolbar.buttongroup
-    pk: 14
--   fields: {name: Style znakowe, position: 0, slug: style-znakowe}
-    model: toolbar.buttongroup
-    pk: 15
--   fields: {name: Separatory, position: 0, slug: separatory}
-    model: toolbar.buttongroup
-    pk: 16
--   fields: {name: Wersy, position: 0, slug: wersy}
-    model: toolbar.buttongroup
-    pk: 17
--   fields: {name: Bloki, position: 0, slug: bloki}
-    model: toolbar.buttongroup
-    pk: 21
--   fields: {name: "Pocz\u0105tek dramatu", position: 0, slug: poczatek-dramatu}
-    model: toolbar.buttongroup
-    pk: 22
--   fields: {name: Przypisy, position: 0, slug: przypisy}
-    model: toolbar.buttongroup
-    pk: 26
--   fields: {name: Autotagowanie, position: 0, slug: autotagowanie}
-    model: toolbar.buttongroup
-    pk: 28
--   fields: {name: Uwaga, position: 0, slug: uwaga}
-    model: toolbar.buttongroup
-    pk: 29
--   fields:
-        accesskey: ''
-        group: [2]
-        label: ",,\u2026\" na \xAB\u2026\xBB"
-        link: ''
-        params: '{"exprs": [[",,", "\u00ab"], ["\"", "\u00bb"]]}'
-        scriptlet: fulltextregexp
-        slug: na-francuskie
-        tooltip: "Zamienia cudzys\u0142owy podw\xF3jne na francuskie"
-    model: toolbar.button
-    pk: 2
--   fields:
-        accesskey: ''
-        group: [2]
-        label: ",,\u2026\" na \xBB\u2026\xAB"
-        link: ''
-        params: '{"exprs": [[",,", "\u00bb"], ["\"", "\u00ab"]]}'
-        scriptlet: fulltextregexp
-        slug: na-niemieckie
-        tooltip: "Zamienia cudzys\u0142owy podw\xF3jne na niemieckie"
-    model: toolbar.button
-    pk: 3
--   fields:
-        accesskey: ''
-        group: [2]
-        label: Podstawowa
-        link: ''
-        params: '[["fulltextregexp", {"exprs": [["\ufeff", ""], ["$[\\s]*\\d+[\\s]*^",
-            ""], ["-\\s*^", ""], ["\\,\\.\\.|\\.\\,\\.|\\.\\.\\,", "..."], ["<(/?)P([aert])",
-            "<$1p$2"], ["[\u2014\u2013\u2010-]{2,}|[\u2014\u2013\u2010]+", "---"],
-            ["(\\s)-([^-])", "$1---$2"], ["([^-])-(\\s)", "$1---$2"], ["(\\d)-+(\\d)",
-            "$1--$2"], ["---(\\S)", "--- $1"], ["(\\S)---", "$1 ---"], ["<akap_dialog>\\s*-+\\s*",
-            "<akap_dialog>--- "]]}], ["lineregexp", {"exprs": [["^\\s+|\\s+$", ""],
-            ["\\s+", " "], ["(,,)\\s+", "$1"], ["\\s+(\")", "$1"], ["([^\\.])(\\s*)</p",
-            "$1.$2</p"], ["([\\.:;!\\?])([^\\s\\\\])", "$1 $2"], ["([^\\s])\\s+([\\.:;!\\?])",
-            "$1$2"], ["\\s+,([^,])", ",$1"], ["([^,]),([^\\s\\\\,])", "$1, $2"], ["([A-Za-z])0",
-            "$1o"], ["(\\. )0([A-Za-z])", "$1O$2"], [" 0([A-Za-z])", " o$1"], ["([A-Za-z])1",
-            "$1l"], ["(\\. )1([A-Za-z])", "$1L$2"], [" 1([A-Za-z])", " l$1"]]}]]'
-        scriptlet: macro
-        slug: basic_correction
-        tooltip: "Wykonuj\u0119 podstawow\u0105 korekt\u0119 tekstu."
-    model: toolbar.button
-    pk: 4
--   fields:
-        accesskey: ''
-        group: [1]
-        label: "cz\u0119\u015B\u0107/ksi\u0119ga"
-        link: ''
-        params: '{"padding_top": 1, "padding_bottom": 4, "tag": "naglowek_czesc"}'
-        scriptlet: insert_tag
-        slug: czesc
-        tooltip: ''
-    model: toolbar.button
-    pk: 10
--   fields:
-        accesskey: ''
-        group: [1]
-        label: "rozdzia\u0142"
-        link: ''
-        params: '{"padding_top": 1, "padding_bottom": 4, "tag": "naglowek_rozdzial"}'
-        scriptlet: insert_tag
-        slug: rozdzial
-        tooltip: ''
-    model: toolbar.button
-    pk: 11
--   fields:
-        accesskey: ''
-        group: [1]
-        label: "podrozdzia\u0142"
-        link: ''
-        params: '{"tag": "naglowek_podrozdzial"}'
-        scriptlet: insert_tag
-        slug: podrozdzial
-        tooltip: ''
-    model: toolbar.button
-    pk: 12
--   fields:
-        accesskey: ''
-        group: [1]
-        label: "\u015Br\xF3dtytu\u0142"
-        link: ''
-        params: '{"tag": "srodtytul"}'
-        scriptlet: insert_tag
-        slug: srodtytul
-        tooltip: ''
-    model: toolbar.button
-    pk: 13
--   fields:
-        accesskey: ''
-        group: [1]
-        label: akt
-        link: ''
-        params: '{"padding_top": 1, "padding_bottom": 4, "tag": "naglowek_akt"}'
-        scriptlet: insert_tag
-        slug: akt
-        tooltip: ''
-    model: toolbar.button
-    pk: 14
--   fields:
-        accesskey: ''
-        group: [1]
-        label: scena
-        link: ''
-        params: '{"tag": "naglowek_scena"}'
-        scriptlet: insert_tag
-        slug: scena
-        tooltip: ''
-    model: toolbar.button
-    pk: 15
--   fields:
-        accesskey: ''
-        group: [12]
-        label: "nag\u0142\xF3wek kwestii"
-        link: ''
-        params: '{"padding_top": 1, "padding_bottom": 4, "tag": "naglowek_osoba"}'
-        scriptlet: insert_tag
-        slug: naglowek-kwestii
-        tooltip: "nag\u0142\xF3wek kwestii - nazwa osoby"
-    model: toolbar.button
-    pk: 16
--   fields:
-        accesskey: ''
-        group: [11]
-        label: "tag g\u0142\xF3wny"
-        link: ''
-        params: '{"tag": "utwor"}'
-        scriptlet: insert_tag
-        slug: tag-glowny
-        tooltip: ''
-    model: toolbar.button
-    pk: 17
--   fields:
-        accesskey: ''
-        group: [11]
-        label: opowiadanie
-        link: ''
-        params: '{"padding_top": 1, "padding_bottom": 4, "tag": "opowiadanie"}'
-        scriptlet: insert_tag
-        slug: opowiadanie
-        tooltip: ''
-    model: toolbar.button
-    pk: 18
--   fields:
-        accesskey: ''
-        group: [11]
-        label: "powie\u015B\u0107"
-        link: ''
-        params: '{"padding_top": 1, "padding_bottom": 4, "tag": "powiesc"}'
-        scriptlet: insert_tag
-        slug: powiesc
-        tooltip: ''
-    model: toolbar.button
-    pk: 19
--   fields:
-        accesskey: ''
-        group: [11]
-        label: dramat wiersz.
-        link: ''
-        params: '{"padding_top": 1, "padding_bottom": 4, "tag": "dramat_wierszowany_l"}'
-        scriptlet: insert_tag
-        slug: dramat-wiersz
-        tooltip: ''
-    model: toolbar.button
-    pk: 20
--   fields:
-        accesskey: ''
-        group: [11]
-        label: "dramat wsp\xF3\u0142czesny"
-        link: ''
-        params: '{"padding_top": 1, "padding_bottom": 4, "tag": "dramat_wspolczesny"}'
-        scriptlet: insert_tag
-        slug: dramat-wspolczesny
-        tooltip: "dramat wsp\xF3\u0142czesny (proz\u0105)"
-    model: toolbar.button
-    pk: 21
--   fields:
-        accesskey: ''
-        group: [11]
-        label: "dramat wiersz./w. \u0142am"
-        link: ''
-        params: '{"padding_top": 1, "padding_bottom": 4, "tag": "dramat_wierszowany_lp"}'
-        scriptlet: insert_tag
-        slug: dramat-wiersz-w-lam
-        tooltip: "dramat wierszowany o zw\u0119\u017Conej szeroko\u015Bci \u0142amu"
-    model: toolbar.button
-    pk: 22
--   fields:
-        accesskey: ''
-        group: [11]
-        label: liryka
-        link: ''
-        params: '{"padding_top": 1, "padding_bottom": 4, "tag": "liryka_l"}'
-        scriptlet: insert_tag
-        slug: liryka
-        tooltip: ''
-    model: toolbar.button
-    pk: 23
--   fields:
-        accesskey: ''
-        group: [11]
-        label: "liryka/w. \u0142am"
-        link: ''
-        params: '{"padding_top": 1, "padding_bottom": 4, "tag": "liryka_lp"}'
-        scriptlet: insert_tag
-        slug: liryka-w-lam
-        tooltip: "utw\xF3r liryczny o zw\u0119\u017Conej szeroko\u015Bci \u0142amu"
-    model: toolbar.button
-    pk: 24
--   fields:
-        accesskey: ''
-        group: [11]
-        label: wywiad
-        link: ''
-        params: '{"padding_top": 1, "padding_bottom": 4, "tag": "wywiad"}'
-        scriptlet: insert_tag
-        slug: wywiad
-        tooltip: ''
-    model: toolbar.button
-    pk: 25
--   fields:
-        accesskey: ''
-        group: [13]
-        label: autor
-        link: ''
-        params: '{"padding_top": 1, "padding_bottom": 2, "tag": "autor_utworu"}'
-        scriptlet: insert_tag
-        slug: autor
-        tooltip: ''
-    model: toolbar.button
-    pk: 32
--   fields:
-        accesskey: ''
-        group: [13]
-        label: nazwa utworu
-        link: ''
-        params: '{"padding_top": 1, "padding_bottom": 2, "tag": "nazwa_utworu"}'
-        scriptlet: insert_tag
-        slug: nazwa-utworu
-        tooltip: ''
-    model: toolbar.button
-    pk: 33
--   fields:
-        accesskey: ''
-        group: [1]
-        label: "podtytu\u0142"
-        link: ''
-        params: '{"tag": "podtytul"}'
-        scriptlet: insert_tag
-        slug: podtytul
-        tooltip: ''
-    model: toolbar.button
-    pk: 34
--   fields:
-        accesskey: v
-        group: [13]
-        label: nota
-        link: ''
-        params: '{"tag": "nota"}'
-        scriptlet: insert_tag
-        slug: nota
-        tooltip: ''
-    model: toolbar.button
-    pk: 35
--   fields:
-        accesskey: ''
-        group: [13, 22]
-        label: motto podpis
-        link: ''
-        params: '{"padding_top": 1, "padding_bottom": 2, "tag": "motto_podpis"}'
-        scriptlet: insert_tag
-        slug: motto-podpis
-        tooltip: ''
-    model: toolbar.button
-    pk: 37
--   fields:
-        accesskey: ''
-        group: [13]
-        label: "dzie\u0142o nadrz\u0119dne"
-        link: ''
-        params: '{"padding_top": 1, "padding_bottom": 2, "tag": "dzielo_nadrzedne"}'
-        scriptlet: insert_tag
-        slug: dzielo-nadrzedne
-        tooltip: ''
-    model: toolbar.button
-    pk: 38
--   fields:
-        accesskey: a
-        group: [14, 12]
-        label: akapit
-        link: ''
-        params: '{"padding_top": 1, "padding_bottom": 3, "tag": "akap"}'
-        scriptlet: insert_tag
-        slug: akapit
-        tooltip: wstawia akapit
-    model: toolbar.button
-    pk: 39
--   fields:
-        accesskey: ''
-        group: [14]
-        label: akapit cd.
-        link: ''
-        params: '{"padding_top": 1, "padding_bottom": 3, "tag": "akap_cd"}'
-        scriptlet: insert_tag
-        slug: akapit-cd
-        tooltip: "ci\u0105g dalszy akapitu po wewn\u0105trzakapitowym wtr\u0105ceniu"
-    model: toolbar.button
-    pk: 40
--   fields:
-        accesskey: d
-        group: [14]
-        label: akapit dialogowy
-        link: ''
-        params: '{"padding_top": 1, "padding_bottom": 3, "tag": "akap_dialog"}'
-        scriptlet: insert_tag
-        slug: akapit-dialogowy
-        tooltip: wstawia akapit dialogowy
-    model: toolbar.button
-    pk: 41
--   fields:
-        accesskey: ''
-        group: [21]
-        label: "d\u0142ugi cytat"
-        link: ''
-        params: '{"tag": "dlugi_cytat"}'
-        scriptlet: insert_tag
-        slug: dlugi-cytat
-        tooltip: "d\u0142ugi cytat wyr\xF3\u017Cniony sk\u0142adem"
-    model: toolbar.button
-    pk: 42
--   fields:
-        accesskey: ''
-        group: [21]
-        label: "ramka"
-        link: ''
-        params: '{"tag": "ramka"}'
-        scriptlet: insert_tag
-        slug: ramka
-        tooltip: "tekst otoczony ramką"
-    model: toolbar.button
-    pk: 43
--   fields:
-        accesskey: f
-        group: [12, 15]
-        label: "wyr\xF3\u017Cnienie"
-        link: ''
-        params: '{"tag": "wyroznienie"}'
-        scriptlet: insert_tag
-        slug: wyroznienie
-        tooltip: "wyr\xF3\u017Cnienie autorskie"
-    model: toolbar.button
-    pk: 44
--   fields:
-        accesskey: ''
-        group: [15]
-        label: "tytu\u0142 dzie\u0142a typ 1"
-        link: ''
-        params: '{"tag": "tytul_dziela", "attrs": {"typ": "1"}}'
-        scriptlet: insert_tag
-        slug: tytul-dziela-typ
-        tooltip: "tytu\u0142 dzie\u0142a w cytowanym tytule dzie\u0142a"
-    model: toolbar.button
-    pk: 45
--   fields:
-        accesskey: e
-        group: [15]
-        label: "s\u0142owo obce"
-        link: ''
-        params: '{"tag": "slowo_obce"}'
-        scriptlet: insert_tag
-        slug: slowo-obce
-        tooltip: "frazy w j\u0119zykach innych ni\u017C polski/definiendum w przypisie"
-    model: toolbar.button
-    pk: 46
--   fields:
-        accesskey: ''
-        group: [15]
-        label: matemat.
-        link: ''
-        params: '{"tag": "mat"}'
-        scriptlet: insert_tag
-        slug: matemat
-        tooltip: "wyra\u017Cenia matematyczne lub zmienne symboliczne"
-    model: toolbar.button
-    pk: 47
--   fields:
-        accesskey: ''
-        group: [15]
-        label: www
-        link: ''
-        params: '{"tag": "www"}'
-        scriptlet: insert_tag
-        slug: www
-        tooltip: ''
-    model: toolbar.button
-    pk: 48
--   fields:
-        accesskey: ''
-        group: [15]
-        label: indeks dolny
-        link: ''
-        params: '{"tag": "indeks_dolny"}'
-        scriptlet: insert_tag
-        slug: indeks-dolny
-        tooltip: ''
-    model: toolbar.button
-    pk: 49
--   fields:
-        accesskey: ''
-        group: [15]
-        label: "wi\u0119ksze odst\u0119py"
-        link: ''
-        params: '{"tag": "wieksze_odstepy"}'
-        scriptlet: insert_tag
-        slug: wieksze-odstepy
-        tooltip: "wi\u0119ksze odst\u0119py mi\u0119dzy s\u0142owami"
-    model: toolbar.button
-    pk: 50
--   fields:
-        accesskey: ''
-        group: [29]
-        label: uwaga
-        link: ''
-        params: '{"tag": "uwaga"}'
-        scriptlet: insert_tag
-        slug: uwaga
-        tooltip: 'uwagi redaktorsko-korektorskie '
-    model: toolbar.button
-    pk: 51
--   fields:
-        accesskey: ''
-        group: [16]
-        label: "\u015Bwiat\u0142o"
-        link: ''
-        params: '{"nocontent": "true", "tag": "sekcja_swiatlo"}'
-        scriptlet: insert_tag
-        slug: sep-swiatlo
-        tooltip: "\u015Bwiat\u0142o rozdzielaj\u0105ce sekcje tekstu"
-    model: toolbar.button
-    pk: 53
--   fields:
-        accesskey: ''
-        group: [16]
-        label: asterysk
-        link: ''
-        params: '{"nocontent": "true", "tag": "sekcja_asterysk"}'
-        scriptlet: insert_tag
-        slug: sep-asterysk
-        tooltip: rozdzielenie partii tekstu asteryskiem
-    model: toolbar.button
-    pk: 54
--   fields:
-        accesskey: ''
-        group: [16]
-        label: linia
-        link: ''
-        params: '{"nocontent": "true", "tag": "separator_linia"}'
-        scriptlet: insert_tag
-        slug: sep-linia
-        tooltip: "rozdzielenie partii tekstu pozioma lini\u0105"
-    model: toolbar.button
-    pk: 55
--   fields:
-        accesskey: ''
-        group: [16]
-        label: "zast\u0119pnik wersu"
-        link: ''
-        params: '{"tag": "zastepnik_wersu"}'
-        scriptlet: insert_tag
-        slug: zastepnik-wersu
-        tooltip: wykropkowanie wersu
-    model: toolbar.button
-    pk: 56
--   fields:
-        accesskey: ''
-        group: [12]
-        label: didaskalia
-        link: ''
-        params: '{"padding_top": 1, "padding_bottom": 2, "tag": "didaskalia"}'
-        scriptlet: insert_tag
-        slug: didaskalia
-        tooltip: ''
-    model: toolbar.button
-    pk: 62
--   fields:
-        accesskey: ''
-        group: [12]
-        label: didaskalia tekstowe
-        link: ''
-        params: '{"tag": "didask_tekst"}'
-        scriptlet: insert_tag
-        slug: didaskalia-tekstowe
-        tooltip: "didaskalia umieszczone w obr\u0119bie innego tekstu"
-    model: toolbar.button
-    pk: 63
--   fields:
-        accesskey: b
-        group: [12]
-        label: osoba
-        link: ''
-        params: '{"tag": "osoba"}'
-        scriptlet: insert_tag
-        slug: osoba
-        tooltip: "wstawia nazw\u0119 osoby w didaskaliach"
-    model: toolbar.button
-    pk: 64
--   fields:
-        accesskey: ''
-        group: [21]
-        label: "d\u0142ugi cyt. poet."
-        link: ''
-        params: '{"tag": "poezja_cyt"}'
-        scriptlet: insert_tag
-        slug: dlugi-cyt-poet
-        tooltip: "d\u0142ugi cytat wierszowany wyr\xF3\u017Cniony sk\u0142adem"
-    model: toolbar.button
-    pk: 67
--   fields:
-        accesskey: ''
-        group: [26]
-        label: przypis autorski
-        link: ''
-        params: '{"tag": "pa"}'
-        scriptlet: insert_tag
-        slug: przypis-autorski
-        tooltip: ''
-    model: toolbar.button
-    pk: 68
--   fields:
-        accesskey: ''
-        group: [26]
-        label: "przypis t\u0142umacza"
-        link: ''
-        params: '{"tag": "pt"}'
-        scriptlet: insert_tag
-        slug: przypis-tlumacza
-        tooltip: ''
-    model: toolbar.button
-    pk: 69
--   fields:
-        accesskey: ''
-        group: [26]
-        label: przypis redaktorski
-        link: ''
-        params: '{"tag": "pr"}'
-        scriptlet: insert_tag
-        slug: przypis-redaktorski
-        tooltip: ''
-    model: toolbar.button
-    pk: 70
--   fields:
-        accesskey: ''
-        group: [26]
-        label: przypis edytorski
-        link: ''
-        params: '{"tag": "pe"}'
-        scriptlet: insert_tag
-        slug: przypis-edytorski
-        tooltip: ''
-    model: toolbar.button
-    pk: 71
--   fields:
-        accesskey: ''
-        group: [21]
-        label: wywiad pytanie
-        link: ''
-        params: '{"tag": "wywiad_pyt"}'
-        scriptlet: insert_tag
-        slug: wywiad-pytanie
-        tooltip: ''
-    model: toolbar.button
-    pk: 72
--   fields:
-        accesskey: ''
-        group: [21]
-        label: "wywiad odpowied\u017A"
-        link: ''
-        params: '{"tag": "wywiad_odp"}'
-        scriptlet: insert_tag
-        slug: wywiad-odpowiedz
-        tooltip: ''
-    model: toolbar.button
-    pk: 73
--   fields:
-        accesskey: ''
-        group: [13, 22]
-        label: dedykacja
-        link: ''
-        params: '{"tag": "dedykacja"}'
-        scriptlet: insert_tag
-        slug: dedykacja
-        tooltip: ''
-    model: toolbar.button
-    pk: 74
--   fields:
-        accesskey: ''
-        group: [13, 22]
-        label: motto
-        link: ''
-        params: '{"padding_top": 1, "padding_bottom": 3, "tag": "motto"}'
-        scriptlet: insert_tag
-        slug: motto
-        tooltip: ''
-    model: toolbar.button
-    pk: 75
--   fields:
-        accesskey: u
-        group: [2]
-        label: "A<sup>\u2193</sup>"
-        link: ''
-        params: '[]'
-        scriptlet: lowercase
-        slug: tolowercase
-        tooltip: "Zamie\u0144 wielkie litery na ma\u0142e"
-    model: toolbar.button
-    pk: 76
--   fields:
-        accesskey: ''
-        group: [2]
-        label: "zamiana cudzys\u0142ow\xF3w 2"
-        link: ''
-        params: '{"exprs": [["\u00bb|\u201e", ",,"], ["\u00ab", "\""], ["([^=])\"([\u0104\u0118\u00d3\u0141\u017b\u0179\u0106\u0143\u0105\u017c\u017a\u015b\u0144\u00f3\u0142\u0107\\w])",
-            "$1,,$2"], ["^\"([\u0104\u0118\u00d3\u0141\u017b\u0179\u0106\u0143\u0105\u017c\u017a\u015b\u0144\u00f3\u0142\u0107\\w])",
-            ",,$1"], ["(,,)\\s+|\\s+(\")", "$1"]]}'
-        scriptlet: lineregexp
-        slug: cudzyslow-niemiecki
-        tooltip: "zamienia \" na ,, oraz \xBBa\xAB na ,,a\""
-    model: toolbar.button
-    pk: 77
--   fields:
-        accesskey: ''
-        group: [22]
-        label: 'typ osoby '
-        link: ''
-        params: '{"padding_top": 1, "padding_bottom": 1, "tag": "lista_osoba", "attrs":
-            {"typ": ""}}'
-        scriptlet: insert_tag
-        slug: lista-osob-pole
-        tooltip: osoby z takim samym opisem
-    model: toolbar.button
-    pk: 78
--   fields:
-        accesskey: ''
-        group: [22]
-        label: "didaskalia pocz\u0105tkowe"
-        link: ''
-        params: '{"padding_top": 1, "padding_bottom": 3, "tag": "miejsce_czas"}'
-        scriptlet: insert_tag
-        slug: didaskalia-poczatkowe
-        tooltip: "komentarze wprowadzaj\u0105ce przed tekstem dramatu"
-    model: toolbar.button
-    pk: 79
--   fields:
-        accesskey: s
-        group: [12, 17]
-        label: strofa
-        link: ''
-        params: '{"padding_top": 1, "padding_bottom": 3, "tag": "strofa"}'
-        scriptlet: insert_stanza
-        slug: strofa
-        tooltip: "wstawia strof\u0119"
-    model: toolbar.button
-    pk: 81
--   fields:
-        accesskey: k
-        group: [12]
-        label: kwestia
-        link: ''
-        params: '{"padding_top": 1, "padding_bottom": 1, "tag": "kwestia"}'
-        scriptlet: insert_tag
-        slug: kwestia
-        tooltip: "wstawia kwesti\u0119"
-    model: toolbar.button
-    pk: 82
--   fields:
-        accesskey: w
-        group: [12, 17]
-        label: "wers mocno wci\u0119ty"
-        link: ''
-        params: '{"tag": "wers_wciety", "attrs": {"typ": ""}}'
-        scriptlet: insert_tag
-        slug: wers-mocno-wciety
-        tooltip: "argumenty wersu wci\u0119tego: od 2 do 6"
-    model: toolbar.button
-    pk: 84
--   fields:
-        accesskey: ''
-        group: [12, 17]
-        label: wers cd.
-        link: ''
-        params: '{"tag": "wers_cd"}'
-        scriptlet: insert_tag
-        slug: wers-cd
-        tooltip: "cz\u0119\u015B\u0107 wersu przeniesiona do innego wiersza"
-    model: toolbar.button
-    pk: 85
--   fields:
-        accesskey: ''
-        group: [12, 17]
-        label: wers do prawej
-        link: ''
-        params: '{"tag": "wers_do_prawej"}'
-        scriptlet: insert_tag
-        slug: wers-do-prawej
-        tooltip: "wers wyr\xf3wnany do prawej"
-    model: toolbar.button
-    pk: 109
--   fields:
-        accesskey: ''
-        group: []
-        label: Wydrukuj
-        link: print/xml
-        params: '[]'
-        scriptlet: insert_tag
-        slug: print-xml
-        tooltip: ''
-    model: toolbar.button
-    pk: 86
--   fields:
-        accesskey: ''
-        group: []
-        label: Wydrukuj
-        link: print/html
-        params: '[]'
-        scriptlet: insert_tag
-        slug: htmleditor-print
-        tooltip: ''
-    model: toolbar.button
-    pk: 87
--   fields:
-        accesskey: ''
-        group: [2]
-        label: "zamiana cudzys\u0142ow\xF3w 1"
-        link: ''
-        params: '{"exprs": [["\u00ab|\u201e", ",,"], ["\u00bb", "\""], ["([^=])\"([\u0104\u0118\u00d3\u0141\u017b\u0179\u0106\u0143\u0105\u017c\u017a\u015b\u0144\u00f3\u0142\u0107\\w])",
-            "$1,,$2"], ["^\"([\u0104\u0118\u00d3\u0141\u017b\u0179\u0106\u0143\u0105\u017c\u017a\u015b\u0144\u00f3\u0142\u0107\\w])",
-            ",,$1"], ["(,,)\\s+|\\s+(\")", "$1"]]}'
-        scriptlet: lineregexp
-        slug: cudzyslow-francuski
-        tooltip: "zamiana \" na ,, oraz  \xABa\xBB na ,,a\""
-    model: toolbar.button
-    pk: 89
--   fields:
-        accesskey: q
-        group: [12, 17]
-        label: "wers wci\u0119ty"
-        link: ''
-        params: '{"tag": "wers_wciety", "attrs": {"typ": "1"}}'
-        scriptlet: insert_tag
-        slug: wers-wciety
-        tooltip: "wstawia wers wci\u0119ty"
-    model: toolbar.button
-    pk: 91
--   fields:
-        accesskey: r
-        group: [15]
-        label: "tytu\u0142 dzie\u0142a"
-        link: ''
-        params: '{"tag": "tytul_dziela"}'
-        scriptlet: insert_tag
-        slug: tytul-dziela
-        tooltip: ''
-    model: toolbar.button
-    pk: 92
--   fields:
-        accesskey: ''
-        group: [22]
-        label: "lista os\xF3b"
-        link: ''
-        params: '{"padding_top": 1, "padding_bottom": 4, "tag": "lista_osob"}'
-        scriptlet: insert_tag
-        slug: lista-osob
-        tooltip: "lista os\xF3b poprzedzaj\u0105ca tekst dramatu"
-    model: toolbar.button
-    pk: 93
--   fields:
-        accesskey: ''
-        group: [22]
-        label: "nag\u0142\xF3wek listy"
-        link: ''
-        params: '{"padding_top": 1, "padding_bottom": 2, "tag": "naglowek_listy"}'
-        scriptlet: insert_tag
-        slug: naglowek-listy
-        tooltip: "nag\u0142\xF3wek listy os\xF3b"
-    model: toolbar.button
-    pk: 94
--   fields:
-        accesskey: ''
-        group: [22]
-        label: osoba na liscie
-        link: ''
-        params: '{"padding_top": 1, "padding_bottom": 1, "tag": "lista_osoba"}'
-        scriptlet: insert_tag
-        slug: osoba-na-liscie
-        tooltip: "nazwa osoby na liscie os\xF3b"
-    model: toolbar.button
-    pk: 95
--   fields:
-        accesskey: ''
-        group: []
-        label: extra
-        link: ''
-        params: '{"tag": "extra"}'
-        scriptlet: insert_tag
-        slug: extra
-        tooltip: "uwagi dotycz\u0105ce sk\u0142adu"
-    model: toolbar.button
-    pk: 96
--   fields:
-        accesskey: ''
-        group: [28]
-        label: akapity
-        link: ''
-        params: '{"tag": "akap"}'
-        scriptlet: autotag
-        slug: akapity
-        tooltip: "autotagowanie akapit\xF3w"
-    model: toolbar.button
-    pk: 97
--   fields:
-        accesskey: ''
-        group: [28]
-        label: strofy
-        link: ''
-        params: '{"tag": "strofa"}'
-        scriptlet: autotag
-        slug: strofy
-        tooltip: autotagowanie strof
-    model: toolbar.button
-    pk: 99
--   fields:
-        accesskey: ''
-        group: [28]
-        label: "wersy wci\u0119te"
-        link: ''
-        params: '{"padding": 1, "tag": "wers_wciety", "split": 1}'
-        scriptlet: autotag
-        slug: wersy-wciete
-        tooltip: "autotagowanie wers\xF3w wci\u0119tych"
-    model: toolbar.button
-    pk: 100
--   fields:
-        accesskey: g
-        group: [12]
-        label: kwestioakapit
-        link: ''
-        params: '[["insert_tag", {"tag": "akap"}], ["insert_tag", {"padding_top":
-            1, "padding_bottom": 1, "tag": "kwestia"}]]'
-        scriptlet: macro
-        slug: kwestioakapit
-        tooltip: ''
-    model: toolbar.button
-    pk: 101
--   fields:
-        accesskey: ''
-        group: [12]
-        label: kwestiostrofa
-        link: ''
-        params: '[["insert_stanza", {"tag": "strofa"}], ["insert_tag", {"padding_top":
-            1, "padding_bottom": 1, "tag": "kwestia"}]]'
-        scriptlet: macro
-        slug: kwestiostrofa
-        tooltip: ''
-    model: toolbar.button
-    pk: 102
--   fields:
-        accesskey: ''
-        group: [28]
-        label: "nag\u0142. dramatu"
-        link: ''
-        params: '{"exprs": [["^AKT(\\s\\w*)$", "<naglowek_akt>AKT$1</naglowek_akt>"],
-            ["^SCENA(\\s\\w*)$", "<naglowek_scena>SCENA$1</naglowek_scena>"], ["([A-Z\u0104\u0106\u0118\u0141\u0143\u00d3\u015a\u017b\u0179]{2}[A-Z\u0104\u0106\u0118\u0141\u0143\u00d3\u015a\u017b\u0179\\s]+)$",
-            "<naglowek_osoba>$1</naglowek_osoba>"]]}'
-        scriptlet: lineregexp
-        slug: nagl-dramatu
-        tooltip: "autotagowanie akt\xF3w, scen, nag\u0142\xF3wk\xF3w os\xF3b"
-    model: toolbar.button
-    pk: 103
--   fields:
-        accesskey: x
-        group: [13]
-        label: nota red.
-        link: ''
-        params: '{"padding_top": 1, "padding_bottom": 3, "tag": "nota_red"}'
-        scriptlet: insert_tag
-        slug: nota-red
-        tooltip: nota redakcyjna
-    model: toolbar.button
-    pk: 104
--   fields:
-        accesskey: ''
-        group: [2]
-        label: slug
-        link: ''
-        params: '[]'
-        scriptlet: slugify
-        slug: slug
-        tooltip: slugifikacja
-    model: toolbar.button
-    pk: 105
--   fields:
-        accesskey: ''
-        group: [11]
-        label: trim begin
-        link: ''
-        params: '{"text": "\n<!-- TRIM_BEGIN -->\n"}'
-        scriptlet: insert_text
-        slug: trim-begin
-        tooltip: "Wstawia pocz\u0105tkowy znacznik ci\u0119cia cz\u0119\u015Bci"
-    model: toolbar.button
-    pk: 106
--   fields:
-        accesskey: ''
-        group: [11]
-        label: trim end
-        link: ''
-        params: '{"text": "\n<!-- TRIM_END -->\n"}'
-        scriptlet: insert_text
-        slug: trim-end
-        tooltip: "Wstawia ko\u0144cowy znacznik ci\u0119cia cz\u0119\u015Bci"
-    model: toolbar.button
-    pk: 107
--   fields:
-        accesskey: ''
-        group: [11]
-        label: etap
-        link: ''
-        params: '{"tag": "developmentStage"}'
-        scriptlet: insert_tag
-        slug: etap
-        tooltip: "wymaga uwsp\xF3\u0142czesnienia: 0.3"
-    model: toolbar.button
-    pk: 108
--   fields: {code: '-'}
-    model: toolbar.scriptlet
-    pk: autotag
--   fields: {code: "editor.showPopup('generic-info', 'Przetwarzanie zaznaczonego tekstu...',\
-            \ '', -1);\n$.log(editor, panel, params);\nvar cm = panel.texteditor;\n\
-            var exprs = $.map(params.exprs, function(expr) {\n    var opts = \"mg\"\
-            ;\n    if(expr.length > 2)\n        opts = expr[2];\n\n    return {rx:\
-            \ new RegExp(expr[0], opts), repl: expr[1]};\n});\n\nvar partial = true;\n\
-            var text = cm.selection();\n\nif(!text) {\n    var cpos = cm.cursorPosition();\n\
-            \    cpos.line = cm.lineNumber(cpos.line)\n    cm.selectLines(cm.firstLine(),\
-            \ 0, cm.lastLine(), 0);\n\n    text = cm.selection();\n    partial = false;\n\
-            }\n\nvar original = text;\n$(exprs).each(function() { \n    text = text.replace(this.rx,\
-            \ this.repl);\n});\n\nif( original != text) \n{    \n    cm.replaceSelection(text);\n\
-            \    panel.fireEvent('contentChanged');\n    editor.showPopup('generic-yes',\
-            \ 'Zmieniono tekst' );\n    editor.advancePopupQueue();\n}\nelse {\n \
-            \   editor.showPopup('generic-info', 'Brak zmian w tek\u015Bcie.');\n\
-            \    editor.advancePopupQueue();\n}\n\nif(!partial) {\n    cm.selectLines(\
-            \ cm.nthLine(cpos.line), cpos.character );\n}"}
-    model: toolbar.scriptlet
-    pk: fulltextregexp
--   fields: {code: "var texteditor = panel.texteditor;\r\nvar text = texteditor.selection();\r\
-            \n\r\nif(text) {\r\n  var verses = text.split('\\n');\r\n  var text =\
-            \ ''; var buf = ''; var ebuf = '';\r\n  var first = true;\r\n\r\n  for(var\
-            \ i=0;  i < verses.length; i++) {\r\n    verse = verses[i].replace(/^\\\
-            s+/, \"\").replace(/\\s+$/, \"\");   \r\n    if(verse) {\r\n      text\
-            \ += (buf ? buf + '/\\n' : '') + ebuf;\r\n      buf = (first ? '<strofa>\\\
-            n' : '') + verses[i];\r\n      ebuf = '';\r\n      first = false;\r\n\
-            \    } else {    \r\n      ebuf += '\\n' + verses[i];\r\n    }\r\n  };\r\
-            \n  text = text + buf + '\\n</strofa>' + ebuf; \r\n  texteditor.replaceSelection(text);\r\
-            \n}\r\n\r\nif (!text) {\r\n    var pos = texteditor.cursorPosition();\r\
-            \n    texteditor.selectLines(pos.line, pos.character + 6 + 2);\r\n}\r\n\
-            \r\n\r\n\r\n\r\n\r\n\r\n\r\npanel.fireEvent('contentChanged');"}
-    model: toolbar.scriptlet
-    pk: insert_stanza
--   fields: {code: "var texteditor = panel.texteditor;\nvar text = texteditor.selection();\n\
-            var start_tag = '<'+params.tag;\nfor (var attr in params.attrs) {\n  \
-            \  start_tag += ' '+attr+'=\"' + params.attrs[attr] + '\"';\n};\nstart_tag\
-            \ += '>';\nvar end_tag = '</'+params.tag+'>';\n\nif(text.length > 0) {\n\
-            // tokenize\nvar output = ''\nvar token = ''\nfor(var index=0; index <\
-            \ text.length; index++)\n{\n    if (text[index].match(/\\s/)) { // whitespace\n\
-            \        token += text[index];\n    }\n    else { // character\n     \
-            \   output += token;\n        if(output == token) output += start_tag;\n\
-            \        token = ''\n        output += text[index];\n    }\n}\n\nif( output[output.length-1]\
-            \ == '\\\\' ) {\n    output = output.substr(0, output.length-1) + end_tag\
-            \ + '\\\\';\n} else {\n    output += end_tag;\n}\noutput += token;\n}\n\
-            else {\n output = start_tag + end_tag;\n}\n\ntexteditor.replaceSelection(output);\n\
-            \nif (text.length == 0) {\n    var pos = texteditor.cursorPosition();\n\
-            \    texteditor.selectLines(pos.line, pos.character + params.tag.length\
-            \ + 2);\n}\n\npanel.fireEvent('contentChanged');"}
-    model: toolbar.scriptlet
-    pk: insert_tag
--   fields: {code: '-'}
-    model: toolbar.scriptlet
-    pk: insert_text
--   fields: {code: "editor.showPopup('generic-info', 'Przetwarzanie zaznaczonego tekstu...',\
-            \ '', -1);\n\nvar cm = panel.texteditor;\nvar exprs = $.map(params.exprs,\
-            \ function(expr) {\n\n    var opts = \"g\";\n\n    if(expr.length > 2)\n\
-            \n        opts = expr[2];\n\n    return {rx: new RegExp(expr[0], opts),\
-            \ repl: expr[1]};\n\n});\n\n\n\nvar partial = true;\n\nvar text = cm.selection();\n\
-            \n\n\nif(!text) {\n\n    var cpos = cm.cursorPosition();\n\n    cpos.line\
-            \ = cm.lineNumber(cpos.line)\n\n    cm.selectLines(cm.firstLine(), 0,\
-            \ cm.lastLine(), 0);\n\n    text = cm.selection();\n\n    partial = false;\n\
-            \n}\n\n\n\nvar changed = 0;\nvar lines = text.split('\\n');\nvar lines\
-            \ = $.map(lines, function(line) { \n    var old_line = line;\n    $(exprs).each(function()\
-            \ { \n        var expr = this;\n        line = line.replace(expr.rx, expr.repl);\n\
-            \    });\n\n    if(old_line != line) changed += 1;\n    return line;\n\
-            });\n\nif(changed > 0) \n{\n    cm.replaceSelection( lines.join('\\n')\
-            \ );\n    panel.fireEvent('contentChanged');\n    editor.showPopup('generic-yes',\
-            \ 'Zmieniono ' + changed + ' linii.', 1500);\n    editor.advancePopupQueue();\n\
-            }\nelse {\n    editor.showPopup('generic-info',  'Brak zmian w tek\u015B\
-            cie', 1500);\n    editor.advancePopupQueue();\n}\n\nif(!partial)\n   \
-            \ cm.selectLines( cm.nthLine(cpos.line), cpos.character )"}
-    model: toolbar.scriptlet
-    pk: lineregexp
--   fields: {code: "var cm = panel.texteditor;\r\nvar text = cm.selection();\r\n\r\
-            \nif(!text) return;\r\nvar repl = '';\r\nvar lcase = text.toLowerCase();\r\
-            \nvar ucase = text.toUpperCase();\r\n\r\nif(lcase == text) repl = ucase;\
-            \ /* was lowercase */\r\nelse if(ucase != text) repl = lcase; /* neither\
-            \ lower- or upper-case */\r\nelse { /* upper case -> title-case */\r\n\
-            \   var words = $(lcase.split(/\\s/)).map(function() { \r\n        if(this.length\
-            \ > 0) { return this[0].toUpperCase() + this.slice(1); } else { return\
-            \ ''}\r\n   }); \r\n   repl = words.join(' ');\r\n} \r\n\r\nif(repl !=\
-            \ text) {\r\n    cm.replaceSelection(repl);\r\n    panel.fireEvent('contentChanged');\r\
-            \n};"}
-    model: toolbar.scriptlet
-    pk: lowercase
--   fields: {code: "$(params).each(function() {\n    $.log(this[0], this[1]);\n  \
-            \  editor.callScriptlet(this[0], panel, this[1]);\n\n});"}
-    model: toolbar.scriptlet
-    pk: macro
--   fields: {code: '-'}
-    model: toolbar.scriptlet
-    pk: slugify
diff --git a/apps/toolbar/locale/pl/LC_MESSAGES/django.mo b/apps/toolbar/locale/pl/LC_MESSAGES/django.mo
deleted file mode 100644 (file)
index 54dabdf..0000000
Binary files a/apps/toolbar/locale/pl/LC_MESSAGES/django.mo and /dev/null differ
diff --git a/apps/toolbar/locale/pl/LC_MESSAGES/django.po b/apps/toolbar/locale/pl/LC_MESSAGES/django.po
deleted file mode 100644 (file)
index 8c7cfb1..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-# 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-08-03 12:14+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:17
-msgid "button group"
-msgstr "grupa przycisków"
-
-#: models.py:17
-msgid "button groups"
-msgstr "grupy przycisków"
-
-#: models.py:51
-msgid "button"
-msgstr "przycisk"
-
-#: models.py:51
-msgid "buttons"
-msgstr "przyciski"
-
-#: models.py:75
-msgid "javascript"
-msgstr ""
diff --git a/apps/toolbar/management/__init__.py b/apps/toolbar/management/__init__.py
deleted file mode 100644 (file)
index 792d600..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#
diff --git a/apps/toolbar/management/commands/__init__.py b/apps/toolbar/management/commands/__init__.py
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/apps/toolbar/management/commands/fixbuttons.py b/apps/toolbar/management/commands/fixbuttons.py
deleted file mode 100644 (file)
index de48ced..0000000
+++ /dev/null
@@ -1,73 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-#
-# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
-# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
-#
-
-from django.core.management.base import NoArgsCommand
-from toolbar.models import Button, ButtonGroup
-import json
-import re
-
-
-class Command(NoArgsCommand):
-
-    def handle_noargs(self, **options):
-        buttons = Button.objects.all()
-        print "Validating parameters... "
-        for b in buttons:
-            params = b.params
-            try:
-                v = json.loads(b.params)
-            except ValueError, e:
-                print 'Trying to fix button "%s" ...' % b.slug
-                if params[0] == u'(':
-                    params = params[1:]
-                if params[-1] == u')':
-                    params = params[:-1]
-                try:
-                    v = son.loads(re.sub(u'([\\w-]+)\\s*:', u'"\\1": ', params).encode('utf-8'))
-                except ValueError, e:
-                    print "Unable to fix '%s' " % b.params
-                    print "Try to fix this button manually and rerun the script."
-                    return False
-
-            # resave
-            b.params = json.dumps(v)
-            b.save()
-
-        print "Merge duplicate buttons (if any)..."
-        hash = {}
-        for b in buttons:
-            if b.slug not in hash:
-                hash[b.slug] = b
-                continue
-
-            # duplicate button
-            print "Found duplicate of '%s'" % b.slug
-            a = hash[b.slug]
-
-            remove_duplicate = True
-            if a.params != b.params:
-                print "Conflicting params for duplicate of '%s'." % b.slug
-                print "Groups will be joined, but won't remove duplicates."
-                remove_duplicate = False
-
-            for g in b.group.all():
-                a.group.add(g)
-
-            b.group.clear()
-
-            a.save()
-            if remove_duplicate:
-                b.delete()
-
-        print "Searching for empty groups and orphaned buttons:"
-        for g in ButtonGroup.objects.all():
-            if len(g.button_set.all()) == 0:
-                print "Empty group: '%s'" % g.slug
-
-        for b in Button.objects.all():
-            if len(b.group.all()) == 0:
-                print "orphan: '%s'" % b.slug
diff --git a/apps/toolbar/migrations/0001_initial.py b/apps/toolbar/migrations/0001_initial.py
deleted file mode 100644 (file)
index 5a6f27c..0000000
+++ /dev/null
@@ -1,93 +0,0 @@
-# encoding: utf-8
-import datetime
-from south.db import db
-from south.v2 import SchemaMigration
-from django.db import models
-
-class Migration(SchemaMigration):
-
-    def forwards(self, orm):
-
-        # Adding model 'ButtonGroup'
-        db.create_table('toolbar_buttongroup', (
-            ('position', self.gf('django.db.models.fields.IntegerField')(default=0)),
-            ('slug', self.gf('django.db.models.fields.SlugField')(max_length=50, db_index=True)),
-            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
-            ('name', self.gf('django.db.models.fields.CharField')(max_length=32)),
-        ))
-        db.send_create_signal('toolbar', ['ButtonGroup'])
-
-        # Adding model 'Button'
-        db.create_table('toolbar_button', (
-            ('key_mod', self.gf('django.db.models.fields.PositiveIntegerField')(default=1, blank=True)),
-            ('scriptlet', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['toolbar.Scriptlet'], null=True, blank=True)),
-            ('tooltip', self.gf('django.db.models.fields.CharField')(max_length=120, blank=True)),
-            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
-            ('link', self.gf('django.db.models.fields.CharField')(default='', max_length=256, blank=True)),
-            ('key', self.gf('django.db.models.fields.CharField')(max_length=1, blank=True)),
-            ('params', self.gf('django.db.models.fields.TextField')(default='[]')),
-            ('label', self.gf('django.db.models.fields.CharField')(max_length=32)),
-            ('slug', self.gf('django.db.models.fields.SlugField')(unique=True, max_length=50, db_index=True)),
-        ))
-        db.send_create_signal('toolbar', ['Button'])
-
-        # Adding M2M table for field group on 'Button'
-        db.create_table('toolbar_button_group', (
-            ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
-            ('button', models.ForeignKey(orm['toolbar.button'], null=False)),
-            ('buttongroup', models.ForeignKey(orm['toolbar.buttongroup'], null=False))
-        ))
-        db.create_unique('toolbar_button_group', ['button_id', 'buttongroup_id'])
-
-        # Adding model 'Scriptlet'
-        db.create_table('toolbar_scriptlet', (
-            ('code', self.gf('django.db.models.fields.TextField')()),
-            ('name', self.gf('django.db.models.fields.CharField')(max_length=64, primary_key=True)),
-        ))
-        db.send_create_signal('toolbar', ['Scriptlet'])
-
-
-    def backwards(self, orm):
-
-        # Deleting model 'ButtonGroup'
-        db.delete_table('toolbar_buttongroup')
-
-        # Deleting model 'Button'
-        db.delete_table('toolbar_button')
-
-        # Removing M2M table for field group on 'Button'
-        db.delete_table('toolbar_button_group')
-
-        # Deleting model 'Scriptlet'
-        db.delete_table('toolbar_scriptlet')
-
-
-    models = {
-        'toolbar.button': {
-            'Meta': {'object_name': 'Button'},
-            'group': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['toolbar.ButtonGroup']"}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'key': ('django.db.models.fields.CharField', [], {'max_length': '1', 'blank': 'True'}),
-            'key_mod': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1', 'blank': 'True'}),
-            'label': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
-            'link': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
-            'params': ('django.db.models.fields.TextField', [], {'default': "'[]'"}),
-            'scriptlet': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['toolbar.Scriptlet']", 'null': 'True', 'blank': 'True'}),
-            'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50', 'db_index': 'True'}),
-            'tooltip': ('django.db.models.fields.CharField', [], {'max_length': '120', 'blank': 'True'})
-        },
-        'toolbar.buttongroup': {
-            'Meta': {'object_name': 'ButtonGroup'},
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
-            'position': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'})
-        },
-        'toolbar.scriptlet': {
-            'Meta': {'object_name': 'Scriptlet'},
-            'code': ('django.db.models.fields.TextField', [], {}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '64', 'primary_key': 'True'})
-        }
-    }
-
-    complete_apps = ['toolbar']
diff --git a/apps/toolbar/migrations/0002_auto__del_field_button_key_mod__chg_field_button_key.py b/apps/toolbar/migrations/0002_auto__del_field_button_key_mod__chg_field_button_key.py
deleted file mode 100644 (file)
index af58c6e..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-# encoding: utf-8
-import datetime
-from south.db import db
-from south.v2 import SchemaMigration
-from django.db import models
-
-class Migration(SchemaMigration):
-
-    def forwards(self, orm):
-
-        # Deleting field 'Button.key_mod'
-        db.delete_column('toolbar_button', 'key_mod')
-
-        # Changing field 'Button.key'
-        db.alter_column('toolbar_button', 'key', self.gf('django.db.models.fields.CharField')(max_length=1, null=True))
-
-    def backwards(self, orm):
-
-        # Adding field 'Button.key_mod'
-        db.add_column('toolbar_button', 'key_mod', self.gf('django.db.models.fields.PositiveIntegerField')(default=1, blank=True), keep_default=False)
-
-        # Changing field 'Button.key'
-        db.alter_column('toolbar_button', 'key', self.gf('django.db.models.fields.CharField')(max_length=1, blank=True))
-
-    models = {
-        'toolbar.button': {
-            'Meta': {'object_name': 'Button'},
-            'group': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['toolbar.ButtonGroup']"}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'key': ('django.db.models.fields.CharField', [], {'max_length': '1', 'null': 'True'}),
-            'label': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
-            'link': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
-            'params': ('django.db.models.fields.TextField', [], {'default': "'[]'"}),
-            'scriptlet': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['toolbar.Scriptlet']", 'null': 'True', 'blank': 'True'}),
-            'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50', 'db_index': 'True'}),
-            'tooltip': ('django.db.models.fields.CharField', [], {'max_length': '120', 'blank': 'True'})
-        },
-        'toolbar.buttongroup': {
-            'Meta': {'object_name': 'ButtonGroup'},
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
-            'position': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'})
-        },
-        'toolbar.scriptlet': {
-            'Meta': {'object_name': 'Scriptlet'},
-            'code': ('django.db.models.fields.TextField', [], {}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '64', 'primary_key': 'True'})
-        }
-    }
-
-    complete_apps = ['toolbar']
diff --git a/apps/toolbar/migrations/0003_button_key_rename_to_accesskey.py b/apps/toolbar/migrations/0003_button_key_rename_to_accesskey.py
deleted file mode 100644 (file)
index 7a0961d..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-# encoding: utf-8
-import datetime
-from south.db import db
-from south.v2 import SchemaMigration
-from django.db import models
-
-class Migration(SchemaMigration):
-
-    def forwards(self, orm):
-
-        # Deleting field 'Button.key'
-        db.rename_column('toolbar_button', 'key', 'accesskey')
-
-
-
-    def backwards(self, orm):
-        db.rename_column('toolbar_button', 'accesskey', 'key')
-
-
-    models = {
-        'toolbar.button': {
-            'Meta': {'object_name': 'Button'},
-            'accesskey': ('django.db.models.fields.CharField', [], {'max_length': '1', 'null': 'True'}),
-            'group': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['toolbar.ButtonGroup']"}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'label': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
-            'link': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
-            'params': ('django.db.models.fields.TextField', [], {'default': "'[]'"}),
-            'scriptlet': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['toolbar.Scriptlet']", 'null': 'True', 'blank': 'True'}),
-            'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50', 'db_index': 'True'}),
-            'tooltip': ('django.db.models.fields.CharField', [], {'max_length': '120', 'blank': 'True'})
-        },
-        'toolbar.buttongroup': {
-            'Meta': {'object_name': 'ButtonGroup'},
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
-            'position': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'})
-        },
-        'toolbar.scriptlet': {
-            'Meta': {'object_name': 'Scriptlet'},
-            'code': ('django.db.models.fields.TextField', [], {}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '64', 'primary_key': 'True'})
-        }
-    }
-
-    complete_apps = ['toolbar']
diff --git a/apps/toolbar/migrations/0004_auto__chg_field_button_accesskey.py b/apps/toolbar/migrations/0004_auto__chg_field_button_accesskey.py
deleted file mode 100644 (file)
index 5f28bf5..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-# encoding: utf-8
-import datetime
-from south.db import db
-from south.v2 import SchemaMigration
-from django.db import models
-
-class Migration(SchemaMigration):
-
-    def forwards(self, orm):
-        
-        # Changing field 'Button.accesskey'
-        db.alter_column('toolbar_button', 'accesskey', self.gf('django.db.models.fields.CharField')(max_length=1))
-
-
-    def backwards(self, orm):
-        
-        # Changing field 'Button.accesskey'
-        db.alter_column('toolbar_button', 'accesskey', self.gf('django.db.models.fields.CharField')(max_length=1, null=True))
-
-
-    models = {
-        'toolbar.button': {
-            'Meta': {'ordering': "('slug',)", 'object_name': 'Button'},
-            'accesskey': ('django.db.models.fields.CharField', [], {'max_length': '1', 'blank': 'True'}),
-            'group': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['toolbar.ButtonGroup']", 'symmetrical': 'False'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'label': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
-            'link': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
-            'params': ('django.db.models.fields.TextField', [], {'default': "'[]'"}),
-            'scriptlet': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['toolbar.Scriptlet']", 'null': 'True', 'blank': 'True'}),
-            'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50', 'db_index': 'True'}),
-            'tooltip': ('django.db.models.fields.CharField', [], {'max_length': '120', 'blank': 'True'})
-        },
-        'toolbar.buttongroup': {
-            'Meta': {'ordering': "('position', 'name')", 'object_name': 'ButtonGroup'},
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
-            'position': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'})
-        },
-        'toolbar.scriptlet': {
-            'Meta': {'object_name': 'Scriptlet'},
-            'code': ('django.db.models.fields.TextField', [], {}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '64', 'primary_key': 'True'})
-        }
-    }
-
-    complete_apps = ['toolbar']
diff --git a/apps/toolbar/migrations/0005_initial_data.py b/apps/toolbar/migrations/0005_initial_data.py
deleted file mode 100644 (file)
index b31f380..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-# encoding: utf-8
-import datetime
-from south.db import db
-from south.v2 import DataMigration
-from django.db import models
-
-class Migration(DataMigration):
-
-    def forwards(self, orm):
-        from django.core.management import call_command
-        call_command("loaddata", "initial_toolbar.yaml")
-
-
-    def backwards(self, orm):
-        "Write your backwards methods here."
-        pass
-
-
-    models = {
-        'toolbar.button': {
-            'Meta': {'ordering': "('slug',)", 'object_name': 'Button'},
-            'accesskey': ('django.db.models.fields.CharField', [], {'max_length': '1', 'blank': 'True'}),
-            'group': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['toolbar.ButtonGroup']", 'symmetrical': 'False'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'label': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
-            'link': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
-            'params': ('django.db.models.fields.TextField', [], {'default': "'[]'"}),
-            'scriptlet': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['toolbar.Scriptlet']", 'null': 'True', 'blank': 'True'}),
-            'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50', 'db_index': 'True'}),
-            'tooltip': ('django.db.models.fields.CharField', [], {'max_length': '120', 'blank': 'True'})
-        },
-        'toolbar.buttongroup': {
-            'Meta': {'ordering': "('position', 'name')", 'object_name': 'ButtonGroup'},
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
-            'position': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'})
-        },
-        'toolbar.scriptlet': {
-            'Meta': {'object_name': 'Scriptlet'},
-            'code': ('django.db.models.fields.TextField', [], {}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '64', 'primary_key': 'True'})
-        }
-    }
-
-    complete_apps = ['toolbar']
diff --git a/apps/toolbar/migrations/__init__.py b/apps/toolbar/migrations/__init__.py
deleted file mode 100644 (file)
index 9012566..0000000
+++ /dev/null
@@ -1 +0,0 @@
-# pragma: no cover
diff --git a/apps/toolbar/models.py b/apps/toolbar/models.py
deleted file mode 100644 (file)
index a23e346..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# This file is part of FNP-Redakcja, 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 _
-
-
-class ButtonGroup(models.Model):
-    name = models.CharField(max_length=32)
-    slug = models.SlugField()
-    position = models.IntegerField(default=0)
-
-    class Meta:
-        ordering = ('position', 'name',)
-        verbose_name, verbose_name_plural = _('button group'), _('button groups')
-
-    def __unicode__(self):
-        return self.name
-
-    def to_dict(self, with_buttons=False):
-        d = {'name': self.name, 'position': self.position}
-
-        if with_buttons:
-            d['buttons'] = [b.to_dict() for b in self.button_set.all()]
-
-        return d
-
-
-class Button(models.Model):
-    label = models.CharField(max_length=32)
-    slug = models.SlugField(unique=True)  # unused
-
-    # behaviour
-    params = models.TextField(default='[]')  # TODO: should be a JSON field
-    scriptlet = models.ForeignKey('Scriptlet', null=True, blank=True)
-    link = models.CharField(max_length=256, blank=True, default='')
-
-    # ui related stuff
-    accesskey = models.CharField(blank=True, max_length=1)
-
-    tooltip = models.CharField(blank=True, max_length=120)
-
-    # Why the button is restricted to have the same position in each group ?
-    # position = models.IntegerField(default=0)
-    group = models.ManyToManyField(ButtonGroup)
-
-    class Meta:
-        ordering = ('slug',)
-        verbose_name, verbose_name_plural = _('button'), _('buttons')
-
-    @property
-    def full_tooltip(self):
-        return u"%s %s" % (self.tooltip, "[Alt+%s]" % self.accesskey if self.accesskey else "")
-
-    def to_dict(self):
-        return {
-            'label': self.label,
-            'tooltip': self.tooltip,
-            'accesskey': self.accesskey,
-            'params': self.params,
-            'scriptlet_id': self.scriptlet_id,
-        }
-
-    def __unicode__(self):
-        return self.label
-
-
-class Scriptlet(models.Model):
-    name = models.CharField(max_length=64, primary_key=True)
-    code = models.TextField()
-
-    def __unicode__(self):
-        return _(u'javascript') + u':' + self.name
diff --git a/apps/toolbar/templates/toolbar/button.html b/apps/toolbar/templates/toolbar/button.html
deleted file mode 100644 (file)
index cdffcaf..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-{% if button.link %}
-<a href="{{button.link}}" target="_blank">
-{% endif %}
-<button type="button"
-       data-ui-action="{{ button.scriptlet_id }}"
-       data-ui-action-params="{{ button.params|escape }}"
-       data-ui-accesskey="{{ button.accesskey }}"
-       {% if button.tooltip %}title="{{ button.full_tooltip }}"{% endif %} >
-       {{ button.label|safe }}
-</button>
-{% if button.link %}
-</a>
-{% endif %}
\ No newline at end of file
diff --git a/apps/toolbar/templates/toolbar/toolbar.html b/apps/toolbar/templates/toolbar/toolbar.html
deleted file mode 100644 (file)
index b9624d6..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-{% load toolbar_tags %}
-<div class="toolbar">
-    <select class="group_selector">
-        {% for group in toolbar_groups %}
-        <option value="{{ group.slug }}" {% if forloop.first %}selected="selected"{% endif %}>{{ group.name }}</option>
-        {% endfor %}
-    </select>
-
-       <button type="button" class="prev">&lt;</button>
-    <div class="button_group_container">
-        {% for group in toolbar_groups %}
-        <ul data-group="{{ group.slug }}" class="button_group">
-            {# buttons for this group #}
-            {% for button in group.button_set.all %}
-               <li class="toolbar-button">{% toolbar_button button %}</li>
-            {% endfor %}
-        </ul>
-        {% endfor %}
-    </div>
-    <button type="button" class="next">&gt;</button>
-</div>
diff --git a/apps/toolbar/templatetags/__init__.py b/apps/toolbar/templatetags/__init__.py
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/apps/toolbar/templatetags/toolbar_tags.py b/apps/toolbar/templatetags/toolbar_tags.py
deleted file mode 100644 (file)
index 0766677..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
-# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
-#
-from django import template
-from toolbar import models
-
-register = template.Library()
-
-
-@register.inclusion_tag('toolbar/toolbar.html')
-def toolbar():
-    return {'toolbar_groups': models.ButtonGroup.objects.all().select_related()}
-
-
-@register.inclusion_tag('toolbar/button.html')
-def toolbar_button(b):
-    return {'button': b}
diff --git a/apps/wiki/__init__.py b/apps/wiki/__init__.py
deleted file mode 100644 (file)
index c53f0e7..0000000
+++ /dev/null
@@ -1 +0,0 @@
-  # pragma: no cover
diff --git a/apps/wiki/admin.py b/apps/wiki/admin.py
deleted file mode 100644 (file)
index ae309a9..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-from django.contrib import admin
-
-from wiki import models
-
-
-class ThemeAdmin(admin.ModelAdmin):
-    search_fields = ['name']
-
-admin.site.register(models.Theme, ThemeAdmin)
diff --git a/apps/wiki/fixtures/initial_themes.yaml b/apps/wiki/fixtures/initial_themes.yaml
deleted file mode 100644 (file)
index fc0b773..0000000
+++ /dev/null
@@ -1,1246 +0,0 @@
-- fields: {name: Alkohol}
-  model: wiki.theme
-  pk: 1
-- fields: {name: Ambicja}
-  model: wiki.theme
-  pk: 2
-- fields: {name: "Anio\u0142"}
-  model: wiki.theme
-  pk: 3
-- fields: {name: Antysemityzm}
-  model: wiki.theme
-  pk: 4
-- fields: {name: Arkadia}
-  model: wiki.theme
-  pk: 5
-- fields: {name: Artysta}
-  model: wiki.theme
-  pk: 6
-- fields: {name: "Bezdomno\u015B\u0107"}
-  model: wiki.theme
-  pk: 7
-- fields: {name: "Bezpiecze\u0144stwo"}
-  model: wiki.theme
-  pk: 8
-- fields: {name: Bieda}
-  model: wiki.theme
-  pk: 9
-- fields: {name: Bijatyka}
-  model: wiki.theme
-  pk: 10
-- fields: {name: Bogactwo}
-  model: wiki.theme
-  pk: 14
-- fields: {name: Brat}
-  model: wiki.theme
-  pk: 16
-- fields: {name: Bunt}
-  model: wiki.theme
-  pk: 17
-- fields: {name: Buntownik}
-  model: wiki.theme
-  pk: 18
-- fields: {name: Burza}
-  model: wiki.theme
-  pk: 19
-- fields: {name: "B\xF3g"}
-  model: wiki.theme
-  pk: 15
-- fields: {name: "B\u0142azen"}
-  model: wiki.theme
-  pk: 11
-- fields: {name: "B\u0142oto"}
-  model: wiki.theme
-  pk: 13
-- fields: {name: "B\u0142\u0105dzenie"}
-  model: wiki.theme
-  pk: 12
-- fields: {name: Car}
-  model: wiki.theme
-  pk: 20
-- fields: {name: Carpe diem}
-  model: wiki.theme
-  pk: 21
-- fields: {name: "Chciwo\u015B\u0107"}
-  model: wiki.theme
-  pk: 25
-- fields: {name: Chleb}
-  model: wiki.theme
-  pk: 26
-- fields: {name: Choroba}
-  model: wiki.theme
-  pk: 28
-- fields: {name: Chrystus}
-  model: wiki.theme
-  pk: 29
-- fields: {name: Chrzest}
-  model: wiki.theme
-  pk: 30
-- fields: {name: "Ch\u0142op"}
-  model: wiki.theme
-  pk: 27
-- fields: {name: "Cia\u0142o"}
-  model: wiki.theme
-  pk: 31
-- fields: {name: "Ciemno\u015B\u0107"}
-  model: wiki.theme
-  pk: 22
-- fields: {name: Cierpienie}
-  model: wiki.theme
-  pk: 32
-- fields: {name: "Cie\u0144"}
-  model: wiki.theme
-  pk: 23
-- fields: {name: Cisza}
-  model: wiki.theme
-  pk: 24
-- fields: {name: Cmentarz}
-  model: wiki.theme
-  pk: 33
-- fields: {name: Cnota}
-  model: wiki.theme
-  pk: 34
-- fields: {name: Cud}
-  model: wiki.theme
-  pk: 36
-- fields: {name: Czarownica}
-  model: wiki.theme
-  pk: 37
-- fields: {name: Czary}
-  model: wiki.theme
-  pk: 38
-- fields: {name: Czas}
-  model: wiki.theme
-  pk: 39
-- fields: {name: Czyn}
-  model: wiki.theme
-  pk: 40
-- fields: {name: "Czy\u015Bciec"}
-  model: wiki.theme
-  pk: 41
-- fields: {name: "C\xF3rka"}
-  model: wiki.theme
-  pk: 35
-- fields: {name: Dama}
-  model: wiki.theme
-  pk: 42
-- fields: {name: Danse macabre}
-  model: wiki.theme
-  pk: 43
-- fields: {name: Deszcz}
-  model: wiki.theme
-  pk: 44
-- fields: {name: "Diabe\u0142"}
-  model: wiki.theme
-  pk: 45
-- fields: {name: Dobro}
-  model: wiki.theme
-  pk: 46
-- fields: {name: Dom}
-  model: wiki.theme
-  pk: 47
-- fields: {name: "Doros\u0142o\u015B\u0107"}
-  model: wiki.theme
-  pk: 48
-- fields: {name: Drzewo}
-  model: wiki.theme
-  pk: 49
-- fields: {name: Duch}
-  model: wiki.theme
-  pk: 50
-- fields: {name: Duma}
-  model: wiki.theme
-  pk: 52
-- fields: {name: Dusza}
-  model: wiki.theme
-  pk: 51
-- fields: {name: Dworek}
-  model: wiki.theme
-  pk: 53
-- fields: {name: Dworzanin}
-  model: wiki.theme
-  pk: 54
-- fields: {name: "Dw\xF3r"}
-  model: wiki.theme
-  pk: 55
-- fields: {name: "Dzieci\u0144stwo"}
-  model: wiki.theme
-  pk: 56
-- fields: {name: Dziecko}
-  model: wiki.theme
-  pk: 57
-- fields: {name: Dziedzictwo}
-  model: wiki.theme
-  pk: 58
-- fields: {name: Dziewictwo}
-  model: wiki.theme
-  pk: 59
-- fields: {name: "D\u017Awi\u0119k"}
-  model: wiki.theme
-  pk: 60
-- fields: {name: Egzorcyzm}
-  model: wiki.theme
-  pk: 61
-- fields: {name: Elita}
-  model: wiki.theme
-  pk: 62
-- fields: {name: Emigrant}
-  model: wiki.theme
-  pk: 63
-- fields: {name: "Fa\u0142sz"}
-  model: wiki.theme
-  pk: 64
-- fields: {name: Filozof}
-  model: wiki.theme
-  pk: 65
-- fields: {name: Fircyk}
-  model: wiki.theme
-  pk: 66
-- fields: {name: Flirt}
-  model: wiki.theme
-  pk: 67
-- fields: {name: Gospodarz}
-  model: wiki.theme
-  pk: 71
-- fields: {name: Gospodyni}
-  model: wiki.theme
-  pk: 72
-- fields: {name: Gotycyzm}
-  model: wiki.theme
-  pk: 74
-- fields: {name: "Go\u015B\u0107"}
-  model: wiki.theme
-  pk: 73
-- fields: {name: Gra}
-  model: wiki.theme
-  pk: 76
-- fields: {name: Grzech}
-  model: wiki.theme
-  pk: 78
-- fields: {name: "Grzeczno\u015B\u0107"}
-  model: wiki.theme
-  pk: 79
-- fields: {name: "Gr\xF3b"}
-  model: wiki.theme
-  pk: 77
-- fields: {name: Gwiazda}
-  model: wiki.theme
-  pk: 80
-- fields: {name: "G\xF3ra"}
-  model: wiki.theme
-  pk: 75
-- fields: {name: "G\u0142upiec"}
-  model: wiki.theme
-  pk: 68
-- fields: {name: "G\u0142upota"}
-  model: wiki.theme
-  pk: 69
-- fields: {name: "G\u0142\xF3d"}
-  model: wiki.theme
-  pk: 70
-- fields: {name: Handel}
-  model: wiki.theme
-  pk: 81
-- fields: {name: "Ha\u0144ba"}
-  model: wiki.theme
-  pk: 82
-- fields: {name: Historia}
-  model: wiki.theme
-  pk: 83
-- fields: {name: Honor}
-  model: wiki.theme
-  pk: 84
-- fields: {name: Idealista}
-  model: wiki.theme
-  pk: 85
-- fields: {name: "Imi\u0119"}
-  model: wiki.theme
-  pk: 86
-- fields: {name: Interes}
-  model: wiki.theme
-  pk: 87
-- fields: {name: "Jab\u0142ka"}
-  model: wiki.theme
-  pk: 88
-- fields: {name: Jedzenie}
-  model: wiki.theme
-  pk: 89
-- fields: {name: "Jesie\u0144"}
-  model: wiki.theme
-  pk: 90
-- fields: {name: Kaleka}
-  model: wiki.theme
-  pk: 91
-- fields: {name: Kara}
-  model: wiki.theme
-  pk: 92
-- fields: {name: Karczma}
-  model: wiki.theme
-  pk: 93
-- fields: {name: "Kl\u0119ska"}
-  model: wiki.theme
-  pk: 94
-- fields: {name: Kobieta}
-  model: wiki.theme
-  pk: 97
-- fields: {name: "Kobieta \"upad\u0142a\""}
-  model: wiki.theme
-  pk: 99
-- fields: {name: Kobieta demoniczna}
-  model: wiki.theme
-  pk: 98
-- fields: {name: Kochanek}
-  model: wiki.theme
-  pk: 100
-- fields: {name: Kochanek romantyczny}
-  model: wiki.theme
-  pk: 101
-- fields: {name: Kolonializm}
-  model: wiki.theme
-  pk: 102
-- fields: {name: Kondycja ludzka}
-  model: wiki.theme
-  pk: 103
-- fields: {name: Konflikt}
-  model: wiki.theme
-  pk: 104
-- fields: {name: "Konflikt wewn\u0119trzny"}
-  model: wiki.theme
-  pk: 105
-- fields: {name: "Koniec \u015Bwiata"}
-  model: wiki.theme
-  pk: 106
-- fields: {name: "Korzy\u015B\u0107"}
-  model: wiki.theme
-  pk: 108
-- fields: {name: Kot}
-  model: wiki.theme
-  pk: 109
-- fields: {name: "Ko\u0144"}
-  model: wiki.theme
-  pk: 107
-- fields: {name: "Kradzie\u017C"}
-  model: wiki.theme
-  pk: 110
-- fields: {name: Krew}
-  model: wiki.theme
-  pk: 111
-- fields: {name: Krzywda}
-  model: wiki.theme
-  pk: 113
-- fields: {name: "Kr\xF3l"}
-  model: wiki.theme
-  pk: 112
-- fields: {name: "Ksi\u0105dz"}
-  model: wiki.theme
-  pk: 114
-- fields: {name: "Ksi\u0105\u017Cka"}
-  model: wiki.theme
-  pk: 115
-- fields: {name: "Ksi\u0119\u017Cyc"}
-  model: wiki.theme
-  pk: 116
-- fields: {name: Kuchnia}
-  model: wiki.theme
-  pk: 117
-- fields: {name: Kuszenie}
-  model: wiki.theme
-  pk: 118
-- fields: {name: Kwiaty}
-  model: wiki.theme
-  pk: 119
-- fields: {name: "K\u0142amstwo"}
-  model: wiki.theme
-  pk: 95
-- fields: {name: "K\u0142\xF3tnia"}
-  model: wiki.theme
-  pk: 96
-- fields: {name: Labirynt}
-  model: wiki.theme
-  pk: 120
-- fields: {name: Las}
-  model: wiki.theme
-  pk: 121
-- fields: {name: Lato}
-  model: wiki.theme
-  pk: 122
-- fields: {name: Lekarz}
-  model: wiki.theme
-  pk: 123
-- fields: {name: Lenistwo}
-  model: wiki.theme
-  pk: 124
-- fields: {name: Liberat}
-  model: wiki.theme
-  pk: 126
-- fields: {name: List}
-  model: wiki.theme
-  pk: 125
-- fields: {name: Los}
-  model: wiki.theme
-  pk: 127
-- fields: {name: Lud}
-  model: wiki.theme
-  pk: 128
-- fields: {name: Lustro}
-  model: wiki.theme
-  pk: 129
-- fields: {name: Marzenie}
-  model: wiki.theme
-  pk: 132
-- fields: {name: Maska}
-  model: wiki.theme
-  pk: 133
-- fields: {name: Maszyna}
-  model: wiki.theme
-  pk: 134
-- fields: {name: Matka}
-  model: wiki.theme
-  pk: 135
-- fields: {name: Matka Boska}
-  model: wiki.theme
-  pk: 136
-- fields: {name: "Ma\u0142\u017Ce\u0144stwo"}
-  model: wiki.theme
-  pk: 131
-- fields: {name: Melancholia}
-  model: wiki.theme
-  pk: 139
-- fields: {name: Miasto}
-  model: wiki.theme
-  pk: 142
-- fields: {name: Mieszczanin}
-  model: wiki.theme
-  pk: 143
-- fields: {name: Mizoginia}
-  model: wiki.theme
-  pk: 152
-- fields: {name: "Mi\u0142osierdzie"}
-  model: wiki.theme
-  pk: 144
-- fields: {name: "Mi\u0142o\u015B\u0107"}
-  model: wiki.theme
-  pk: 145
-- fields: {name: "Mi\u0142o\u015B\u0107 niespe\u0142niona"}
-  model: wiki.theme
-  pk: 146
-- fields: {name: "Mi\u0142o\u015B\u0107 platoniczna"}
-  model: wiki.theme
-  pk: 147
-- fields: {name: "Mi\u0142o\u015B\u0107 romantyczna"}
-  model: wiki.theme
-  pk: 148
-- fields: {name: "Mi\u0142o\u015B\u0107 silniejsza ni\u017C \u015Bmier\u0107"}
-  model: wiki.theme
-  pk: 149
-- fields: {name: "Mi\u0142o\u015B\u0107 spe\u0142niona"}
-  model: wiki.theme
-  pk: 150
-- fields: {name: "Mi\u0142o\u015B\u0107 tragiczna"}
-  model: wiki.theme
-  pk: 151
-- fields: {name: Moda}
-  model: wiki.theme
-  pk: 154
-- fields: {name: Modlitwa}
-  model: wiki.theme
-  pk: 155
-- fields: {name: Morderstwo}
-  model: wiki.theme
-  pk: 156
-- fields: {name: Morze}
-  model: wiki.theme
-  pk: 157
-- fields: {name: Motyl}
-  model: wiki.theme
-  pk: 158
-- fields: {name: Mucha}
-  model: wiki.theme
-  pk: 159
-- fields: {name: Muzyka}
-  model: wiki.theme
-  pk: 160
-- fields: {name: "M\u0105dro\u015B\u0107"}
-  model: wiki.theme
-  pk: 137
-- fields: {name: "M\u0105\u017C"}
-  model: wiki.theme
-  pk: 138
-- fields: {name: "M\u0119drzec"}
-  model: wiki.theme
-  pk: 140
-- fields: {name: "M\u0119\u017Cczyzna"}
-  model: wiki.theme
-  pk: 141
-- fields: {name: "M\u0142odo\u015B\u0107"}
-  model: wiki.theme
-  pk: 153
-- fields: {name: Narodziny}
-  model: wiki.theme
-  pk: 161
-- fields: {name: "Nar\xF3d"}
-  model: wiki.theme
-  pk: 162
-- fields: {name: Natura}
-  model: wiki.theme
-  pk: 163
-- fields: {name: Nauczyciel}
-  model: wiki.theme
-  pk: 164
-- fields: {name: Nauczycielka}
-  model: wiki.theme
-  pk: 165
-- fields: {name: Nauka}
-  model: wiki.theme
-  pk: 166
-- fields: {name: "Niebezpiecze\u0144stwo"}
-  model: wiki.theme
-  pk: 167
-- fields: {name: Niedziela}
-  model: wiki.theme
-  pk: 168
-- fields: {name: Niemiec}
-  model: wiki.theme
-  pk: 169
-- fields: {name: "Nienawi\u015B\u0107"}
-  model: wiki.theme
-  pk: 170
-- fields: {name: Niewola}
-  model: wiki.theme
-  pk: 172
-- fields: {name: "Nie\u015Bmiertelno\u015B\u0107"}
-  model: wiki.theme
-  pk: 171
-- fields: {name: Noc}
-  model: wiki.theme
-  pk: 173
-- fields: {name: Nuda}
-  model: wiki.theme
-  pk: 174
-- fields: {name: Obcy}
-  model: wiki.theme
-  pk: 175
-- fields: {name: "Obowi\u0105zek"}
-  model: wiki.theme
-  pk: 177
-- fields: {name: "Obraz \u015Bwiata"}
-  model: wiki.theme
-  pk: 178
-- fields: {name: "Obrz\u0119dy"}
-  model: wiki.theme
-  pk: 179
-- fields: {name: Obyczaje}
-  model: wiki.theme
-  pk: 180
-- fields: {name: Obywatel}
-  model: wiki.theme
-  pk: 181
-- fields: {name: "Ob\u0142ok"}
-  model: wiki.theme
-  pk: 176
-- fields: {name: "Odrodzenie przez gr\xF3b"}
-  model: wiki.theme
-  pk: 182
-- fields: {name: Odwaga}
-  model: wiki.theme
-  pk: 183
-- fields: {name: Ofiara}
-  model: wiki.theme
-  pk: 184
-- fields: {name: "Ogie\u0144"}
-  model: wiki.theme
-  pk: 185
-- fields: {name: "Ogr\xF3d"}
-  model: wiki.theme
-  pk: 186
-- fields: {name: Ojciec}
-  model: wiki.theme
-  pk: 187
-- fields: {name: Ojczyzna}
-  model: wiki.theme
-  pk: 188
-- fields: {name: Oko}
-  model: wiki.theme
-  pk: 189
-- fields: {name: "Okrucie\u0144stwo"}
-  model: wiki.theme
-  pk: 191
-- fields: {name: "Okr\u0119t"}
-  model: wiki.theme
-  pk: 190
-- fields: {name: Omen}
-  model: wiki.theme
-  pk: 192
-- fields: {name: Opieka}
-  model: wiki.theme
-  pk: 193
-- fields: {name: Organizm}
-  model: wiki.theme
-  pk: 194
-- fields: {name: "Otch\u0142a\u0144"}
-  model: wiki.theme
-  pk: 195
-- fields: {name: "O\u015Bwiadczyny"}
-  model: wiki.theme
-  pk: 415
-- fields: {name: "Paj\u0105k"}
-  model: wiki.theme
-  pk: 196
-- fields: {name: "Pami\u0119\u0107"}
-  model: wiki.theme
-  pk: 197
-- fields: {name: Pan}
-  model: wiki.theme
-  pk: 198
-- fields: {name: "Panna m\u0142oda"}
-  model: wiki.theme
-  pk: 199
-- fields: {name: Patriota}
-  model: wiki.theme
-  pk: 201
-- fields: {name: "Pa\u0144stwo"}
-  model: wiki.theme
-  pk: 200
-- fields: {name: "Piek\u0142o"}
-  model: wiki.theme
-  pk: 202
-- fields: {name: Pielgrzym}
-  model: wiki.theme
-  pk: 203
-- fields: {name: "Pieni\u0105dz"}
-  model: wiki.theme
-  pk: 204
-- fields: {name: Pies}
-  model: wiki.theme
-  pk: 205
-- fields: {name: "Pija\u0144stwo"}
-  model: wiki.theme
-  pk: 207
-- fields: {name: Piwnica}
-  model: wiki.theme
-  pk: 208
-- fields: {name: "Pi\u0119tno"}
-  model: wiki.theme
-  pk: 206
-- fields: {name: Plotka}
-  model: wiki.theme
-  pk: 209
-- fields: {name: "Pobo\u017Cno\u015B\u0107"}
-  model: wiki.theme
-  pk: 210
-- fields: {name: "Poca\u0142unek"}
-  model: wiki.theme
-  pk: 211
-- fields: {name: Pochlebstwo}
-  model: wiki.theme
-  pk: 212
-- fields: {name: "Podr\xF3\u017C"}
-  model: wiki.theme
-  pk: 216
-- fields: {name: "Podst\u0119p"}
-  model: wiki.theme
-  pk: 217
-- fields: {name: Poeta}
-  model: wiki.theme
-  pk: 213
-- fields: {name: Poetka}
-  model: wiki.theme
-  pk: 214
-- fields: {name: Poezja}
-  model: wiki.theme
-  pk: 215
-- fields: {name: Pogrzeb}
-  model: wiki.theme
-  pk: 218
-- fields: {name: Pojedynek}
-  model: wiki.theme
-  pk: 219
-- fields: {name: Pokora}
-  model: wiki.theme
-  pk: 220
-- fields: {name: Pokusa}
-  model: wiki.theme
-  pk: 221
-- fields: {name: Polak}
-  model: wiki.theme
-  pk: 222
-- fields: {name: Polityka}
-  model: wiki.theme
-  pk: 223
-- fields: {name: Polowanie}
-  model: wiki.theme
-  pk: 224
-- fields: {name: Polska}
-  model: wiki.theme
-  pk: 225
-- fields: {name: Portret}
-  model: wiki.theme
-  pk: 226
-- fields: {name: Porwanie}
-  model: wiki.theme
-  pk: 227
-- fields: {name: "Potw\xF3r"}
-  model: wiki.theme
-  pk: 229
-- fields: {name: Powstanie}
-  model: wiki.theme
-  pk: 230
-- fields: {name: Powstaniec}
-  model: wiki.theme
-  pk: 231
-- fields: {name: Pozory}
-  model: wiki.theme
-  pk: 232
-- fields: {name: "Pozycja spo\u0142eczna"}
-  model: wiki.theme
-  pk: 233
-- fields: {name: "Po\u015Bwi\u0119cenie"}
-  model: wiki.theme
-  pk: 228
-- fields: {name: "Po\u017Car"}
-  model: wiki.theme
-  pk: 234
-- fields: {name: "Po\u017C\u0105danie"}
-  model: wiki.theme
-  pk: 235
-- fields: {name: Praca}
-  model: wiki.theme
-  pk: 236
-- fields: {name: Praca organiczna}
-  model: wiki.theme
-  pk: 238
-- fields: {name: Praca u podstaw}
-  model: wiki.theme
-  pk: 237
-- fields: {name: Prawda}
-  model: wiki.theme
-  pk: 239
-- fields: {name: Prawnik}
-  model: wiki.theme
-  pk: 240
-- fields: {name: Prometeusz}
-  model: wiki.theme
-  pk: 241
-- fields: {name: Proroctwo}
-  model: wiki.theme
-  pk: 242
-- fields: {name: Prorok}
-  model: wiki.theme
-  pk: 243
-- fields: {name: Przebranie}
-  model: wiki.theme
-  pk: 245
-- fields: {name: Przeczucie}
-  model: wiki.theme
-  pk: 246
-- fields: {name: "Przedmurze chrze\u015Bcija\u0144stwa"}
-  model: wiki.theme
-  pk: 247
-- fields: {name: "Przekle\u0144stwo"}
-  model: wiki.theme
-  pk: 248
-- fields: {name: Przekupstwo}
-  model: wiki.theme
-  pk: 249
-- fields: {name: Przemiana}
-  model: wiki.theme
-  pk: 250
-- fields: {name: Przemijanie}
-  model: wiki.theme
-  pk: 251
-- fields: {name: Przemoc}
-  model: wiki.theme
-  pk: 252
-- fields: {name: "Przestrze\u0144"}
-  model: wiki.theme
-  pk: 253
-- fields: {name: "Przyja\u017A\u0144"}
-  model: wiki.theme
-  pk: 254
-- fields: {name: "Przyroda nieo\u017Cywiona"}
-  model: wiki.theme
-  pk: 255
-- fields: {name: "Przysi\u0119ga"}
-  model: wiki.theme
-  pk: 256
-- fields: {name: "Przyw\xF3dca"}
-  model: wiki.theme
-  pk: 257
-- fields: {name: "Pr\xF3\u017Cno\u015B\u0107"}
-  model: wiki.theme
-  pk: 244
-- fields: {name: Ptak}
-  model: wiki.theme
-  pk: 258
-- fields: {name: Pustynia}
-  model: wiki.theme
-  pk: 259
-- fields: {name: Pycha}
-  model: wiki.theme
-  pk: 260
-- fields: {name: Raj}
-  model: wiki.theme
-  pk: 261
-- fields: {name: Realista}
-  model: wiki.theme
-  pk: 262
-- fields: {name: Religia}
-  model: wiki.theme
-  pk: 263
-- fields: {name: Rewolucja}
-  model: wiki.theme
-  pk: 264
-- fields: {name: Robak}
-  model: wiki.theme
-  pk: 265
-- fields: {name: Robotnik}
-  model: wiki.theme
-  pk: 266
-- fields: {name: Rodzina}
-  model: wiki.theme
-  pk: 267
-- fields: {name: Rosja}
-  model: wiki.theme
-  pk: 268
-- fields: {name: Rosjanin}
-  model: wiki.theme
-  pk: 269
-- fields: {name: Rozczarowanie}
-  model: wiki.theme
-  pk: 271
-- fields: {name: Rozpacz}
-  model: wiki.theme
-  pk: 272
-- fields: {name: Rozstanie}
-  model: wiki.theme
-  pk: 273
-- fields: {name: Rozum}
-  model: wiki.theme
-  pk: 274
-- fields: {name: "Ro\u015Bliny"}
-  model: wiki.theme
-  pk: 270
-- fields: {name: Ruiny}
-  model: wiki.theme
-  pk: 275
-- fields: {name: Rycerz}
-  model: wiki.theme
-  pk: 276
-- fields: {name: Rzeka}
-  model: wiki.theme
-  pk: 277
-- fields: {name: Salon}
-  model: wiki.theme
-  pk: 278
-- fields: {name: "Samob\xF3jstwo"}
-  model: wiki.theme
-  pk: 279
-- fields: {name: Samolubstwo}
-  model: wiki.theme
-  pk: 280
-- fields: {name: Samotnik}
-  model: wiki.theme
-  pk: 281
-- fields: {name: "Samotno\u015B\u0107"}
-  model: wiki.theme
-  pk: 282
-- fields: {name: Sarmata}
-  model: wiki.theme
-  pk: 283
-- fields: {name: Sen}
-  model: wiki.theme
-  pk: 287
-- fields: {name: Serce}
-  model: wiki.theme
-  pk: 288
-- fields: {name: Sielanka}
-  model: wiki.theme
-  pk: 290
-- fields: {name: Sierota}
-  model: wiki.theme
-  pk: 291
-- fields: {name: Siostra}
-  model: wiki.theme
-  pk: 293
-- fields: {name: "Si\u0142a"}
-  model: wiki.theme
-  pk: 292
-- fields: {name: "Sk\u0105piec"}
-  model: wiki.theme
-  pk: 299
-- fields: {name: "Sobowt\xF3r"}
-  model: wiki.theme
-  pk: 300
-- fields: {name: "Spowied\u017A"}
-  model: wiki.theme
-  pk: 302
-- fields: {name: "Spo\u0142ecznik"}
-  model: wiki.theme
-  pk: 301
-- fields: {name: "Sprawiedliwo\u015B\u0107"}
-  model: wiki.theme
-  pk: 303
-- fields: {name: "Staro\u015B\u0107"}
-  model: wiki.theme
-  pk: 304
-- fields: {name: Strach}
-  model: wiki.theme
-  pk: 305
-- fields: {name: "Str\xF3j"}
-  model: wiki.theme
-  pk: 306
-- fields: {name: Stworzenie}
-  model: wiki.theme
-  pk: 307
-- fields: {name: Sumienie}
-  model: wiki.theme
-  pk: 308
-- fields: {name: Swaty}
-  model: wiki.theme
-  pk: 309
-- fields: {name: Syberia}
-  model: wiki.theme
-  pk: 310
-- fields: {name: Syn}
-  model: wiki.theme
-  pk: 311
-- fields: {name: Syn marnotrawny}
-  model: wiki.theme
-  pk: 312
-- fields: {name: Syzyf}
-  model: wiki.theme
-  pk: 313
-- fields: {name: Szaleniec}
-  model: wiki.theme
-  pk: 314
-- fields: {name: "Szale\u0144stwo"}
-  model: wiki.theme
-  pk: 315
-- fields: {name: "Szanta\u017C"}
-  model: wiki.theme
-  pk: 316
-- fields: {name: Szatan}
-  model: wiki.theme
-  pk: 317
-- fields: {name: "Szcz\u0119\u015Bcie"}
-  model: wiki.theme
-  pk: 318
-- fields: {name: "Szko\u0142a"}
-  model: wiki.theme
-  pk: 319
-- fields: {name: Szlachcic}
-  model: wiki.theme
-  pk: 320
-- fields: {name: Szpieg}
-  model: wiki.theme
-  pk: 321
-- fields: {name: Sztuka}
-  model: wiki.theme
-  pk: 322
-- fields: {name: "S\u0105d"}
-  model: wiki.theme
-  pk: 285
-- fields: {name: "S\u0105d Ostateczny"}
-  model: wiki.theme
-  pk: 286
-- fields: {name: "S\u0105siad"}
-  model: wiki.theme
-  pk: 284
-- fields: {name: "S\u0119dzia"}
-  model: wiki.theme
-  pk: 289
-- fields: {name: "S\u0142awa"}
-  model: wiki.theme
-  pk: 294
-- fields: {name: "S\u0142owo"}
-  model: wiki.theme
-  pk: 296
-- fields: {name: "S\u0142o\u0144ce"}
-  model: wiki.theme
-  pk: 295
-- fields: {name: "S\u0142uga"}
-  model: wiki.theme
-  pk: 297
-- fields: {name: "S\u0142u\u017Calczo\u015B\u0107"}
-  model: wiki.theme
-  pk: 298
-- fields: {name: Tajemnica}
-  model: wiki.theme
-  pk: 332
-- fields: {name: Taniec}
-  model: wiki.theme
-  pk: 333
-- fields: {name: "Tch\xF3rzostwo"}
-  model: wiki.theme
-  pk: 334
-- fields: {name: Teatr}
-  model: wiki.theme
-  pk: 335
-- fields: {name: Testament}
-  model: wiki.theme
-  pk: 336
-- fields: {name: Theatrum mundi}
-  model: wiki.theme
-  pk: 338
-- fields: {name: Trucizna}
-  model: wiki.theme
-  pk: 340
-- fields: {name: Trup}
-  model: wiki.theme
-  pk: 341
-- fields: {name: "Tw\xF3rczo\u015B\u0107"}
-  model: wiki.theme
-  pk: 342
-- fields: {name: "T\u0119sknota"}
-  model: wiki.theme
-  pk: 337
-- fields: {name: "T\u0142um"}
-  model: wiki.theme
-  pk: 339
-- fields: {name: "Ucze\u0144"}
-  model: wiki.theme
-  pk: 343
-- fields: {name: Uczta}
-  model: wiki.theme
-  pk: 344
-- fields: {name: Umiarkowanie}
-  model: wiki.theme
-  pk: 346
-- fields: {name: Upadek}
-  model: wiki.theme
-  pk: 347
-- fields: {name: "Upi\xF3r"}
-  model: wiki.theme
-  pk: 348
-- fields: {name: Uroda}
-  model: wiki.theme
-  pk: 345
-- fields: {name: "Urz\u0119dnik"}
-  model: wiki.theme
-  pk: 349
-- fields: {name: Vanitas}
-  model: wiki.theme
-  pk: 350
-- fields: {name: Walka}
-  model: wiki.theme
-  pk: 351
-- fields: {name: Walka klas}
-  model: wiki.theme
-  pk: 352
-- fields: {name: Wampir}
-  model: wiki.theme
-  pk: 353
-- fields: {name: Warszawa}
-  model: wiki.theme
-  pk: 354
-- fields: {name: Wdowa}
-  model: wiki.theme
-  pk: 356
-- fields: {name: Wdowiec}
-  model: wiki.theme
-  pk: 357
-- fields: {name: Wesele}
-  model: wiki.theme
-  pk: 358
-- fields: {name: Wiatr}
-  model: wiki.theme
-  pk: 359
-- fields: {name: Wiedza}
-  model: wiki.theme
-  pk: 363
-- fields: {name: "Wierno\u015B\u0107"}
-  model: wiki.theme
-  pk: 360
-- fields: {name: Wierzenia}
-  model: wiki.theme
-  pk: 361
-- fields: {name: "Wie\u015B"}
-  model: wiki.theme
-  pk: 362
-- fields: {name: "Wie\u017Ca Babel"}
-  model: wiki.theme
-  pk: 364
-- fields: {name: Wina}
-  model: wiki.theme
-  pk: 367
-- fields: {name: Wino}
-  model: wiki.theme
-  pk: 368
-- fields: {name: Wiosna}
-  model: wiki.theme
-  pk: 369
-- fields: {name: Wizja}
-  model: wiki.theme
-  pk: 370
-- fields: {name: "Wi\u0119zienie"}
-  model: wiki.theme
-  pk: 365
-- fields: {name: "Wi\u0119zie\u0144"}
-  model: wiki.theme
-  pk: 366
-- fields: {name: Woda}
-  model: wiki.theme
-  pk: 373
-- fields: {name: Wojna}
-  model: wiki.theme
-  pk: 374
-- fields: {name: "Wojna pokole\u0144"}
-  model: wiki.theme
-  pk: 375
-- fields: {name: "Wolno\u015B\u0107"}
-  model: wiki.theme
-  pk: 376
-- fields: {name: "Wr\xF3g"}
-  model: wiki.theme
-  pk: 377
-- fields: {name: Wspomnienia}
-  model: wiki.theme
-  pk: 378
-- fields: {name: "Wsp\xF3\u0142praca"}
-  model: wiki.theme
-  pk: 379
-- fields: {name: Wygnanie}
-  model: wiki.theme
-  pk: 380
-- fields: {name: Wyrzuty sumienia}
-  model: wiki.theme
-  pk: 381
-- fields: {name: Wyspa}
-  model: wiki.theme
-  pk: 382
-- fields: {name: Wzrok}
-  model: wiki.theme
-  pk: 383
-- fields: {name: "W\u0105\u017C"}
-  model: wiki.theme
-  pk: 355
-- fields: {name: "W\u0142adza"}
-  model: wiki.theme
-  pk: 371
-- fields: {name: "W\u0142asno\u015B\u0107"}
-  model: wiki.theme
-  pk: 372
-- fields: {name: Zabawa}
-  model: wiki.theme
-  pk: 384
-- fields: {name: Zabobony}
-  model: wiki.theme
-  pk: 385
-- fields: {name: Zamek}
-  model: wiki.theme
-  pk: 386
-- fields: {name: "Zar\u0119czyny"}
-  model: wiki.theme
-  pk: 387
-- fields: {name: "Zazdro\u015B\u0107"}
-  model: wiki.theme
-  pk: 389
-- fields: {name: "Za\u015Bwiaty"}
-  model: wiki.theme
-  pk: 388
-- fields: {name: Zbawienie}
-  model: wiki.theme
-  pk: 390
-- fields: {name: Zbrodnia}
-  model: wiki.theme
-  pk: 391
-- fields: {name: Zbrodniarz}
-  model: wiki.theme
-  pk: 392
-- fields: {name: Zdrada}
-  model: wiki.theme
-  pk: 393
-- fields: {name: Zdrowie}
-  model: wiki.theme
-  pk: 394
-- fields: {name: Zemsta}
-  model: wiki.theme
-  pk: 395
-- fields: {name: "Zes\u0142aniec"}
-  model: wiki.theme
-  pk: 396
-- fields: {name: Ziarno}
-  model: wiki.theme
-  pk: 397
-- fields: {name: Ziemia}
-  model: wiki.theme
-  pk: 398
-- fields: {name: Zima}
-  model: wiki.theme
-  pk: 399
-- fields: {name: Zmartwychwstanie}
-  model: wiki.theme
-  pk: 403
-- fields: {name: "Zwierz\u0119ta"}
-  model: wiki.theme
-  pk: 405
-- fields: {name: "Zwyci\u0119stwo"}
-  model: wiki.theme
-  pk: 406
-- fields: {name: "Zw\u0105tpienie"}
-  model: wiki.theme
-  pk: 404
-- fields: {name: "Z\u0142o"}
-  model: wiki.theme
-  pk: 400
-- fields: {name: "Z\u0142odziej"}
-  model: wiki.theme
-  pk: 401
-- fields: {name: "Z\u0142oty wiek"}
-  model: wiki.theme
-  pk: 402
-- fields: {name: "\u0141zy"}
-  model: wiki.theme
-  pk: 130
-- fields: {name: "\u015Alub"}
-  model: wiki.theme
-  pk: 323
-- fields: {name: "\u015Amiech"}
-  model: wiki.theme
-  pk: 324
-- fields: {name: "\u015Amier\u0107"}
-  model: wiki.theme
-  pk: 325
-- fields: {name: "\u015Amier\u0107 bohaterska"}
-  model: wiki.theme
-  pk: 326
-- fields: {name: "\u015Apiew"}
-  model: wiki.theme
-  pk: 327
-- fields: {name: "\u015Awiat\u0142o"}
-  model: wiki.theme
-  pk: 328
-- fields: {name: "\u015Awit"}
-  model: wiki.theme
-  pk: 331
-- fields: {name: "\u015Awi\u0119toszek"}
-  model: wiki.theme
-  pk: 329
-- fields: {name: "\u015Awi\u0119ty"}
-  model: wiki.theme
-  pk: 330
-- fields: {name: "\u017Ba\u0142oba"}
-  model: wiki.theme
-  pk: 407
-- fields: {name: "\u017Bebrak"}
-  model: wiki.theme
-  pk: 408
-- fields: {name: "\u017Bona"}
-  model: wiki.theme
-  pk: 410
-- fields: {name: "\u017Bo\u0142nierz"}
-  model: wiki.theme
-  pk: 409
-- fields: {name: "\u017Bycie jako w\u0119dr\xF3wka"}
-  model: wiki.theme
-  pk: 411
-- fields: {name: "\u017Bycie snem"}
-  model: wiki.theme
-  pk: 412
-- fields: {name: "\u017Byd"}
-  model: wiki.theme
-  pk: 413
-- fields: {name: "\u017Bywio\u0142y"}
-  model: wiki.theme
-  pk: 414
-
diff --git a/apps/wiki/forms.py b/apps/wiki/forms.py
deleted file mode 100644 (file)
index 3ef3ed1..0000000
+++ /dev/null
@@ -1,104 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# This file is part of FNP-Redakcja, 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 catalogue.models import Chunk
-
-
-class DocumentPubmarkForm(forms.Form):
-    """
-        Form for marking revisions for publishing.
-    """
-
-    id = forms.CharField(widget=forms.HiddenInput)
-    publishable = forms.BooleanField(required=False, initial=True,
-            label=_('Publishable'))
-    revision = forms.IntegerField(widget=forms.HiddenInput)
-
-
-class DocumentTextSaveForm(forms.Form):
-    """
-    Form for saving document's text:
-
-        * parent_revision - revision which the modified text originated from.
-        * comment - user's verbose comment; will be used in commit.
-        * stage_completed - mark this change as end of given stage.
-
-    """
-
-    parent_revision = forms.IntegerField(widget=forms.HiddenInput, required=False)
-    text = forms.CharField(widget=forms.HiddenInput)
-
-    author_name = forms.CharField(
-        required=True,
-        label=_(u"Author"),
-        help_text=_(u"Your name"),
-    )
-
-    author_email = forms.EmailField(
-        required=True,
-        label=_(u"Author's email"),
-        help_text=_(u"Your email address, so we can show a gravatar :)"),
-    )
-
-    comment = forms.CharField(
-        required=True,
-        widget=forms.Textarea,
-        label=_(u"Your comments"),
-        help_text=_(u"Describe changes you made."),
-    )
-
-    stage_completed = forms.ModelChoiceField(
-        queryset=Chunk.tag_model.objects.all(),
-        required=False,
-        label=_(u"Completed"),
-        help_text=_(u"If you completed a life cycle stage, select it."),
-    )
-
-    publishable = forms.BooleanField(required=False, initial=False,
-        label=_('Publishable'),
-        help_text=_(u"Mark this revision as publishable.")
-    )
-
-    def __init__(self, *args, **kwargs):
-        user = kwargs.pop('user')
-        r = super(DocumentTextSaveForm, self).__init__(*args, **kwargs)
-        if user and user.is_authenticated():
-            self.fields['author_name'].required = False
-            self.fields['author_email'].required = False
-        return r
-
-
-class DocumentTextRevertForm(forms.Form):
-    """
-    Form for reverting document's text:
-
-        * revision - revision to revert to.
-        * comment - user's verbose comment; will be used in commit.
-
-    """
-
-    revision = forms.IntegerField(widget=forms.HiddenInput)
-
-    author_name = forms.CharField(
-        required=False,
-        label=_(u"Author"),
-        help_text=_(u"Your name"),
-    )
-
-    author_email = forms.EmailField(
-        required=False,
-        label=_(u"Author's email"),
-        help_text=_(u"Your email address, so we can show a gravatar :)"),
-    )
-
-    comment = forms.CharField(
-        required=True,
-        widget=forms.Textarea,
-        label=_(u"Your comments"),
-        help_text=_(u"Describe the reason for reverting."),
-    )
diff --git a/apps/wiki/helpers.py b/apps/wiki/helpers.py
deleted file mode 100644 (file)
index 877a9d0..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-from datetime import datetime
-from functools import wraps
-
-from django import http
-import json
-from django.utils.functional import Promise
-
-
-class ExtendedEncoder(json.JSONEncoder):
-
-    def default(self, obj):
-        if isinstance(obj, Promise):
-            return unicode(obj)
-
-        if isinstance(obj, datetime):
-            return datetime.ctime(obj) + " " + (datetime.tzname(obj) or 'GMT')
-
-        return json.JSONEncoder.default(self, obj)
-
-
-# shortcut for JSON reponses
-class JSONResponse(http.HttpResponse):
-
-    def __init__(self, data={}, **kwargs):
-        # get rid of content_type
-        kwargs.pop('content_type', None)
-
-        data = json.dumps(data, cls=ExtendedEncoder)
-        super(JSONResponse, self).__init__(data, content_type="application/json", **kwargs)
-
-
-# return errors
-class JSONFormInvalid(JSONResponse):
-    def __init__(self, form):
-        super(JSONFormInvalid, self).__init__(form.errors, status=400)
-
-
-class JSONServerError(JSONResponse):
-    def __init__(self, *args, **kwargs):
-        kwargs['status'] = 500
-        super(JSONServerError, self).__init__(*args, **kwargs)
-
-
-def ajax_login_required(view):
-    @wraps(view)
-    def authenticated_view(request, *args, **kwargs):
-        if not request.user.is_authenticated():
-            return http.HttpResponse("Login required.", status=401, content_type="text/plain")
-        return view(request, *args, **kwargs)
-    return authenticated_view
-
-
-def ajax_require_permission(permission):
-    def decorator(view):
-        @wraps(view)
-        def authorized_view(request, *args, **kwargs):
-            if not request.user.has_perm(permission):
-                return http.HttpResponse("Access Forbidden.", status=403, content_type="text/plain")
-            return view(request, *args, **kwargs)
-        return authorized_view
-    return decorator
diff --git a/apps/wiki/locale/pl/LC_MESSAGES/django.mo b/apps/wiki/locale/pl/LC_MESSAGES/django.mo
deleted file mode 100644 (file)
index cc5b3a5..0000000
Binary files a/apps/wiki/locale/pl/LC_MESSAGES/django.mo and /dev/null differ
diff --git a/apps/wiki/locale/pl/LC_MESSAGES/django.po b/apps/wiki/locale/pl/LC_MESSAGES/django.po
deleted file mode 100644 (file)
index a679724..0000000
+++ /dev/null
@@ -1,470 +0,0 @@
-# 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: Platforma Redakcyjna\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2013-07-10 16:58+0200\n"
-"PO-Revision-Date: 2013-07-10 16:58+0100\n"
-"Last-Translator: Radek Czajka <radoslaw.czajka@nowoczesnapolska.org.pl>\n"
-"Language-Team: Fundacja Nowoczesna Polska <fundacja@nowoczesnapolska.org."
-"pl>\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"X-Generator: Poedit 1.5.4\n"
-
-#: forms.py:19 forms.py:63 views.py:279
-msgid "Publishable"
-msgstr "Gotowe do publikacji"
-
-#: forms.py:38 forms.py:89
-msgid "Author"
-msgstr "Autor"
-
-#: forms.py:39 forms.py:90
-msgid "Your name"
-msgstr "Imię i nazwisko"
-
-#: forms.py:44 forms.py:95
-msgid "Author's email"
-msgstr "E-mail autora"
-
-#: forms.py:45 forms.py:96
-msgid "Your email address, so we can show a gravatar :)"
-msgstr "Adres e-mail, żebyśmy mogli pokazać gravatar :)"
-
-#: forms.py:51 forms.py:102
-msgid "Your comments"
-msgstr "Twój komentarz"
-
-#: forms.py:52
-msgid "Describe changes you made."
-msgstr "Opisz swoje zmiany"
-
-#: forms.py:58
-msgid "Completed"
-msgstr "Ukończono"
-
-#: forms.py:59
-msgid "If you completed a life cycle stage, select it."
-msgstr "Jeśli został ukończony etap prac, wskaż go."
-
-#: forms.py:64
-msgid "Mark this revision as publishable."
-msgstr "Oznacz tę wersję jako gotową do publikacji."
-
-#: forms.py:103
-msgid "Describe the reason for reverting."
-msgstr "Opisz powód przywrócenia."
-
-#: models.py:14
-msgid "name"
-msgstr "nazwa"
-
-#: models.py:18
-msgid "theme"
-msgstr "motyw"
-
-#: models.py:19
-msgid "themes"
-msgstr "motywy"
-
-#: views.py:281
-msgid "Published"
-msgstr "Opublikowano"
-
-#: views.py:302
-msgid "Revision marked"
-msgstr "Wersja oznaczona"
-
-#: views.py:304
-msgid "Nothing changed"
-msgstr "Nic nie uległo zmianie"
-
-#: templates/admin/wiki/theme/change_list.html:22
-msgid "Table for Redmine wiki"
-msgstr "Tabela do wiki na Redmine"
-
-#: templates/wiki/diff_table.html:5
-msgid "Old version"
-msgstr "Stara wersja"
-
-#: templates/wiki/diff_table.html:6
-msgid "New version"
-msgstr "Nowa wersja"
-
-#: templates/wiki/document_details.html:32
-msgid "Click to open/close gallery"
-msgstr "Kliknij, aby (ro)zwinąć galerię"
-
-#: templates/wiki/document_details_base.html:33
-msgid "Help"
-msgstr "Pomoc"
-
-#: templates/wiki/document_details_base.html:35
-msgid "Version"
-msgstr "Wersja"
-
-#: templates/wiki/document_details_base.html:35
-msgid "Unknown"
-msgstr "nieznana"
-
-#: templates/wiki/document_details_base.html:37
-#: templates/wiki/pubmark_dialog.html:16
-msgid "Save"
-msgstr "Zapisz"
-
-#: templates/wiki/document_details_base.html:38
-msgid "Save attempt in progress"
-msgstr "Trwa zapisywanie"
-
-#: templates/wiki/document_details_base.html:39
-msgid "There is a newer version of this document!"
-msgstr "Istnieje nowsza wersja tego dokumentu!"
-
-#: templates/wiki/pubmark_dialog.html:17 templates/wiki/revert_dialog.html:40
-msgid "Cancel"
-msgstr "Anuluj"
-
-#: templates/wiki/revert_dialog.html:39
-msgid "Revert"
-msgstr "Przywróć"
-
-#: templates/wiki/tabs/annotations_view.html:9
-msgid "all"
-msgstr "wszystkie"
-
-#: templates/wiki/tabs/annotations_view_item.html:3
-msgid "Annotations"
-msgstr "Przypisy"
-
-#: templates/wiki/tabs/gallery_view.html:5
-msgid "Go to first image of this part"
-msgstr "Przejdź na początek"
-
-#: templates/wiki/tabs/gallery_view.html:8
-msgid "Previous"
-msgstr "Poprzednie"
-
-#: templates/wiki/tabs/gallery_view.html:13
-msgid "Next"
-msgstr "Następne"
-
-#: templates/wiki/tabs/gallery_view.html:16
-msgid "Zoom in"
-msgstr "Powiększ"
-
-#: templates/wiki/tabs/gallery_view.html:17
-msgid "Zoom out"
-msgstr "Zmniejsz"
-
-#: templates/wiki/tabs/gallery_view_item.html:3
-msgid "Gallery"
-msgstr "Galeria"
-
-#: templates/wiki/tabs/history_view.html:5
-msgid "Compare versions"
-msgstr "Porównaj wersje"
-
-#: templates/wiki/tabs/history_view.html:8
-msgid "Mark for publishing"
-msgstr "Oznacz do publikacji"
-
-#: templates/wiki/tabs/history_view.html:11
-msgid "Revert document"
-msgstr "Przywróć wersję"
-
-#: templates/wiki/tabs/history_view.html:14
-msgid "View version"
-msgstr "Zobacz wersję"
-
-#: templates/wiki/tabs/history_view_item.html:3
-msgid "History"
-msgstr "Historia"
-
-#: templates/wiki/tabs/search_view.html:3
-#: templates/wiki/tabs/search_view.html:5
-msgid "Search"
-msgstr "Szukaj"
-
-#: templates/wiki/tabs/search_view.html:8
-msgid "Replace with"
-msgstr "Zamień na"
-
-#: templates/wiki/tabs/search_view.html:10
-msgid "Replace"
-msgstr "Zamień"
-
-#: templates/wiki/tabs/search_view.html:12
-msgid "Replace all"
-msgstr "Zamień wszystko"
-
-msgid "Options"
-msgstr "Opcje"
-
-#: templates/wiki/tabs/search_view.html:15
-msgid "Case sensitive"
-msgstr "Rozróżniaj wielkość liter"
-
-#: templates/wiki/tabs/search_view.html:17
-msgid "From cursor"
-msgstr "Zacznij od kursora"
-
-#: templates/wiki/tabs/search_view_item.html:3
-msgid "Search and replace"
-msgstr "Znajdź i zamień"
-
-#: templates/wiki/tabs/source_editor_item.html:5
-msgid "Source code"
-msgstr "Kod źródłowy"
-
-#: templates/wiki/tabs/summary_view.html:13
-msgid "Refresh from working copy"
-msgstr "Odśwież z edytowanej wersji"
-
-#: templates/wiki/tabs/summary_view.html:17
-msgid "Title"
-msgstr "Tytuł"
-
-#: templates/wiki/tabs/summary_view.html:21
-msgid "Go to the book's page"
-msgstr "Przejdź do strony książki"
-
-#: templates/wiki/tabs/summary_view.html:24
-msgid "Document ID"
-msgstr "ID dokumentu"
-
-#: templates/wiki/tabs/summary_view.html:28
-msgid "Current version"
-msgstr "Aktualna wersja"
-
-#: templates/wiki/tabs/summary_view.html:31
-msgid "Last edited by"
-msgstr "Ostatnio edytowane przez"
-
-#: templates/wiki/tabs/summary_view.html:35
-msgid "Link to gallery"
-msgstr "Link do galerii"
-
-#: templates/wiki/tabs/summary_view.html:40
-msgid "Characters in document"
-msgstr "Znaków w dokumencie"
-
-#: templates/wiki/tabs/summary_view.html:41
-msgid "pages"
-msgstr "stron maszynopisu"
-
-#: templates/wiki/tabs/summary_view.html:41
-msgid "untagged"
-msgstr "nieotagowane"
-
-#: templates/wiki/tabs/summary_view_item.html:3
-msgid "Summary"
-msgstr "Podsumowanie"
-
-#: templates/wiki/tabs/wysiwyg_editor.html:9
-msgid "Insert theme"
-msgstr "Wstaw motyw"
-
-#: templates/wiki/tabs/wysiwyg_editor.html:12
-msgid "Insert annotation"
-msgstr "Wstaw przypis"
-
-#: templates/wiki/tabs/wysiwyg_editor_item.html:3
-msgid "Visual editor"
-msgstr "Edytor wizualny"
-
-#~ msgid "ZIP file"
-#~ msgstr "Plik ZIP"
-
-#~ msgid "Chunk with this slug already exists"
-#~ msgstr "Część z tym slugiem już istnieje"
-
-#~ msgid "Append to"
-#~ msgstr "Dołącz do"
-
-#~ msgid "title"
-#~ msgstr "tytuł"
-
-#~ msgid "scan gallery name"
-#~ msgstr "nazwa galerii skanów"
-
-#~ msgid "parent"
-#~ msgstr "rodzic"
-
-#~ msgid "parent number"
-#~ msgstr "numeracja rodzica"
-
-#~ msgid "book"
-#~ msgstr "książka"
-
-#~ msgid "books"
-#~ msgstr "książki"
-
-#~ msgid "Slug already used for %s"
-#~ msgstr "Slug taki sam jak dla pliku %s"
-
-#~ msgid "Slug already used in repository."
-#~ msgstr "Dokument o tym slugu już istnieje w repozytorium."
-
-#~ msgid "File should be UTF-8 encoded."
-#~ msgstr "Plik powinien mieć kodowanie UTF-8."
-
-#~ msgid "Tag added"
-#~ msgstr "Dodano tag"
-
-#~ msgid "Append book"
-#~ msgstr "Dołącz książkę"
-
-#~ msgid "edit"
-#~ msgstr "edytuj"
-
-#~ msgid "add basic document structure"
-#~ msgstr "dodaj podstawową strukturę dokumentu"
-
-#~ msgid "change master tag to"
-#~ msgstr "zmień tak master na"
-
-#~ msgid "add begin trimming tag"
-#~ msgstr "dodaj początkowy ogranicznik"
-
-#~ msgid "add end trimming tag"
-#~ msgstr "dodaj końcowy ogranicznik"
-
-#~ msgid "unstructured text"
-#~ msgstr "tekst bez struktury"
-
-#~ msgid "unknown XML"
-#~ msgstr "nieznany XML"
-
-#~ msgid "broken document"
-#~ msgstr "uszkodzony dokument"
-
-#~ msgid "Apply fixes"
-#~ msgstr "Wykonaj zmiany"
-
-#~ msgid "Append to other book"
-#~ msgstr "Dołącz do innej książki"
-
-#~ msgid "Last published"
-#~ msgstr "Ostatnio opublikowano"
-
-#~ msgid "Full XML"
-#~ msgstr "Pełny XML"
-
-#~ msgid "HTML version"
-#~ msgstr "Wersja HTML"
-
-#~ msgid "TXT version"
-#~ msgstr "Wersja TXT"
-
-#~ msgid "EPUB version"
-#~ msgstr "Wersja EPUB"
-
-#~ msgid "PDF version"
-#~ msgstr "Wersja PDF"
-
-#~ msgid "This book cannot be published yet"
-#~ msgstr "Ta książka nie może jeszcze zostać opublikowana"
-
-#~ msgid "Add chunk"
-#~ msgstr "Dodaj część"
-
-#~ msgid "Clear filter"
-#~ msgstr "Wyczyść filtr"
-
-#~ msgid "No books found."
-#~ msgstr "Nie znaleziono książek."
-
-#~ msgid "Your last edited documents"
-#~ msgstr "Twoje ostatnie edycje"
-
-#~ msgid "Bulk documents upload"
-#~ msgstr "Hurtowe dodawanie dokumentów"
-
-#~ msgid ""
-#~ "Please submit a ZIP with UTF-8 encoded XML files. Files not ending with "
-#~ "<code>.xml</code> will be ignored."
-#~ msgstr ""
-#~ "Proszę wskazać archiwum ZIP z plikami XML w kodowaniu UTF-8. Pliki nie "
-#~ "kończące się na <code>.xml</code> zostaną zignorowane."
-
-#~ msgid "Upload"
-#~ msgstr "Załaduj"
-
-#~ msgid ""
-#~ "There have been some errors. No files have been added to the repository."
-#~ msgstr "Wystąpiły błędy. Żadne pliki nie zostały dodane do repozytorium."
-
-#~ msgid "Offending files"
-#~ msgstr "Błędne pliki"
-
-#~ msgid "Correct files"
-#~ msgstr "Poprawne pliki"
-
-#~ msgid "Files have been successfully uploaded to the repository."
-#~ msgstr "Pliki zostały dodane do repozytorium."
-
-#~ msgid "Uploaded files"
-#~ msgstr "Dodane pliki"
-
-#~ msgid "Skipped files"
-#~ msgstr "Pominięte pliki"
-
-#~ msgid "Files skipped due to no <code>.xml</code> extension"
-#~ msgstr "Pliki pominięte z powodu braku rozszerzenia <code>.xml</code>."
-
-#~ msgid "Users"
-#~ msgstr "Użytkownicy"
-
-#~ msgid "Assigned to me"
-#~ msgstr "Przypisane do mnie"
-
-#~ msgid "Unassigned"
-#~ msgstr "Nie przypisane"
-
-#~ msgid "All"
-#~ msgstr "Wszystkie"
-
-#~ msgid "Add"
-#~ msgstr "Dodaj"
-
-#~ msgid "Admin"
-#~ msgstr "Administracja"
-
-#~ msgid "First correction"
-#~ msgstr "Autokorekta"
-
-#~ msgid "Tagging"
-#~ msgstr "Tagowanie"
-
-#~ msgid "Initial Proofreading"
-#~ msgstr "Korekta"
-
-#~ msgid "Annotation Proofreading"
-#~ msgstr "Sprawdzenie przypisów źródła"
-
-#~ msgid "Modernisation"
-#~ msgstr "Uwspółcześnienie"
-
-#~ msgid "Themes"
-#~ msgstr "Motywy"
-
-#~ msgid "Editor's Proofreading"
-#~ msgstr "Ostateczna redakcja literacka"
-
-#~ msgid "Technical Editor's Proofreading"
-#~ msgstr "Ostateczna redakcja techniczna"
-
-#~ msgid "Finished stage: %s"
-#~ msgstr "Ukończony etap: %s"
-
-#~ msgid "Refresh"
-#~ msgstr "Odśwież"
-
-#~ msgid "Insert special character"
-#~ msgstr "Wstaw znak specjalny"
diff --git a/apps/wiki/migrations/0001_initial.py b/apps/wiki/migrations/0001_initial.py
deleted file mode 100644 (file)
index 4acf5ba..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-# encoding: utf-8
-import datetime
-from south.db import db
-from south.v2 import SchemaMigration
-from django.db import models
-
-class Migration(SchemaMigration):
-    
-    def forwards(self, orm):
-        pass
-    
-    
-    def backwards(self, orm):
-        pass
-    
-    
-    models = {
-        
-    }
-    
-    complete_apps = ['wiki']
diff --git a/apps/wiki/migrations/0002_auto__add_theme.py b/apps/wiki/migrations/0002_auto__add_theme.py
deleted file mode 100644 (file)
index 6688139..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-# encoding: utf-8
-import datetime
-from south.db import db
-from south.v2 import SchemaMigration
-from django.db import models
-
-class Migration(SchemaMigration):
-    
-    def forwards(self, orm):
-        
-        # Adding model 'Theme'
-        db.create_table('wiki_theme', (
-            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
-            ('name', self.gf('django.db.models.fields.CharField')(unique=True, max_length=50)),
-        ))
-        db.send_create_signal('wiki', ['Theme'])
-        
-        if not db.dry_run:
-            from django.core.management import call_command
-            call_command("loaddata", "initial_themes.yaml")
-
-    
-    
-    def backwards(self, orm):
-        
-        # Deleting model 'Theme'
-        db.delete_table('wiki_theme')
-    
-    
-    models = {
-        'wiki.theme': {
-            'Meta': {'object_name': 'Theme'},
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '50'})
-        }
-    }
-    
-    complete_apps = ['wiki']
diff --git a/apps/wiki/migrations/__init__.py b/apps/wiki/migrations/__init__.py
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/apps/wiki/models.py b/apps/wiki/models.py
deleted file mode 100644 (file)
index c539908..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# This file is part of FNP-Redakcja, 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 _
-
-import logging
-logger = logging.getLogger("fnp.wiki")
-
-
-class Theme(models.Model):
-    name = models.CharField(_('name'), max_length=50, unique=True)
-
-    class Meta:
-        ordering = ('name',)
-        verbose_name = _('theme')
-        verbose_name_plural = _('themes')
-
-    def __unicode__(self):
-        return self.name
-
-    def __repr__(self):
-        return "Theme(name=%r)" % self.name
-
diff --git a/apps/wiki/nice_diff.py b/apps/wiki/nice_diff.py
deleted file mode 100644 (file)
index b228fad..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
-# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
-#
-import difflib
-import re
-from collections import deque
-
-from django.template.loader import render_to_string
-from django.utils.html import escape as html_escape
-
-DIFF_RE = re.compile(r"""\x00([+^-])""", re.UNICODE)
-NAMES = {'+': 'added', '-': 'removed', '^': 'changed'}
-
-
-def diff_replace(match):
-    return """<span class="diff_mark diff_mark_%s">""" % NAMES[match.group(1)]
-
-
-def filter_line(line):
-    return  DIFF_RE.sub(diff_replace, html_escape(line)).replace('\x01', '</span>')
-
-
-def format_changeset(a, b, change):
-    return (a[0], filter_line(a[1]), b[0], filter_line(b[1]), change)
-
-
-def html_diff_table(la, lb, context=None):
-    all_changes = difflib._mdiff(la, lb)
-
-    if context is None:
-        changes = (format_changeset(*c) for c in all_changes)
-    else:
-        changes = []
-        q = deque()
-        after_change = False
-
-        for changeset in all_changes:
-            q.append(changeset)
-
-            if changeset[2]:
-                after_change = True
-                if not after_change:
-                    changes.append((0, '-----', 0, '-----', False))
-                changes.extend(format_changeset(*c) for c in q)
-                q.clear()
-            else:
-                if len(q) == context and after_change:
-                    changes.extend(format_changeset(*c) for c in q)
-                    q.clear()
-                    after_change = False
-                elif len(q) > context:
-                    q.popleft()
-
-    return render_to_string("wiki/diff_table.html", {
-        "changes": changes,
-    })
-
-
-__all__ = ['html_diff_table']
diff --git a/apps/wiki/settings.py b/apps/wiki/settings.py
deleted file mode 100644 (file)
index 50f49d8..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-from django.conf import settings
-
-GALLERY_URL = settings.MEDIA_URL + 'images/'
diff --git a/apps/wiki/templates/admin/wiki/theme/change_list.html b/apps/wiki/templates/admin/wiki/theme/change_list.html
deleted file mode 100755 (executable)
index 3e5d2ea..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-{% extends "admin/change_list.html" %}
-{% load i18n %}
-
-{% block extrahead %}
-{{ block.super }}
-<script type="text/javascript">
-(function($) {
-    $(document).ready(function($) {
-        $("#redmine-table-switch").click(function() {
-            $('#redmine-table').toggle()
-        });
-    });
-})(django.jQuery);
-</script>
-{% endblock %}
-
-
-
-{% block pretitle %}
-
-
-<a id="redmine-table-switch">↓ {% trans "Table for Redmine wiki" %} ↓</a>
-<div id="redmine-table" style="display:none; padding:1em; border: 1px solid #aaa;">
-    |{% for theme in cl.get_queryset %}[[{{ theme }}]]|{% if forloop.counter|divisibleby:7 %}<br/>
-        |{% endif %}{% endfor %}
-</div>
-
-{{ block.super }}
-{% endblock %}
diff --git a/apps/wiki/templates/wiki/diff_table.html b/apps/wiki/templates/wiki/diff_table.html
deleted file mode 100644 (file)
index 818c38c..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-{% load i18n %}
-<table class="diff_table">
-       <thead>
-               <tr>
-                       <th colspan="2">{% trans "Old version" %}</th>
-                       <th colspan="2">{% trans "New version" %}</th>
-               </tr>
-       </thead>
-<tbody>
-{% for an, a, bn, b, has_change in changes %}
-
-<tr class="{% if has_change %}change{% endif %}">
-<td>{{an}}</td>
-<td class="left">{{ a|safe }}&nbsp;</td>
-<td>{{bn}}</td>
-<td class="right">{{ b|safe }}&nbsp;</td>
-</tr>
-
-{% endfor %}
-</tbody>
-</table>
\ No newline at end of file
diff --git a/apps/wiki/templates/wiki/document_details.html b/apps/wiki/templates/wiki/document_details.html
deleted file mode 100644 (file)
index 2211c0a..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-{% extends "wiki/document_details_base.html" %}
-{% load i18n %}
-
-{% block extrabody %}
-{{ block.super }}
-<script src="{{ STATIC_URL }}js/lib/codemirror-0.8/codemirror.js" type="text/javascript" charset="utf-8">
-</script>
-<script src="{{ STATIC_URL }}js/wiki/loader.js" type="text/javascript" charset="utf-8"> </script>
-{% endblock %}
-
-{% block tabs-menu %}
-    {% include "wiki/tabs/summary_view_item.html" %}
-    {% include "wiki/tabs/wysiwyg_editor_item.html" %}
-    {% include "wiki/tabs/source_editor_item.html" %}
-    {% include "wiki/tabs/history_view_item.html" %}
-{% endblock %}
-
-{% block tabs-content %}
-    {% include "wiki/tabs/summary_view.html" %}
-    {% include "wiki/tabs/wysiwyg_editor.html" %}
-    {% include "wiki/tabs/source_editor.html" %}
-    {% include "wiki/tabs/history_view.html" %}
-{% endblock %}
-
-{% block tabs-right %}
-    {% include "wiki/tabs/gallery_view_item.html" %}
-    {% include "wiki/tabs/annotations_view_item.html" %}
-    {% include "wiki/tabs/search_view_item.html" %}
-{% endblock %}
-
-{% block splitter-extra %}
-<div class="vsplitbar" title="{% trans "Click to open/close gallery" %}">
-    <p class="vsplitbar-title"></p>
-    </div>
-    <div id="sidebar">
-        {% include "wiki/tabs/gallery_view.html" %}
-        {% include "wiki/tabs/annotations_view.html" %}
-        {% include "wiki/tabs/search_view.html" %}
-    </div>
-    <div id="drag-layer"></div>
-{% endblock %}
-
-{% block dialogs %}
-    {% include "wiki/save_dialog.html" %}
-    {% include "wiki/revert_dialog.html" %}
-    {% include "wiki/tag_dialog.html" %}
-    {% if can_pubmark %}
-        {% include "wiki/pubmark_dialog.html" %}
-    {% endif %}
-{% endblock %}
diff --git a/apps/wiki/templates/wiki/document_details_base.html b/apps/wiki/templates/wiki/document_details_base.html
deleted file mode 100644 (file)
index 00c4f6a..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-{% extends "base.html" %}
-{% load toolbar_tags i18n %}
-
-{% block titleextra %}{% if chunk.pretty_title %}{{ chunk.pretty_title }}{% else %}{{ chunk.book.title }}{% endif %}{% endblock %}
-{% block extrahead %}
-{% load pipeline %}
-{% stylesheet 'detail' %}
-{% endblock %}
-
-{% block extrabody %}
-<script type="text/javascript">
-    var STATIC_URL = '{{STATIC_URL}}';
-</script>
-{% javascript 'detail' %}
-{% endblock %}
-
-{% block maincontent %}
-<div id="document-meta"
-       data-chunk-id="{{ chunk.pk }}" style="display:none">
-
-       <span data-key="gallery">{{ chunk.book.gallery }}</span>
-       <span data-key="gallery-start">{% if chunk.gallery_start %}{{ chunk.gallery_start }}{% endif %}</span>
-       <span data-key="revision">{{ revision }}</span>
-    <span data-key="diff">{{ request.GET.diff }}</span>
-
-       {% block meta-extra %} {% endblock %}
-</div>
-
-<div id="header">
-    <h1><a href="{% url 'catalogue_document_list' %}"><img src="{{STATIC_URL}}icons/go-home.png"  alt="Home" /></a><a href="{% url 'catalogue_document_list' %}">Strona<br>główna</a></h1>
-    <div id="tools">
-        <a href="{{ REDMINE_URL }}projects/wl-publikacje/wiki/Pomoc" target="_blank">
-        {% trans "Help" %}</a>
-        | {% include "registration/head_login.html" %}
-        | {% trans "Version" %}: <span id="document-revision">{% trans "Unknown" %}</span>
-               {% if not readonly %}
-            | <button style="margin-left: 6px" id="save-button">{% trans "Save" %}</button>
-                       <span id='save-attempt-info'>{% trans "Save attempt in progress" %}</span>
-            <span id='out-of-date-info'>{% trans "There is a newer version of this document!" %}</span>
-               {% endif %}
-    </div>
-    <ol id="tabs" class="tabs">
-       {% block tabs-menu %} {% endblock %}
-    </ol>
-    <ol id="tabs-right" class="tabs">
-        {% block tabs-right %} {% endblock %}
-    </ol>
-</div>
-<div id="splitter">
-    <div id="editor" class="{% block editor-class %} {% endblock %}">
-       {% block tabs-content %} {% endblock %}
-    </div>
-       {% block splitter-extra %} {% endblock %}
-</div>
-
-{% block dialogs %} {% endblock %}
-
-{% endblock %}
diff --git a/apps/wiki/templates/wiki/document_details_readonly.html b/apps/wiki/templates/wiki/document_details_readonly.html
deleted file mode 100644 (file)
index 71556a1..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-{% extends "wiki/document_details_base.html" %}
-{% load i18n %}
-
-{% block editor-class %}readonly{% endblock %}
-
-{% block extrabody %}
-{{ block.super }}
-<script src="{{STATIC_URL}}js/lib/codemirror-0.8/codemirror.js" type="text/javascript" charset="utf-8">
-</script>
-<script src="{{STATIC_URL}}js/wiki/loader_readonly.js" type="text/javascript" charset="utf-8"> </script>
-{% endblock %}
-
-{% block tabs-menu %}
-    {% include "wiki/tabs/wysiwyg_editor_item.html" %}
-       {% include "wiki/tabs/source_editor_item.html" %}
-{% endblock %}
-
-{% block tabs-content %}
-    {% include "wiki/tabs/wysiwyg_editor.html" %}
-       {% include "wiki/tabs/source_editor.html" %}
-{% endblock %}
-
-{% block splitter-extra %}
-{% endblock %}
-
-{% block dialogs %}
-{% endblock %}
\ No newline at end of file
diff --git a/apps/wiki/templates/wiki/pubmark_dialog.html b/apps/wiki/templates/wiki/pubmark_dialog.html
deleted file mode 100755 (executable)
index a70a0c3..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-{% load i18n %}
-<div id="pubmark_dialog" class="dialog" data-ui-jsclass="PubmarkDialog">
-       <form method="POST" action="#">
-    {% csrf_token %}
-               {% for field in forms.pubmark.visible_fields %}
-               <p>{{ field.label_tag }} {{ field }} <span data-ui-error-for="{{ field.name }}"> </span></p>
-               <p>{{ field.help_text }}</p>
-               {% endfor %}
-
-               {% for f in forms.pubmark.hidden_fields %}
-                       {{ f }}
-               {% endfor %}
-               <p data-ui-error-for="__all__"> </p>
-
-               <p class="action_area">
-                       <button type="submit" class="ok" data-ui-action="save">{% trans "Save" %}</button>
-                       <button type="button" class="cancel" data-ui-action="cancel">{% trans "Cancel" %}</button>
-               </p>
-       </form>
-</div>
diff --git a/apps/wiki/templates/wiki/revert_dialog.html b/apps/wiki/templates/wiki/revert_dialog.html
deleted file mode 100644 (file)
index 6f1793b..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-{% load i18n %}
-<div id="revert_dialog" class="dialog" data-ui-jsclass="RevertDialog">
-       <form method="POST" action="#">
-    {% csrf_token %}
-       <p>{{ forms.text_revert.comment.label }}</p>
-       <p class="help_text">
-               {{ forms.text_revert.comment.help_text}}
-               <span data-ui-error-for="{{ forms.text_revert.comment.name }}"> </span>
-       </p>
-       {{forms.text_revert.comment }}
-
-
-
-       {% if request.user.is_anonymous %}
-    <table style='margin:0 4%;'>
-    <tr>
-        <td>{{ forms.text_revert.author_name.label }}:</td>
-        <td>{{ forms.text_revert.author_name }}
-        <span class="help_text">{{ forms.text_revert.author_name.help_text }}</span>
-        <span data-ui-error-for="{{ forms.text_revert.author_name.name }}"> </span></td>
-    </tr>
-    <tr>
-        <td>{{ forms.text_revert.author_email.label }}:</td>
-        <td>{{ forms.text_revert.author_email }}
-        <span class="help_text">{{ forms.text_revert.author_email.help_text }}</span>
-        <span data-ui-error-for="{{ forms.text_revert.author_email.name }}"> </span></td>
-    </tr>
-    </table>
-       {% endif %}
-
-
-       {% for f in forms.text_revert.hidden_fields %}
-               {{ f }}
-       {% endfor %}
-
-       <p data-ui-error-for="__all__"> </p>
-
-       <p class="action_area">
-               <button type="submit" class="ok" data-ui-action="revert">{% trans "Revert" %}</button>
-               <button type="button" class="cancel" data-ui-action="cancel">{% trans "Cancel" %}</button>
-       </p>
-       </form>
-</div>
diff --git a/apps/wiki/templates/wiki/save_dialog.html b/apps/wiki/templates/wiki/save_dialog.html
deleted file mode 100644 (file)
index b133045..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-{% load i18n %}
-<div id="save_dialog" class="dialog" data-ui-jsclass="SaveDialog">
-       <form method="POST" action="#">
-    {% csrf_token %}
-       <p>{{ forms.text_save.comment.label }}</p>
-       <p class="help_text">
-               {{ forms.text_save.comment.help_text}}
-               <span data-ui-error-for="{{ forms.text_save.comment.name }}"> </span>
-       </p>
-       {{forms.text_save.comment }}
-
-
-
-       {% if request.user.is_anonymous %}
-    <table style='margin:0 4%;'>
-    <tr>
-        <td>{{ forms.text_save.author_name.label }}:</td>
-        <td>{{ forms.text_save.author_name }}
-        <span class="help_text">{{ forms.text_save.author_name.help_text }}</span>
-        <span data-ui-error-for="{{ forms.text_save.author_name.name }}"> </span></td>
-    </tr>
-    <tr>
-        <td>{{ forms.text_save.author_email.label }}:</td>
-        <td>{{ forms.text_save.author_email }}
-        <span class="help_text">{{ forms.text_save.author_email.help_text }}</span>
-        <span data-ui-error-for="{{ forms.text_save.author_email.name }}"> </span></td>
-    </tr>
-    </table>
-       {% else %}
-       <p>
-               {{ forms.text_save.stage_completed.label }}:
-               {{ forms.text_save.stage_completed }}
-               <span class="help_text">{{ forms.text_save.stage_completed.help_text }}</span>
-               <span data-ui-error-for="{{ forms.text_save.stage_completed.name }}"> </span>
-       </p>
-    {% if can_pubmark %}
-       <p>
-               {{ forms.text_save.publishable.label_tag }}:
-               {{ forms.text_save.publishable }}
-               <span class="help_text">{{ forms.text_save.publishable.help_text }}</span>
-               <span data-ui-error-for="{{ forms.text_save.publishable.name }}"> </span>
-       </p>
-    {% endif %}
-
-       {% endif %}
-
-
-       {% for f in forms.text_save.hidden_fields %}
-               {{ f }}
-       {% endfor %}
-
-       <p data-ui-error-for="__all__"> </p>
-
-       <p class="action_area">
-               <button type="submit" class="ok" data-ui-action="save">Zapisz</button>
-               <button type="button" class="cancel" data-ui-action="cancel">Anuluj</button>
-       </p>
-       </form>
-</div>
diff --git a/apps/wiki/templates/wiki/tabs/annotations_view.html b/apps/wiki/templates/wiki/tabs/annotations_view.html
deleted file mode 100644 (file)
index 8e54242..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-{% load i18n %}
-<div id="side-annotations">
-    <!-- annotations toolbar -->
-    <div class="toolbar">
-        <button class="refresh" title="Przypisy autorskie" data-tag="pa">pa</button>
-        <button class="refresh active" title="Przypisy edytorskie" data-tag="pe">pe</button>
-        <button class="refresh" title="Przypisy redakcyjne" data-tag="pr">pr</button>
-        <button class="refresh" title="Przypisy tłumacza" data-tag="pt">pt</button>
-        <button class="refresh" title="Wszystkie przypisy" data-tag="pa,pe,pr,pt">{% trans "all" %}</button>
-        <div class="toolbar-end">
-        </div>
-    </div>
-    <div class="error-message">
-    </div>
-    <div class="annotations-list">
-    </div>
-    <div class="spinner">
-        <img src='/media/static/img/spinner.gif' alt="Loading" />
-    </div>
-</div>
diff --git a/apps/wiki/templates/wiki/tabs/annotations_view_item.html b/apps/wiki/templates/wiki/tabs/annotations_view_item.html
deleted file mode 100644 (file)
index 7f17ce5..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-{% load i18n %}
-<li id="AnnotationsPerspective" data-ui-related="side-annotations" data-ui-jsclass="AnnotationsPerspective">
-    <a href="#"><sup title="{% trans "Annotations" %}">[1]</sup></a>
-</li>
diff --git a/apps/wiki/templates/wiki/tabs/gallery_view.html b/apps/wiki/templates/wiki/tabs/gallery_view.html
deleted file mode 100644 (file)
index 4e57ea5..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-{% load i18n %}
-<div id="side-gallery">
-    <!-- gallery toolbar -->
-    <div class="toolbar">
-        <button class="start-page" alt="{% trans "Go to first image of this part" %}" title="{% trans "Go to first image of this part" %}">
-            <img src="{{STATIC_URL}}icons/revert.png"/>
-        </button>
-        <button class="previous-page" alt="{% trans "Previous" %}" title="{% trans "Previous" %}">
-            <img src="{{STATIC_URL}}icons/go-previous.png"/>
-        </button>
-        <input type="text" size="3" maxlength="3" value="0" class="page-number" />
-        <span id="imagesCount">/0</span>
-        <button class="next-page" alt="{% trans "Next" %}" title="{% trans "Next" %}">
-            <img src="{{STATIC_URL}}icons/go-next.png"
-                alt="{% trans "Next" %}" title="{% trans "Next" %}"/>
-        </button>
-        <button class="zoom-in">{% trans "Zoom in" %}</button>
-        <button class="zoom-out">{% trans "Zoom out" %}</button>
-        <div class="toolbar-end">
-        </div>
-    </div>
-    <div class="error_message">
-    </div>
-    <div class="gallery-image">
-        <img src="{{MEDIA_URL}}images/empty.png" alt="no image" />
-    </div>
-</div>
diff --git a/apps/wiki/templates/wiki/tabs/gallery_view_item.html b/apps/wiki/templates/wiki/tabs/gallery_view_item.html
deleted file mode 100644 (file)
index 0ad3add..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-{% load i18n %}
-<li id="ScanGalleryPerspective" data-ui-related="side-gallery" data-ui-jsclass="ScanGalleryPerspective" class='active'>
-    <a href="#"><img src="{{STATIC_URL}}icons/image-x-generic.png" alt="{% trans "Gallery" %}" title="{% trans "Gallery" %}" /></a>
-</li>
diff --git a/apps/wiki/templates/wiki/tabs/history_view.html b/apps/wiki/templates/wiki/tabs/history_view.html
deleted file mode 100644 (file)
index 93b0bb6..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-{% load i18n %}
-<div id="history-view-editor" class="editor" style="display: none">
-    <div class="toolbar">
-       <button type="button" id="make-diff-button"
-                       data-enabled-when="2" disabled="disabled">{% trans "Compare versions" %}</button>
-        {% if can_pubmark %}
-               <button type="button" id="pubmark-changeset-button"
-                       data-enabled-when="1" disabled="disabled">{% trans "Mark for publishing" %}</button>
-        {% endif %}
-               <button type="button" id="doc-revert-button"
-                       data-enabled-when="1" disabled="disabled">{% trans "Revert document" %}</button>
-               <button id="open-preview-button" disabled="disabled"
-                       data-enabled-when="1"
-                       data-basehref="{% url 'wiki_editor_readonly' chunk.book.slug chunk.slug %}">{% trans "View version" %}</button>
-
-       </div>
-    <div id="history-view">
-        <p class="message-box" style="display:none;"></p>
-
-               <table id="changes-list-container">
-        <tbody id="changes-list">
-        </tbody>
-               <tbody style="display: none;">
-                       <tr class="entry row-stub">
-                       <td data-stub-value="version"></td>
-                       <td>
-                <span data-stub-value="date"></span>
-               <br/><span data-stub-value="author"></span>
-                               <br />
-                               <span data-stub-value="description"></span>
-                       </td>
-                       <td>
-                <div data-stub-value="publishable"></div>
-                <div data-stub-value="published"></div>
-                <div data-stub-value="tag"></div>
-                       </td>
-               </tr>
-               </tbody>
-               </table>
-    </div>
-</div>
diff --git a/apps/wiki/templates/wiki/tabs/history_view_item.html b/apps/wiki/templates/wiki/tabs/history_view_item.html
deleted file mode 100644 (file)
index e9375cd..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-{% load i18n %}
-<li id="HistoryPerspective" data-ui-related="history-view-editor" data-ui-jsclass="HistoryPerspective">
-    <a href="#">{% trans "History" %}</a>
-</li>
diff --git a/apps/wiki/templates/wiki/tabs/search_view.html b/apps/wiki/templates/wiki/tabs/search_view.html
deleted file mode 100644 (file)
index 2ed15ec..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-{% load i18n %}
-<div id='side-search'>
-    <p>{% trans "Search" %}:<br/>
-    <input id='search-input' /><br/>
-    <button id='search-button'>{% trans "Search" %}</button>
-    </p>
-
-    <p>{% trans "Replace with" %}:<br/>
-        <input id='replace-input' /><br/>
-        <button id='replace-button'>{% trans "Replace" %}</button><br/>
-        <input type="checkbox" id="replace-all" />
-            <label for="replace-all">{% trans "Replace all" %}</label><br/>
-    </p>
-
-    <p>{% trans "Options" %}<br/>
-        <input type="checkbox" id="search-case-sensitive" />
-            <label for="search-case-sensitive">{% trans "Case sensitive" %}</label><br/>
-        <input type="checkbox" id="search-from-cursor" />
-            <label for="search-from-cursor">{% trans "From cursor" %}</label><br/>
-    </p>
-</div>
\ No newline at end of file
diff --git a/apps/wiki/templates/wiki/tabs/search_view_item.html b/apps/wiki/templates/wiki/tabs/search_view_item.html
deleted file mode 100644 (file)
index 7acfaac..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-{% load i18n %}
-<li id="SearchPerspective" data-ui-related="side-search" data-ui-jsclass="SearchPerspective">
-    <img src="{{STATIC_URL}}icons/system-search.png" alt="{% trans "Search and replace" %}" title="{% trans "Search and replace" %}" />
-</li>
diff --git a/apps/wiki/templates/wiki/tabs/source_editor.html b/apps/wiki/templates/wiki/tabs/source_editor.html
deleted file mode 100644 (file)
index 9b22825..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-{% load toolbar_tags i18n %}
-<div id="source-editor" class="editor">
-    {% if not readonly %}{% toolbar %}{% endif %}
-    <textarea id="codemirror_placeholder">&lt;br/&gt;</textarea>
-</div>
diff --git a/apps/wiki/templates/wiki/tabs/source_editor_item.html b/apps/wiki/templates/wiki/tabs/source_editor_item.html
deleted file mode 100644 (file)
index 22b6d66..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-{% load i18n %}
-<li id="CodeMirrorPerspective"
-       data-ui-related="source-editor"
-       data-ui-jsclass="CodeMirrorPerspective">
-    <a href="#">{% trans "Source code" %}</a>
-</li>
\ No newline at end of file
diff --git a/apps/wiki/templates/wiki/tabs/summary_view.html b/apps/wiki/templates/wiki/tabs/summary_view.html
deleted file mode 100644 (file)
index 49ad5fe..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-{% load i18n %}
-<div id="summary-view-editor" class="editor" style="display: none">
-    <!-- <div class="toolbar">
-    </div> -->
-    <div id="summary-view">
-       <div class="summary-cover-area">
-               <p><img id="summary-cover" class="book-cover"
-                       {% if revision %}
-                            src="{% url 'cover_preview' chunk.book.slug chunk.slug revision %}"
-                       {% else %}
-                            src="{% url 'cover_preview' chunk.book.slug chunk.slug %}"
-                       {% endif %}></p>
-               <p><button id="summary-cover-refresh">{% trans "Refresh from working copy" %}</button></p>
-               </div>
-
-               <h2>
-                       <label>{% trans "Title" %}:</label>
-                       <span data-ui-editable="true" data-edit-target="meta.displayTitle"
-                       >{{ chunk.pretty_name }}</span>
-               </h2>
-        <p><a href="{{ chunk.book.get_absolute_url }}">{% trans "Go to the book's page" %}</a>
-        </p>
-               <p>
-                       <label>{% trans "Document ID" %}:</label>
-                       <span>{{ chunk.book.slug }}/{{ chunk.slug }}</span>
-               </p>
-               <p>
-                       <label>{% trans "Current version" %}:</label>
-                       {{ chunk.revision }} ({{ chunk.head.created_at }})
-               </p>
-               <p>
-                       <label>{% trans "Last edited by" %}:</label>
-                       {{ chunk.head.author }}
-               </p>
-               <p>
-                       <label>{% trans "Link to gallery" %}:</label>
-                       <span data-ui-editable="true" data-edit-target="meta.galleryLink"
-                       >{{ chunk.book.gallery }}</span>
-               </p>
-               <p>
-                   <label>{% trans "Characters in document" %}:</label>
-                   <span id="charcount"></span> (<span id="charcount_pages"></span> {% trans "pages" %}<span id="charcount_untagged">, {% trans "untagged" %}</span>)
-               </p>
-       </div>
-</div>
diff --git a/apps/wiki/templates/wiki/tabs/summary_view_item.html b/apps/wiki/templates/wiki/tabs/summary_view_item.html
deleted file mode 100644 (file)
index ea7ae74..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-{% load i18n %}
-<li id="SummaryPerspective" data-ui-related="summary-view-editor" data-ui-jsclass="SummaryPerspective">
-    <a href="#">{% trans "Summary" %}</a>
-</li>
diff --git a/apps/wiki/templates/wiki/tabs/wysiwyg_editor.html b/apps/wiki/templates/wiki/tabs/wysiwyg_editor.html
deleted file mode 100644 (file)
index f54f3fb..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-{% load i18n %}
-<div id="simple-editor" class="editor" style="display: none">
-    <div id="html-view" class="htmlview">
-    </div>
-
-       <div class="toolbar">
-       {% if not readonly %}
-        <button id="insert-theme-button">
-            {% trans "Insert theme" %}
-        </button>
-        <button id="insert-annotation-button">
-            {% trans "Insert annotation" %}
-        </button>
-               {% endif %}
-        <div class="toolbar-end">
-        </div>
-    </div>
-</div>
diff --git a/apps/wiki/templates/wiki/tabs/wysiwyg_editor_item.html b/apps/wiki/templates/wiki/tabs/wysiwyg_editor_item.html
deleted file mode 100644 (file)
index ec853cd..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-{% load i18n %}
-<li id="VisualPerspective" data-ui-related="simple-editor" data-ui-jsclass="VisualPerspective">
-    <a href="#">{% trans "Visual editor" %}</a>
-</li>
diff --git a/apps/wiki/tests/xslt/auto/auto_test.js b/apps/wiki/tests/xslt/auto/auto_test.js
deleted file mode 100644 (file)
index ab485f5..0000000
+++ /dev/null
@@ -1,139 +0,0 @@
-var jsdom = require("jsdom");
-var $ = require('jquery')(new jsdom.JSDOM().window);
-var fs = require('fs');
-var exec = require('child_process').exec;
-var pd = require('pretty-data').pd;
-var ansidiff = require('ansidiff');
-
-
-eval(fs.readFileSync(__dirname + '/../../../../../redakcja/static/js/wiki/xslt.js') + '');
-
-
-function assertNodesEqual(lhs, rhs, areHTMLNodes) {
-
-    function throwError() {
-        throw new Error(ansidiff.chars(pd.xml(lhs), pd.xml(rhs)));
-    }
-
-    var lhsNode = $(lhs)[0],
-        rhsNode = $(rhs)[0];
-
-    if(areHTMLNodes) {
-        var getMagicAttrsInfo = function(node) {
-            var valuePrefix = 'x-attr-value-';
-            var namePrefix = 'x-attr-name-';
-            var startsWith = function(str, prefix) { return str.substr(0, prefix.length) === prefix; }
-            var isNameAttribute = function(attr) { return startsWith(attr.name, namePrefix); }
-            var isValueAttribute = function(attr) { return startsWith(attr.name, valuePrefix); }
-            var extractId = function(attr) { return attr.name.split('-').pop(); }
-            var temp = {};
-            var toret = {map: {}, magicAttrsList: []};
-            
-            for(var i = 0; i < node.attributes.length; i++) {
-                var attr = node.attributes[i];
-                if(isNameAttribute(attr) || isValueAttribute(attr)) {
-                    var id = extractId(attr);
-                    temp[id] = typeof temp[id] === 'undefined' ? {} : temp[id];
-                    if(isNameAttribute(attr))
-                        temp[id]['name'] = attr.value;
-                    else
-                        temp[id]['value'] = attr.value;
-                    toret.magicAttrsList.push(attr.name);
-                }
-            }
-
-            Object.keys(temp).forEach(function(id) {
-                var pair = temp[id];
-                toret.map[pair.name] = typeof toret.map[pair.name] === 'undefined' ? toret.map[pair.name] = [] : toret.map[pair.name];
-                toret.map[pair.name].push(pair.value);
-            });
-            
-            return toret;
-        }
-    
-        var removeAttrs = function(node, attrsNames) {
-            attrsNames.forEach(function(name) {
-                node.removeAttribute(name);
-            });
-        }
-        
-        var mapsEqual = function(map1, map2) {
-            if(Object.keys(map1).length != Object.keys(map2).length)
-                return false;        
-            var arraysEqual = function(a1, a2) {return a1.slice().sort().join('') === a2.slice().sort().join('');}
-            Object.keys(map1).forEach(function(key) {
-                if(Object.keys(map2).indexOf(key) === -1 || !arraysEqual(map2[key], map1[key]))
-                    return false;
-            });
-            return true;
-        }
-    
-        var lhsMagicAttrsInfo = getMagicAttrsInfo(lhsNode),
-            rhsMagicAttrsInfo = getMagicAttrsInfo(rhsNode);
-        
-        removeAttrs(lhsNode, lhsMagicAttrsInfo.magicAttrsList);
-        removeAttrs(rhsNode, rhsMagicAttrsInfo.magicAttrsList);
-        
-        if(!mapsEqual(lhsMagicAttrsInfo.map, rhsMagicAttrsInfo.map))
-            throwError();
-    }
-    
-    if(!lhsNode.isEqualNode(rhsNode))
-        throwError();
-}
-
-suite('wiki.tests.xslt.auto', function() {
-        
-        var tempFileName = '.temp.xml';
-        var xsltStyleSheetPath = __dirname + '/../../../../../redakcja/static/xsl/wl2html_client.xsl';
-        
-        fs.readdirSync(__dirname + '/data/').forEach(function(fileName) {
-            
-            if(fileName === tempFileName)
-                return;
-            
-            var ext = fileName.split('.').pop();
-            if(ext !== 'html' && ext !== 'xml')
-                return;
-
-            var inputData = fs.readFileSync(__dirname + '/data/' + fileName) + '';
-            
-            if(ext === 'html') {
-                test('[HTML->XML->HTML] ' + fileName, function(done) {
-                    var result = html2text({
-                        element: $(inputData)[0],
-                        stripOuter: false,
-                        success: function(generatedXML) {
-                            fs.writeFileSync(tempFileName, generatedXML);
-                            exec(['xsltproc', xsltStyleSheetPath, tempFileName].join(' ') , {}, 
-                                function(error, stdout, stderr) {
-                                    fs.unlinkSync(tempFileName);
-                                    assertNodesEqual(inputData, stdout, true);
-                                    done();
-                            });
-                        },
-                        error: function(msg){throw msg;}
-                    });
-                });
-            } else if(ext === 'xml') {
-                test('[XML->HTML->XML] ' + fileName, function(done) {
-                    var originalXML = $(inputData);
-                    exec(['xsltproc', xsltStyleSheetPath, __dirname + '/data/' + fileName].join(' ') , {},
-                        function(error, stdout, stderr) {
-                            var generatedHTML = $(stdout);
-                            var result = html2text({
-                                element: generatedHTML[0],
-                                stripOuter: false,
-                                success: function(xmltext) {
-                                    assertNodesEqual(inputData, xmltext);
-                                    done();
-                                },
-                                error: function(msg){throw msg;}
-                            });
-                    });
-                });
-            }
-        });
-});
-
-
diff --git a/apps/wiki/tests/xslt/auto/data/akap.html b/apps/wiki/tests/xslt/auto/data/akap.html
deleted file mode 100644 (file)
index 9b2a3ed..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-<p xmlns="http://www.w3.org/1999/xhtml" x-editable="true" class="akap " x-node="akap">
-    Potrzebne materialy k1asa
-</p>
diff --git a/apps/wiki/tests/xslt/auto/data/big.xml b/apps/wiki/tests/xslt/auto/data/big.xml
deleted file mode 100644 (file)
index de73d5f..0000000
+++ /dev/null
@@ -1,1049 +0,0 @@
-<utwor><liryka_l> 
-
-
-<!-- TRIM_BEGIN -->
-
-
-<strofa>Mistrz rzekł i dodał: «Prawdzie nie skłamałem»./ 
-A na dnie jamy wszyscy potępieńce/ 
-Stanęli, patrzą na mnie gronem całem,/ 
-Zapominając z podziwu o męce./ 
---- «Jak ujrzysz słońce, powiedz Dulcynowi<pr><slowo_obce>Dulcyn</slowo_obce> --- braciszek zakonny; umknął po kryjomu i wstąpił w rolę zapaleńca religijnego, wmawiając w lud łatwowierny, że on jest rzeczywistym apostołem bożym. W r. 1305 w górach Nowary założył sektę znaną odtąd pod imieniem dulcynów. Dulcyn w krótkim czasie zebrał z tysiąc zwolenników około siebie, lecz ścigany przez wojsko biskupa miasta Vercelli schwytany został w górach Nowary razem ze swoją żoną i z nią [w:] 1307 r. żywcem spalony na rynku tegoż miasta.</pr>/ 
-Jeśli tu prędko zstąpić nieochoczy,/ 
-Niech skupi żywność, śniegiem się otoczy;/ 
-Bo jak ci mówię, bez śniegu i głodu,/ 
-Nowarczyk w górach niełatwo go złowi»./ 
-<begin id="b1315850210533-628546177"/><motyw id="m1315850210533-628546177">Duch</motyw>Tak z podniesioną stopą do pochodu,/ 
-Duch Mahometa mówił, potem nogę/ 
-Na dłuż prostując poszedł w swoją drogę./ 
-Duch drugi z krtanią przebitą, kaleki,/ 
-Z nosem rozciętym pod same powieki,/ 
-I z jednym uchem od lewego oka,/ 
-Stanął, z nim całe wstrzymało się grono:/ 
-Duch patrząc na mnie z twarzą zadziwioną,/ 
-Otworzył gębę jak jamę czerwoną,/ 
-Którą broczyła świeża krwi posoka,<end id="e1315850210533-628546177"/>/ 
-I rzekł: «Schodzący tu gościu bez winy,/ 
-Jeśli nie ludzi wielkie podobieństwo,/ 
-Widziałem ciebie pomiędzy Latyny./ 
-Przypomnij sobie Piotra z Medicyny<pr><slowo_obce>Przypomnij sobie Piotra z Medicyny</slowo_obce> --- Medicyna wziął imię od miejsca swego urodzenia w ziemi bolońskiej; warchoł i intrygant, kłócił
-lud ze szlachtą, waśnił władców Rawenny i Rimini.</pr>!/ 
-Idąc z Wercelli smugiem do Markabo<pr><slowo_obce>Idąc z Wercelli smugiem do Markabo</slowo_obce> --- Kraj lombardzki od Wercelli w Piemoncie aż do ujścia rzeki Po, nad którą stał niegdyś zamek Makrabo.</pr>/ 
-<begin id="b1315850272218-1030517085"/><motyw id="m1315850272218-1030517085">Kara, Śmierć</motyw>Ostrzeż ode mnie dwóch najlepszych z Fano,/ 
-Andziolello i Gwido ich miano<pr><slowo_obce>Andziolello i Gwido (...) zdradzi najniegodniej</slowo_obce> --- Malatestino, książę w Rimini, który był na jedno oko ciemnym, zaprosił dwóch najznakomitszych obywateli miasta Fano, Gwidona del Cassero i Angiolela de Carignano do miasteczka Katoliki na ucztę, gdzie mieli się wspólnie naradzać nad pewnymi sprawami. Wysłał po nich łodzie, lecz majtkowie z jego rozkazu w porcie Katoliki obydwu wrzucili do morza. Dante w tej pieśni prorokuje, co się już stało.</pr>,/ 
-Że niespodziane czeka ich męczeństwo./ 
-Gdy zmysł proroczy widzi tu niesłabo,/ 
-Wrzucą ich w morze w porcie Katoliki;/ 
-Bo od Majorki do Cypru zatoki/ 
-Neptun nie widział nigdy takiej zbrodni,/ 
-Gdzie wciąż koczują greckie rozbójniki./ 
-Zdrajca rządzący, chociaż jednooki,/ 
-Krajem, którego duch, co za mną kroczy,/ 
-Nie chciałby nigdy widzieć w żywe oczy,/ 
-Sprosi ich w gości, zdradzi najniegodniej,/ 
-Czym ich uwolni od ślubów ofiary/ 
-Przeciw wiejącym wiatrom od Fokary<pr><slowo_obce>Fokara</slowo_obce> --- góra blisko Katoliki skąd często wiejące wiatry i burze mitrężą żeglugę. Ponieważ zginęli, nie potrzebują już obawiać się tych wichrów.</pr>».<end id="e1315850272218-1030517085"/>/ 
---- «Gdy chcesz,» odrzekłem, «abym tam wysoko/ 
-Mówił o tobie w twoim ziemskim raju,/ 
-Kto ten nieszczęsny, wskaż mi go na oko,/ 
-Któremu było tak gorzko w tym kraju?»/ 
-Do towarzysza duch wyciągnął rękę,/ 
-Na oścież jemu otwierając szczękę,/ 
-Krzyczał: «Patrz teraz, to on, lecz nie mówi:/ 
-On myśl wątpiącą uśpił Cezarowi,/ 
-Twierdząc, że zawsze niebezpiecznie czekać,/ 
-Zamiar dojrzały do czynu odwlekać»./ 
-Jakże przeraził mnie wrażeniem dzikiem,/ 
-Ze swoim w krtani uciętym językiem,/ 
-Ten Kurion<pr><slowo_obce>Kurio</slowo_obce> --- wygnany z senatu rzymskiego jako stronnik i przyjaciel Cezara, z nim się połączył i pierwszy go zachęcił do przejścia Rubikonu, co było powodem wojen domowych. Ma tu odcięty język, którym dał tak złą radę.</pr> niegdyś tak zuchwały w mowie./ 
-Duch drugi obie miał ucięte ręce,/ 
-W zmroku wywijał tępymi ramiony./ 
-Krwią, co z nich ciekła, cały oczerniony,/ 
-I krzyknął, krzyk ten aż mi szumiał w głowie:/ 
-«Przypomnij Moskę<pr><slowo_obce>Przypomnij Moskę</slowo_obce> --- Buondelmonte przyrzekł ożenić się z panną z domu Amidei, gdy tymczasem nagle zmienił swój zamysł i ożenił się z krewną Donatich. Ten postępek rozjątrzył gniew wielu domów spokrewnionych z rodem Amidei. Uberti i Lamberti głośno wołali: ,,Zemścić się, ukarać Buondelmonta!". Starsi radzili roztropność i umiarkowanie: lecz młody Moska wrzący gniewem, radził zabić Buondelmonta, którego sam na koniec przebił puginałem. Ta familijna tragedia dała początek stronnictwom i sporom, jakie przez długie czasy wstrząsały Florencją.</pr>, przebóg, ja to rzekłem:/ 
-»Koniec koroną powinien być czynów!«/ 
-Z tych słów urosły kłótnie florentynów»./ 
---- «I śmierć twej całej rodziny!» dodałem./ 
-On wtenczas z bólu łypiąc okiem wściekłem,/ 
-Odszedł jak gdyby już oszalał w męce./ 
-Ja wciąż na trzodę piekielną patrzałem;/ 
-I to widziałem, czego bez dowodu/ 
-Nie śmiałbym w pieśni opowiadać mojej,/ 
-Gdyby nie dobry nasz świadek, sumienie,/ 
-Które pod zbroją nam czystości swojej/ 
-Serce umacnia, zagłusza zwątpienie./ 
-<begin id="b1315905795971-3302522595"/><motyw id="m1315905795971-3302522595">Duch</motyw>Widziałem, wierzcie na słowo poety,/ 
-Tułów bez głowy, jako inne cienie/ 
-Szedł równie dobrze sporym krokiem chodu,/ 
-A w ręku trzymał swą uciętą głowę;/ 
-Głowa na włosach na obraz latarni/ 
-Zwieszona, z gestem bolesnym męczarni/ 
-Patrzając na nas, mówiła: Niestety!/ 
-Tułów przejrzysty jak szkło kryształowe/ 
-Sam stał się lampą i razem w tej chwili/ 
-Dwoje ich w jednym, jeden w dwojgu byli./ 
-Ile to widmo było rzeczywiste,/ 
-Pan nasz i mściciel wie o tym zaiste!/ 
-Tułów podszedłszy pod szyję mostową,/ 
-Wzniósł w górę rękę z całą swoją głową,<end id="e1315905795971-3302522595"/>/ 
-Ażeby do nas przybliżyć swe słowo,/ 
-Które tak brzmiało: «Gościu nieumarły,/ 
-Patrz, jakie męki na mnie się wywarły,/ 
-Patrz, czy jest większa od mojej tortura?/ 
-Jeśli mną zająć chcesz ciekawość czyją,/ 
-Wiedz, że ja byłem Bertrandem z Bornijo<pr><slowo_obce>Wiedz, że ja byłem Bertrandem z Bornijo</slowo_obce> --- Bertrand z Bornio (Bertram de Born) był obwiniany za to, że między Henrykiem II, angielskim królem, a jego synem jątrzył niezgodę i syna do buntu przeciw własnemu ojcu pobudził. Ponieważ Bertrand przeciw głowie rodziny jej członków buntował, tu za karę nosi własną głowę odciętą od tułowia. Ta głowa służy mu w piekle za latarkę, jak i na ziemi podobną jemu powinna była posługę, ażeby go ochroniła od smutnych następstw jego występku.</pr>/ 
-Dałem złe rady młodemu królowi,/ 
-Jątrzyłem syna przeciwko ojcowi:/ 
-Sam Architofel nie gorszym się wyda,/ 
-Co Absalona jątrzył na Dawida./ 
-Za to żem dzielił, co łączy natura,/ 
-Chodzę tu, przebóg, jak bezgłowa mara;/ 
-I słusznie, jaka zbrodnia, taka kara».</strofa> 
-
-
-
-
-<naglowek_rozdzial>Pieśń XXIX</naglowek_rozdzial> 
-
-
-
-
-
-
-
-
-<akap>(Krąg VIII. Tłumok X. Fałszerze. Alchemiści.)</akap> 
-
-
-
-<strofa>Rozmaitymi tłum ludu ranami/ 
-Tak moje oczy upoił w tej chwili,/ 
-Że pragnął spocząć i trzeźwić je łzami./ 
-«Co tak spoglądasz?» przemówił Wirgili,/ 
-«Czemu tak pilno patrzą twe powieki/ 
-Na tłum tych cieniów tak smutnie kaleki?/ 
-Tegoś nie robił przy drugich tłumokach:/ 
-Gdy chcesz policzyć, duchów tu jest ile,/ 
-Pomyśl; ta jama wykuta w opokach,/ 
-Ma w swym obwodzie dwadzieścia dwie mile./ 
-Księżyc już zaszedł pod nasze podnóże<pr><slowo_obce>Księżyc już zaszedł pod nasze podnóże</slowo_obce> --- Kiedy poeci rozpoczynali swoją pielgrzymkę po piekle, księżyc był w pełni, a więc słońce, kiedy teraz księżyc jest pod ich stopami, stoi ponad ich głową; przeto w tej chwili na wschodniej półsferze jest samo południe.</pr>,/ 
-Odtąd dość krótki (szanuj lot chwil czynnych,)/ 
-Czas dozwolony do pielgrzymki twojej;/ 
-Jeszcze masz widzieć wiele rzeczy innych,/ 
-O których może myśl twa ani roi»./ 
-Odpowiedziałem: «Gdybyś spojrzeć raczył/ 
-Na powód, co tu wzrok mój tak zahaczył,/ 
-Dłużej mi patrzeć pozwoliłbyś może?»/ 
-Wódz szedł, ja za nim z wolna sunąc nogą,/ 
-Dodałem jeszcze, wciąż mówić ochoczy:/ 
-«Tam, skąd utkwione odjąłem me oczy,/ 
-W jamie, co w skał tych zapadła krawędzie,/ 
-Myślę, swej winy płacze duch mój krewny,/ 
-Która tam jego kosztuje tak drogo!»/ 
-Mistrz odpowiedział: «Hamuj pociąg rzewny,/ 
-Dłużej tym duchem nie rozrzewniaj myśli,/ 
-Marz o czym innym, gdzie on jest, niech będzie./ 
-Widziałem jego w chwili, gdyśmy przyszli/ 
-Na łuk mostowy, on cię zauważał,/ 
-Wskazywał, żywo palcem ci pograżał;/ 
-Słyszałem, jak nań jeden duch przeklęty:/ 
-Geri del Bello, po imieniu krzyknął<pr><slowo_obce>Geri del Bello</slowo_obce> --- po matce krewny Dantego; zabił go Sachetti; śmierci jego trzydzieści lat po popełnionym morderstwie pomści jego synowiec.</pr>,/ 
-Lecz tyś był hrabią z Hetfortu zajęty<pr><slowo_obce>Hrabia z Hetfortu</slowo_obce> --- jest to Bertrand z Bornio, o którym wzmianka była w pieśni poprzedniej.</pr>,/ 
-W to miejsce wtedy spojrzałeś, gdy zniknął»./ 
-A ja: «Śmierć jego, której wstyd podziela/ 
-Krew nasza, od nas wygląda mściciela;/ 
-Dlatego, myślę, z uczuciem pogardy,/ 
-Nie mówiąc do mnie, odszedł duch ten hardy,/ 
-Co go w mej duszy jeszcze droższym czyni»./ 
-Tak mówiąc szliśmy do pierwszej przystani,/ 
-Skąd mógłbym widzieć dno innej otchłani,/ 
-Gdyby być mogło świetlej<pe><slowo_obce>świetlej</slowo_obce> --- jaśniej.</pe> w tej jaskini./ 
-Gdym już od mostu stał o kilka kroków,/ 
-<begin id="b1315906189530-2342764147"/><motyw id="m1315906189530-2342764147">Krzywda, Kara</motyw>Gdzie był ostatni klasztor <slowo_obce>Złych Tłumoków</slowo_obce><pr><slowo_obce>Gdzie był ostatni klasztor Złych Tłumoków</slowo_obce> --- Ostatni oddział kręgu ósmego poeta nazywa klasztorem Złych Tłumoków, prawdopodobnie bez głębszej myśli, bo duchy są tam jak wszędzie w piekle zamknięte jak mnichy za furtą klasztorną.</pr>,/ 
-Krzyki boleści, co w mej czułej duszy,/ 
-Ile ich było, razem się zmieniły/ 
-W żelazne strzały, tak serce raniły,/ 
-Tak, że rękoma zatykałem uszy./ 
-W miesiącu sierpniu społem tłum zebrany/ 
-Chorych z Maremmy i Waldikijany<pr><slowo_obce>Waldikiana</slowo_obce> --- okolica błotnista i bardzo niezdrowa dla jej mieszkańców; leży między Arezzo a Kortoną. Oprócz Sardynii, gdzie w środku lata powietrze równie jest niezdrowe, poeta wspomina błota, tak zwane Maremmy, rozciągające się wzdłuż brzegów morskich między Pizą a Sieną.</pr>/ 
-I co w sardyńskich szpitalach się mieści,/ 
-Dałby nam chyba obraz tych boleści./ 
-Z jamy buchały podobne wyziewy,/ 
-Jakimi dysze<pe><slowo_obce>dysze</slowo_obce> --- dziś popr. forma: dyszy.</pe> zgangreniałe<pe><slowo_obce>zgangreniały</slowo_obce> --- toczony gangreną.</pe> ciało.<end id="e1315906189530-2342764147"/>/ 
-I zwróciliśmy krok nasz na brzeg lewy<pr><slowo_obce>I zwróciliśmy krok nasz na brzeg lewy</slowo_obce> --- Poeci znowu zstępują z łuku mostowego na tamę, ażeby mogli, będąc bliżej otchłani, widzieć lepiej, co się w niej dzieje. Czytelnik może często pyta sam siebie: jak Dante w ciemnej nocy piekielnej mógł coś widzieć? Sam poeta wyraźnie tego nie objaśnia; być może, że ogień, który widzieliśmy w różnych miejscach piekła, a mianowicie ten, co oświeca cnotliwych i uczonych mężów pogańskich, ten, który spada na czyniących gwałt przeciw Bogu, ten, co się pali w grobach odszczepieńców religijnych, na koniec ten, w którym fałszywi doradcy są ukryci, tyle światła po wszystkich kręgach piekielnych rozrzuca, że tworzy się jakiś zmrok pół widny, przez który można coś widzieć. A może ci, których przewodnikiem jest rozum (jak tu Dantego prowadzi Wergiliusz, symbol rozumu), zupełną nocą nie są otoczeni i widzą jak w półzmroku.</pr>,/ 
-Który się długo jedną ciągnie skałą;/ 
-<begin id="b1315906247406-1597770989"/><motyw id="m1315906247406-1597770989">Kara</motyw>Spojrzałem w otchłań, gdzie na dnie jej łoża/ 
-Karze fałszerzy sprawiedliwość boża./ 
-Nie mógł smutniejszy być widok w Eginie,/ 
-Gdzie tysiącami chory lud wymierał,/ 
-A mór<pe><slowo_obce>mór</slowo_obce> (daw.) --- zaraza, pomór.</pe> zwierzęta i płazy pożerał;/ 
-Gdzie, jeśli wierzyć na poetów słowo,/ 
-Z nasienia mrówek wylęgły na nowo/ 
-Lud ją zaludnił, zwany Mirmidony,/ 
-Jak widok duchów w tej ciemnej dolinie,/ 
-Jakby stos trupów bezładnie zrzucony./ 
-Ten brzuchem cały do ziemi przypada,/ 
-Ów głowę skłonił na ramię sąsiada,/ 
-Ci na czworakach pełzają jak gady./ 
-A szliśmy milcząc jako dwa niemowy<pe><slowo_obce>dwa niemowy</slowo_obce> --- dziś popr.: dwie niemowy.</pe>,/ 
-Słuchając, patrząc na chore gromady,/ 
-Niezdolne dźwigać bezwładne tułowy<pe><slowo_obce>tułowy</slowo_obce> --- dziś popr. forma: tułowia.</pe>./ 
-Dwóch wzajem na się opartych siedziało<pr><slowo_obce>Dwóch wzajem na się opartych siedziało</slowo_obce> --- Dwaj alchemicy, jako fałszerze złota, siedzą wzajem o siebie oparci, okryci trądem i przez całą wieczność drapią aż do krwi bolące ich strupy.</pr>,/ 
-Strupy im całe cętkowały ciało;/ 
-Jak zgrzebłem żywo pociąga stajenny/ 
-Przed przyjściem pana, choć ziewa półsenny,/ 
-Tak ci po strupach paznokciami wodzą,/ 
-Przez co bolesne swe świerzby łagodzą,/ 
-Przeciwko którym już nie było środka;/ 
-Jak pod kucharskim nożem pstrąg lub płotka/ 
-Miece<pe><slowo_obce>miece</slowo_obce> --- dziś popr. forma: miota.</pe> łuskami, tak strupów kawały/ 
-Spod ich paznokci jak łuska leciały.<end id="e1315906247406-1597770989"/>/ 
-«Ty, co jak garbarz przez smutną konieczność,»/ 
-Rzekł wódz mój, «skórę chropawą pryszczami/ 
-Wyprawiasz, gładząc paznokcia ostrzami,/ 
-Mów, czy się spotkam tutaj z Latynami?/ 
-Oby twój paznokć<pe><slowo_obce>paznokć</slowo_obce> --- dziś popr. forma: paznokieć; tu forma skrócona dla zachowania rytmu jedenastozgłoskowca.</pe> nie stępiał przez wieczność!»/ 
---- «Tutaj w nas obu dwóch Latynów witasz!»/ 
-Rzekł jeden z płaczem, «ktoś ty, co nas pytasz?»/ 
-Mój wódz tak mówił: «Jestem duch i oto/ 
-Schodzę tu razem z żyjącą istotą,/ 
-By całe piekło poznał chodząc ze mną»./ 
-Dwa cienie łamiąc podporę wzajemną,/ 
-Ze drżeniem ku mnie zwrócili się<pe><slowo_obce>Dwa cienie (...) zwrócili się</slowo_obce> --- dziś popr. forma czasownika: zwróciły się.</pe> oba;/ 
-Na wieść tę wszyscy zerwali się społem./ 
-Mistrz rzekł: «Mów teraz, co ci się podoba»./ 
-Rad z przyzwolenia tak mówić zacząłem:/ 
-«Niech pamięć o was czas, co wszystko gładzi/ 
-Wskrzesi od wschodu słońca do zachodu;/ 
-<begin id="b1315906353790-3331885609"/><motyw id="m1315906353790-3331885609">Czary, Grzech, Kara</motyw>Kto wy jesteście, z jakiego narodu,/ 
-Że wśród mąk takich wam tu serce radzi/ 
-Przede mną z chęcią otworzyć się szczerą?»/ 
-Cień rzekł: «Arezzo jest moja kraina<pr><slowo_obce>Cień rzekł: Arezzo jest moja kraina</slowo_obce> --- Griftolino, rodem z miasta Arezzo, alchemik, utrzymywał, że umie latać. Gdy Albero z Sieny, naturalny syn tamtejszego biskupa, udał się doń, aby go tej sztuki nauczył, a to się oczywiście nie udało, wymógł na swym ojcu, że kazał alchemika spalić na stosie jako czarownika.</pr>/ 
-Kazał mnie spalić z Syjeny Albero,/ 
-Chociaż tu inna wtrąciła mnie wina./ 
-Wprawdzie z nim mówiąc napomknąłem żartem,/ 
-Że ze mnie dzielny powietrzny latawiec;/ 
-On człowiek małej głowy a ciekawiec<pe><slowo_obce>ciekawiec</slowo_obce> --- dziś popr.: ciekawski. </pe>,/ 
-Chciał, abym odkrył sztuki tajemnicę;/ 
-A że nie byłem Dedalem w praktyce,/ 
-Spłonąłem za to, żem kumał się z czartem./ 
-A że był ze mnie alchemik imienny,/ 
-Minos niemylny w sędziego pojęciu,/ 
-Wtrącił mnie w Tłumok ostatni z dziesięciu».<end id="e1315906353790-3331885609"/>/ 
-«Oprócz Francuzów,» mówiłem poecie,/ 
-«Więcej próżnego narodu na świecie/ 
-Nie ma zaiste jako lud z Syjeny»./ 
-Wtenczas to słysząc, drugi trędowaty,/ 
-Mówił: «Z nich pierwszym wyjątkiem jest Strikka<pr><slowo_obce>Z nich pierwszym wyjątkiem jest Strikka</slowo_obce> --- Tu poeta ironicznie wyszydza wszystkich głośnych marnotrawców i rozgardiaszów sieneńskich.</pr>,/ 
-Który wydawał pieniądz przyzwoicie,/ 
-Trwonił kapitał, gdy brakło intraty;/ 
-Drugim Nikolo, co pierwszy użycie/ 
-Zbytkowne odkrył wonnego goździka<pr><slowo_obce>użycie zbytkowne odkrył wonnego goździka</slowo_obce> --- Dowiadujemy się tu od poety, że Nikolo pierwszy smakosz sieneński, pierwszy korzenne zaprawy do sosów i pieczeni wprowadził w użycie.</pr>/ 
-Mógłbym wyłączyć inne pasibraty<pr><slowo_obce>inne pasibraty</slowo_obce> --- Za czasów Dantego w Sienie było towarzystwo złożone z dwunastu młodych i bogatych ludzi, którzy sprzedawszy swoje posiadłości ziemskie, złożyli wielki kapitał, ażeby wspólnie go roztrwonić i zarazem żyć wesoło. Kapitał ten, jak mówi Benvenuto d'Imola, składał się z 216 tysięcy florenów, a w przeciągu niespełna roku już go marnotrawcy stracili.</pr>,/ 
-Z tych Asciano winnicę bogatą,/ 
-Wielki las przejadł i Abbagliato/ 
-Dowiódł, że jemu w głowie niepstrokato./ 
-Lecz iżbyś wiedział, kto twoim wykrzykom/ 
-Taki wtór trzyma przeciw sieneńczykom,/ 
-Skieruj twe oczy do mojej figury,/ 
-A w twej pamięci rysy me odżyją./ 
-Ja za pomocą alchemiji złoto/ 
-Rad fałszowałem, jam cień Kapokijo<pr><slowo_obce>złoto rad fałszowałem, jam cień Kapokijo</slowo_obce> --- Kapokio, sieneńczyk, znany osobiście poecie, miał jakoby razem z Dantem uczyć się fizyki i historii naturalnej.</pr>!/ 
-Możesz przypomnieć, że byłem niecnotą/ 
-A przy tym małpą wyborną z natury».</strofa> 
-
-
-
-
-<naglowek_rozdzial>Pieśń XXX</naglowek_rozdzial> 
-
-
-
-<akap>(Ciąg dalszy.)</akap> 
-
-
-<strofa><begin id="b1315906624392-3995347367"/><motyw id="m1315906624392-3995347367">Morderstwo, Grzech</motyw>Gdy za Semelę gorączką obrazy<pr><slowo_obce>Semele</slowo_obce> --- córka Kadmusa, która z Jowisza urodziła Bachusa. Junona przez zazdrość prześladowała za to cały ród Kadmusa, a szczególnie jej siostrę Ino, mamkę Bachusa. Atamas, mąż jej, rozwścieklony gniewem pozabijał jej dzieci. Tak była ukarana nienawiść macosza, z jaką Ino dzieci z pierwszej żony Atamasa prześladowała.</pr>/ 
-Junona wrzała przeciw krwi tebańskiej,/ 
-Spędzając na nich gniew po wiele razy,/ 
-Widząc swą żonę Atamas szatański/ 
-Wchodzącą w progi z dwojgiem małych dzieci,/ 
-Krzyknął jak wściekły: «Rozciągajmy sieci,/ 
-Lwica z lwiątkami w ich przeguby wleci»./ 
-I jedno dziecko, Learkiem nazwane,/ 
-Porwał i cisnął o twardą skał ścianę,/ 
-A matka, myśl tę snadź rozpacz natchnęła,/ 
-Z drugim ciężarem swoim utonęła.<end id="e1315906624392-3995347367"/>/ 
-Kiedy Fortuna twarz swą odwróciła/ 
-Od wielkich Trojan, tak że jednej chwili/ 
-Społem królestwo i króla stracili,/ 
-Hekuba smutna, niewolnica chora,/ 
-Płacząc nad ciałem swego Polidora<pr><slowo_obce>Hekuba smutna (...) nad ciałem swego Polidora</slowo_obce> --- Hekuba, żona Priama, opłakująca zwłoki syna swojego.</pr>,/ 
-Jak pies szalona w swej boleści wyła,/ 
-Tak boleść wszystkie jej zmysły zmąciła!/ 
-<begin id="b1315906686233-136023046"/><motyw id="m1315906686233-136023046">Nienawiść, Kara</motyw>Jednak Tebanie i Trojanie wściekli,/ 
-Choć tyle zwierząt i ludzi wysiekli,/ 
-Nie pastwili się z okrucieństwem takiem:/ 
-Dwa cienie nagie biegły jednym szlakiem,/ 
-Gryząc się w biegu, jak wieprz wszystkim w oczy/ 
-Rzuca się, gdy z swej zagrody wyskoczy./ 
-Jeden z nich, biegnąc, wpadł na Kapokiję,/ 
-W kark jemu pięścią grzmotnął ponad uchem,/ 
-Zwalił, po ziemi ciągnął go za szyję,/ 
-Gracując drogę Kapokija brzuchem.<end id="e1315906686233-136023046"/>/ 
-A Griffolino, z przestrachu wielkiego/ 
-Drżąc, mówił do mnie: «To duch Jana Skiki<pr><slowo_obce>To duch Jana Skiki</slowo_obce> --- Jan Skika, z familii Kawalkantych, rodem florentyńczyk: miał talent naśladowania głosu i gestów swoich znajomych, którego często na złe używał. Będąc przyjacielem Szymona Donata, któremu umarł jego krewny Buoso (zob. Pieśń XXV, gdzie on między złodziejami się znajduje) bez testamentu, a nie mógł po nim brać spadku z powodu, że Buoso zostawił bliższych krewnych, Szymon Donati, ażeby zostać spadkobiercą, przez kilka dni ukrywał ciało zmarłego Buosa, rozgłosiwszy, że jeszcze złożony chorobą oczekuje bliskiego zgonu, a w miejsce zmarłego położył w łóżko swojego przyjaciela Jana Skikę, który udając głos i postać Buosa, zrobił testament, pisząc w nim spadkobiercą po Buosie Szymona Donata, z którym zawarł uprzednio umowę, że w nagrodę dobrze odegranej roli weźmie klacz wielkiej ceny z jego stada.</pr>,/ 
-Tak wszystkich dręczy ten szaleniec dziki»./ 
---- «Jeśli ten drugi duch cię nie uszczyknie<pe><slowo_obce>uszczyknie</slowo_obce> --- dziś popr.: uszczknie; tu forma wydłużona dla zachowania rytmu jedenastozgłoskowca.</pe>/ 
-Zębem lub szponą,» wołałem do niego,/ 
-«Powiedz mi wprzódy, kto on jest, nim zniknie»./ 
-A on: <begin id="b1315906794189-1532356216"/><motyw id="m1315906794189-1532356216">Kłamstwo, Grzech</motyw>«To Myrry starożytnej dusza<pr><slowo_obce>Myrra</slowo_obce> --- córka króla Cypru, winna tej zbrodni, jaką tu opisał poeta, po jej odkryciu przeklęta i wygnana przez ojca, uciekła do Arabii, gdzie długo i póty opłakiwała swój występek, aż od łez i żalu zamieniła się w gumę drzewną od jej imienia nazwaną myrra.</pr>,/ 
-Która do ojca czuła upał żywy,/ 
-Przeciwko prawu miłości uczciwej;/ 
-Pragnąc grzech ukryć w kryjomym niewstydzie,/ 
-Ojca pod cudzą postacią spokusza<pe><slowo_obce>spokusza</slowo_obce> --- dziś popr. forma: kusi; tu forma dokonana, wydłużona dla zachowania rytmu jedenastozgłoskowca.</pe>,/ 
-Podobnie jak duch, który oto idzie!/ 
-Znęcony zyskiem końskich stad królowy,/ 
-Wybieg zaiste cudacki i nowy,/ 
-Zamiast Donata w łóżko się położył,/ 
-Skłamał testament i znów z grobu ożył»./ 
-Gdy znikli z oczu mych ci dwaj okrutni,<end id="e1315906794189-1532356216"/>/ 
-<begin id="b1315906962269-313830910"/><motyw id="m1315906962269-313830910">Duch</motyw>Wnet odwróciłem ciekawe spojrzenie,/ 
-Patrzeć na inne tam leżące cienie./ 
-Jeden cień byłby podobny do lutni<pr><slowo_obce>Jeden cień byłby podobny do lutni</slowo_obce> --- Lutnia ma okrągły wystający brzuch, a cienką szyję; w tym więc podobieństwo do chorego na wodną puchlinę.</pr>, / 
-Gdyby miał otwór jego brzuch obrzydły,/ 
-W miejscu, skąd ciało rozdziela się w widły:/ 
-Puchlina wodna w jego ciele całem/ 
-Zmieniając kształty przez wilgoci chore,/ 
-Rozwarte usta zwiesiła do brody,/ 
-Jako suchotnik, gdy pragnieniem gore.<end id="e1315906962269-313830910"/>/ 
-«Wy, których wita z podziwem ta jama,»/ 
-Cień mówił, «patrzcie na mistrza Adama<pr><slowo_obce>Mistrz Adam</slowo_obce> --- rodem z Brescii, na żądanie hrabiego z Romeny, chcącego fałszerstwem monety poprawić zły stan swojego skarbu, fałszował złote floreny mieszaniną podlejszych metali, za co był na stosie ogniem spalony. Puchlina wodna oznacza chorobliwy stan finansowy tych krajów, co zniżaniem wewnętrznej wartości monety chcą sobie chwilowo dopomóc. Przypomnienie tego, co mistrz Adam miał niegdyś, a czego teraz brak czuje przez własną winę, piękne jest i prawdziwe! Tasso, który często w cudze odziewał się pióra, miał wyraźnie to miejsce przed oczyma w <tytul_dziela>Jerozolimie Wyzwolonej</tytul_dziela>, Pieśń XIII.</pr>!/ 
-Czegom zapragnął, żyjąc wszystko miałem,/ 
-Teraz, niestety, pragnę kropli wody./ 
-Małe strumyki, co żywią nurt Arny,/ 
-Płynąc z pagórków zielonych Kasenty,/ 
-Z rzeźwiącą treścią, przejrzyste aż do dna,/ 
-Ciągną tam oczy tu z wieczności czarnej;/ 
-Bo ich obrazu marzone ponęty/ 
-Więcej mnie trawią jak puchlina wodna./ 
-Tu Sprawiedliwość z swoim sądem w zgodzie,/ 
-Tym samym miejscem, gdziem grzeszył, mnie bodzie,/ 
-<begin id="b1315907016817-1028229828"/><motyw id="m1315907016817-1028229828">Kłamstwo, Grzech, Kara</motyw>By więcej westchnień wydostać z grzesznika./ 
-Tam jest Romena, gdziem oprócz psot wiela/ 
-Fałszował pieniądz z popiersiem Chrzciciela,/ 
-Za co spalono żywcem fałszownika./ 
-Lecz gdybym spotkał tu duchy Gwidona/ 
-I Aleksandra, i obu ich brata<pr><slowo_obce>Gwidon, Aleksander</slowo_obce> --- hrabiowie z Romeny.</pr>,/ 
-Jeszcze bym tego nie mieniał widoku/ 
-Na Fontebrandę, co stoi w mym oku<pr><slowo_obce>Fontebrandą</slowo_obce> --- wspaniała fontanna w Sienie.</pr>./ 
-Już jeden pono tu zstąpił ze świata,/ 
-Jeśli wieść do mnie doszła nieskażona/ 
-Z ust innych cieniów; ale co mi po tem,/ 
-Gdy ja tu leżę jak przybity młotem!/ 
-Gdybym tak lekki był, że zrobić mogę/ 
-W sto lat krok jeden, już poszedłbym w drogę,/ 
-Szukając jego w tej wielkiej przepaści./ 
-Która w obwodzie ma mil jedenaście/ 
-A wszerz pół mili. Zły duch, co w nich siedzi./ 
-Skusił mnie radą tych hrabiów Romeny,/ 
-Abym w mennicy bił takie floreny,/ 
-W których jest najmniej trzy karaty miedzi<end id="e1315907016817-1028229828"/>»./ 
-Rzekłem do niego: «Wskaż tych dwóch na imię,/ 
-Co tam na prawo leżą razem w dymie,/ 
-Jak zimą dymiąc parą z mokrej ręki?»/ 
---- «Tak ich znalazłem,» odpowiedział «wtedy,/ 
-Gdy mnie w tę otchłań wtrącono na męki,/ 
-I wątpię, z miejsca czy ruszą się kiedy?/ 
-To cień świadczącej krzywo Putyfary<pr><slowo_obce>To cień świadczącej krzywo Putyfary</slowo_obce> --- Żona Putifara i Sinon Grek, oboje fałszywie świadczący; ta Józefa, którego chciała wciągnąć do winy, fałszywie oskarżyła przed mężem; a ten fałszywą wieścią o koniu drewnianym, którego Grecy zostawili pod murami Troi, oszukał Trojan. Za to oboje w zgniłej gorączce tarzają się tu po ziemi.</pr>,/ 
-Drugi Grek Sinon, co oszukał Troję;/ 
-W zgniłej gorączce tu leżą oboje,/ 
-Z ciał swych cuchnące wyziewając pary»./ 
-Cień pierwszy zemstę warzący w milczeniu,/ 
-Że śmiał bezczelną nazwać po imieniu,/ 
-Pięścią w brzuch twardy Adama uderzył,/ 
-Który jak bęben huk daleki szerzył,/ 
-Mistrz Adam wzajem dłonią niemniej twardą/ 
-Odbił policzek mówiąc doń ze wzgardą:/ 
-«Choć mi puchlina poruszyć się broni,/ 
-O! do wybitej dość ruchu mam w dłoni»./ 
-Na to cień drugi: «Gdyś na stos wstępował,/ 
-Pewnoś tak żywo ręką nie szermował,/ 
-Lecz miałeś równie, może więcej, żywą/ 
-Rękę, gdy biłeś monetę fałszywą»./ 
-Opuchły tak rzekł: «Nie kłamie twe słowo,/ 
-Lecz gdzieś był z swoją prawdomówną mową,/ 
-Kiedy o prawdę pytano się w Troi?»/ 
---- «Jam fałsz powiedział,» Grek odparł na nowo,/ 
-«A tyś fałszował pieniądz najniegodniej./ 
-Jam winny jednej, a ty mnóstwa zbrodni»./ 
---- «Czy koń drewniany na myśli ci stoi?»/ 
-Cisnął żart mówca, co miał brzuch wydęty,/ 
-«Świat za mną woła: bądź, kłamco, przeklęty!»/ 
---- «Niech ci nawzajem» tak Grek mówił w gniewie/ 
-«Język pragnienie spali jak zarzewie,/ 
-Niech zgniła woda tak twój brzuch wydyma,/ 
-By jak zagroda stał ci przed oczyma»./ 
-A mincarz<pe><slowo_obce>mincarz</slowo_obce> a. <slowo_obce>mincerz</slowo_obce> --- rzemieślnik zajmujący się biciem monet i medali.</pe>: «Bluźnisz na twą gębę całą,/ 
-Bo jeśli pragnę i mam spuchłe ciało,/ 
-Tyś sam w gorączce i gore ci głowa./ 
-Ażebyś lizał Narcyza zwierciadło<pr><slowo_obce>Zwierciadło Narcyza</slowo_obce> --- woda, w której przeglądał się piękny młodzieniec Narcissus.</pr>,/ 
-Krótka by ciebie skłoniła przemowa»./ 
-Gdym był zajęty ich kłótnią zajadłą,/ 
-Mistrz rzekł: «Czas, abyś tę gawiedź porzucił,/ 
-Niewiele braknie, bym z tobą się skłócił»./ 
-Na głos ten jam się do mistrza obrócił,/ 
-<begin id="b1315907201088-3637867090"/><motyw id="m1315907201088-3637867090">Wstyd, Wina, Słowo</motyw>Ogromnym wstydem spłoniony na twarzy;/ 
-Podobny temu, co nieszczęście marzy,/ 
-A marząc życzy, by to, co się śniło,/ 
-Snem się rozwiało, jakby nic nie było./ 
-Chciałem przemówić, wstyd mi uciął słowa,/ 
-Choć wola z winy tłumaczyć się radzi,/ 
-Ze wstydu przed nim stałem jak niemowa./ 
-Mistrz rzekł: «Wstyd mniejszy większe winy gładzi,/ 
-Uspokój siebie i nie patrz tak smutnie:/ 
-Gdy czasem trafisz na podobne kłótnie,/ 
-Gdzie ludzie w mowie nie dosyć są skromni,/ 
-Że, wódz twój, jestem przy tobie, przypomnij!/ 
-<begin id="b1333739937255-1525704821"/><motyw id="m1333739937255-1525704821">Słowo, Mądrość</motyw>Bo chcieć łakomie słuchać lada bredni,/ 
-Chętka ta zdradza umysł dość powszedni<end id="e1333739937255-1525704821"/>».<end id="e1315907201088-3637867090"/></strofa> 
-
-
-
-
-<naglowek_rozdzial>Pieśń XXXI</naglowek_rozdzial> 
-
-
-<akap>(Do kręgu IX. Studnia. Olbrzymi. Anteus osadza poetów na dnie piekła.)</akap> 
-
-
-
-<strofa>Język, co wprzódy zranił mnie do tyla,/ 
-Żem spłonął wstydem, wnet balsam żądany/ 
-Podał i leczył jak włócznia Achilla,/ 
-Co i raniła, i leczyła rany<pr><slowo_obce>włócznia Achilla, co i raniła, i leczyła rany</slowo_obce> --- Achilles zranił króla Telefosa, a przyłożenie do rany tejże włóczni, która ją zadała, uzdrowiło rannego.</pr>./ 
-<begin id="b1315907392685-3050830674"/><motyw id="m1315907392685-3050830674">Dźwięk</motyw>I milcząc szliśmy od tej smutnej jamy,/ 
-Brzegiem krążącej wokoło niej tamy<pr><slowo_obce>Brzegiem krążącej wokoło niej tamy</slowo_obce> --- Dziesięć oddziałów zwanych <wyroznienie>Złe Tłumoki</wyroznienie> z rozmaitymi rodzajami oszustów, jacy w nich się znajdują, zwiedziwszy, teraz idą poeci wokoło kamiennej tamy okrążającej ostatni ten oddział kręgu ósmego; żeby przybliżyć się do dziewiątego kręgu piekła, w którym karani są zdrajcy. Jest to ta studnia, o której poeta napomyka na początku Pieśni XVIII.</pr>;/ 
-Tam nie noc, zmrok był pół nocny, pół dzienny,/ 
-Widziałem tylko szary grunt kamienny,/ 
-Lecz echo za mną dźwiękiem rogu grało,/ 
-Dźwiękiem, co mógłby zagłuszyć trzask grzmotu./ 
-W stronę, skąd dźwięk ten pobrzmiewał za skałą,/ 
-Oczy z uwagą obróciłem całą./ 
-Nie tak straszliwie Roland do odwrotu/ 
-Dął w róg wojenny, gdy z chrześcijan żalem<end id="e1315907392685-3050830674"/>/ 
-Karloman przegrał bój pod Ronsewalem<pr><slowo_obce>Karloman przegrał bój pod Ronsewalem</slowo_obce> --- Karol Wielki (Karloman, Charlemagne, Carolin Magnus) gdy walczył z Maurami z Hiszpanii, zdradzony przez jednego ze swoich wodzów, przegrał bitwę pod Roncewalem. Wtedy słynny rycerz Roland tak potężnie zadął w róg, że dźwięk tego rogu więcej jak w okręgu milowym najwyraźniej słyszano.</pr>/ 
-Podniosłem głowę; nad poziom opoki,/ 
-Zda się z jej gruntu, tłum wieżyc wyrasta,/ 
-Dlatego rzekłem: «Widzę mur wysoki,/ 
-Mistrzu, jakiego to widok jest miasta?»/ 
-A mistrz: «Tyś w błędzie, bo patrzysz z daleka,/ 
-Gdy przyjdziesz bliżej, uzna twa powieka,/ 
-Jak oddalenie fałszuje zmysł wzroku,/ 
-Więc trochę więcej przyspiesz twego kroku»./ 
-Wziął mnie za rękę i czule ją ściska,/ 
-I mówi: «Nim ten przedmiot ujrzysz z bliska,/ 
-<begin id="b1315907539427-2384102178"/><motyw id="m1315907539427-2384102178">Potwór</motyw>Aby mniej dziwnym zdał się, jak widzimy,/ 
-Wiedz, że nie wieże to są, lecz olbrzymy,/ 
-Od stóp po biodra sami i bezludni,/ 
-Wokoło zrębów stoją w wielkiej studni»./ 
-Jak wzrok wśród mglistej uwięzły ciemnoty/ 
-Z wolna odkrywa ukryte przedmioty,/ 
-Gdy mgła spadając ze światłem się zetrze;/ 
-Tak przerzynając to ciemne powietrze,/ 
-W miarę jak szedłem pod studni tej progi,/ 
-Błąd mój się rozwiał, a wzrastał chłód trwogi/ 
-<begin id="b1333740292530-1195838801"/><motyw id="m1333740292530-1195838801">Potwór, Olbrzym</motyw>Jak wieże zamku Monteregijone<pr><slowo_obce>Monteregione</slowo_obce> --- zamek w okolicach Sieny.</pr>/ 
-Tworzą okrągłych jego ścian koronę,/ 
-Tak nad zrąb studni pod same podpasy/ 
-Sterczały wkoło potworne Gigasy,/ 
-A którym zda się jeszcze groził z góry,/ 
-Piorunem Jowisz, gdy brzmi z ciemnej chmury,/ 
-Jeden stał twarzą do nas obrócony,/ 
-Barkami, piersią i dwoma ramiony;/ 
-Zaiste, jeśli natura przestała/ 
-Tworzyć olbrzymy, w tym loicznie<pe><slowo_obce>loicznie</slowo_obce> --- dziś popr.: logicznie.</pe> działa,/ 
-Bo przez to, gwoli świata pokojowi,/ 
-Takich siłaczy odjęła Marsowi./ 
-A gdy bez troski zapładza w swym łonie/ 
-Jeszcze ogromne wieloryby, słonie,/ 
-Łatwo w tym dojrzy ludzka przenikliwość/ 
-Mądrą ostrożność jej i sprawiedliwość./ 
-Bo gdzie się rozum człowieka zespoli/ 
-Z siłą potęgi i chęcią złej woli,/ 
-Tam ludziom opór nigdy się nie uda./ 
-Czaszka tak wielka była wielkoluda,/ 
-Jakby świętego Piotra szyszka w Rzymie<pr><slowo_obce>świętego Piotra szyszka w Rzymie</slowo_obce> --- Szyszka pozłocona, spiżowa, 2 i 1/2 metrów wysoka, zabytek starożytności, znajdowała się wtedy przed kościołem św. Piotra w Rzymie, dzisiaj umieszczona na ostatnim dziedzińcu Pałacu Watykańskiego.</pr>./ 
-I inne kości miał równie olbrzymie:/ 
-Tak, że począwszy od pasa w pół ciała,/ 
-Trzech Fryzów rosłych, daremna ich chluba,/ 
-Ledwo by wzrostem doszli mu do czuba<pr><slowo_obce>Trzech Fryzów rosłych (...) Ledwo by wzrostem doszli mu do czuba</slowo_obce> --- Fryzowie, mieszkający w północnych Niemczech, w średnich wiekach uchodzili za wielkoludów.</pr>;/ 
-Bo jego wzrostu potworna wyżyna,/ 
-Mogła, jak studni kończyły się zręby,/ 
-Do miejsca, człowiek gdzie płaszcz swój zapina,/ 
-Mieć miar trzydzieści wielkiej rzymskiej palmy<pr><slowo_obce>palma rzymska</slowo_obce> --- miara nieco większa od stopy paryskiej.</pr>./ 
-<slowo_obce><begin id="b1333740358445-3959607359"/><motyw id="m1333740358445-3959607359">Słowo</motyw>Rapho Lmaj Amec</slowo_obce>! jakaś mowa brzmiała/ 
-Dzika, nam obca, z jego dumnej gęby,/ 
-Dla której słodsze niestosowne psalmy.<end id="e1333740292530-1195838801"/><end id="e1315907539427-2384102178"/>/ 
-Mój wódz doń mówił: «Nieroztropny duchu!/ 
-Zadmij w róg, niech się rozlega w twym uchu/ 
-Łowczy dźwięk rogu; jeśli tobą miota/ 
-Gniew albo inna namiętność żywota,/ 
-Ulgi twej męce szukaj w jego brzmieniu:/ 
-Patrz, on z twej szyi zwisa na rzemieniu,/ 
-Który ogromną twoją pierś oplata»./ 
-Potem rzekł do mnie: «Patrz, to duch Nemroda<pr><slowo_obce>Nemrod</slowo_obce> --- władca asyryjski, sławny łowca, znajomy z ksiąg starego Zakonu, który budowaniem wieży Babel winien był podstępnego buntu przeciwko Bogu.</pr>,/ 
-Głupi, sam siebie oskarża w swej dumie,/ 
-Że przezeń znikła jedna mowa świata./ 
-Zostawmy jego, tu słów naszych szkoda,/ 
-My go nie pojmiem, on nas nie zrozumie».<end id="e1333740358445-3959607359"/>/ 
-<begin id="b1315907628566-3300871456"/><motyw id="m1315907628566-3300871456">Grzech, Kara</motyw>I szliśmy kołem, zwracając się w lewo./ 
-Z dala sterczało jakieś wielkie drzewo,/ 
-Podchodzim, widzim olbrzyma drugiego,/ 
-Jeszcze był dzikszy i wzrostu większego./ 
-Jaki go siłacz łańcuchem skrępował,/ 
-Nie wiem, lecz obie miał związane ręce,/ 
-Łańcuch od szyi pięć razy zagięty/ 
-Ciało mu ściskał żelaznymi pręty./ 
-«Pyszny, z Jowiszem o władzę szermował<pe><slowo_obce>szermować</slowo_obce> --- walczyć; por. szermierka.</pe>,»/ 
-Tak mój wódz mówił, «a skończył na męce:/ 
-To Efialtes<pr><slowo_obce>Efialtes</slowo_obce> --- ze swoim bratem Otusem, zwaliwszy Pelion i Ossę, górę na górę, wdzierali się po nich do nieba. Młody Apollo przebił go strzałą. Olbrzym ten ręce ma związane, lecz za ich poruszeniem ziemia drży cała.</pr>! w walce z olbrzymami/ 
-Przed nim zadrżeli i bogowie sami,/ 
-Myśląc, że pierwszy drzwi niebios wyłamie;/ 
-Teraz bezwładne to straszliwe ramie».<end id="e1315907628566-3300871456"/>/ 
-A ja do wodza: «Chciałbym na swe oczy/ 
-Widzieć potworny kształt Bryjareusza<pr><slowo_obce>Briareusz</slowo_obce> --- sturamienny olbrzym, syn Urana i Ziemi. <wyroznienie>Anteusz</wyroznienie>, syn Ziemi, która, ledwo wyszedł na świat, obdarzyła go nadzwyczajną siłą. Herkules zmuszonym był go zadusić, ponieważ Anteusz, jako wróg nieubłagany Herkulesa, z nim się wciąż mocował. Olbrzym ten nie ma rąk związanych, ponieważ nie brał udziału w wojnie olbrzymów z bogami.</pr>»./ 
-Wódz rzekł: «Tu bliżej ujrzysz Anteusza./ 
-Nieskrępowany i mówić ochoczy,/ 
-On nas jak piłkę na dno piekła stoczy./ 
-<begin id="b1315908459190-2183327380"/><motyw id="m1315908459190-2183327380">Strach, Zło</motyw>Lecz Bryjareusz dobrze dalej stoi,/ 
-Jak ten podobnie skuty, prócz że dwoi/ 
-Przestrach wrażeniem okropności swojej»./ 
-Nigdy tak wieżą gwałtownie nie chwiała/ 
-Trzęsieniem ziemi poruszona skała,/ 
-Jak Efialtes wstrząsł się na te słowa;/ 
-Wtenczas mnie trwoga przeszyła grobowa,/ 
-Strach by mnie dobił i padłbym bez duchu,/ 
-Gdybym nie widział olbrzyma w łańcuchu./ 
-Anteusz, gdyśmy podeszli doń bliżej,/ 
-Nad zrębem studni stał pięć łokci wyżej.<end id="e1315908459190-2183327380"/>/ 
----<begin id="b1315908511794-3857309908"/><motyw id="m1315908511794-3857309908">Zdrada</motyw>«O ty, co w owej szczęśliwej dolinie<pr><slowo_obce>O ty, co w owej szczęśliwej dolinie</slowo_obce> --- Mieszkanie Anteusza naznacza poeta, według Lukana, w okolicy, w której Hannibala Scypion pokonał.</pr>/ 
-Przez którą imię Scypijona słynie,/ 
-Gdzie Hannibala zmusił do odwrotu,/ 
-Uprowadziłeś do swego namiotu/ 
-Łup z lwów tysiąca; ty, co gdybyś zbrojnie/ 
-Wsparł twoich braci w ich olbrzymiej wojnie,/ 
-Mógłbyś zapewnić tryumf synom ziemi!/ 
-Jeśli nie gardzisz prośbami naszemi,/ 
-Znieś nas, gdzie Kocyt chłód zamarzać zmusza,/ 
-Lecz nie odsyłaj mnie do Tyfeusza<pr><slowo_obce>Tyfeusz</slowo_obce> --- Inny olbrzym.</pr>./ 
-Stąd mój towarzysz, czego chce twa dusza/ 
-(Tylko nam schyl się i nie marszcz tak czoła),/ 
-Sławne twe imię zaniesie do świata;/ 
-On żyw i przed nim jeszcze długie lata,/ 
-Gdy go przed czasem Łaska nie powoła<end id="e1315908511794-3857309908"/>»./ 
-Gdy mowa mistrza zamilkła skończona,/ 
-Olbrzym już wodza wziął w swoje ramiona,/ 
-Których sam Herkul czuł uścisk straszliwy!/ 
-Z ramion olbrzyma rzekł do mnie Wirgili:/ 
-«Chodź, ja do swego przytulę cię łona»./ 
-Tak mnie i wodza Anteusz tej chwili/ 
-Jak jeden ciężar na plecy zarzucił./ 
-<begin id="b1315908542558-1748055252"/><motyw id="m1315908542558-1748055252">Zdrada, Kara</motyw>Patrząc na wieży bolońskiej kąt krzywy<pr><slowo_obce>Patrząc na wieży bolońskiej kąt krzywy</slowo_obce> --- La Carisenda, wieża pochyła w Bolonii, ma sto trzydzieści stóp wysokości,</pr>/ 
-Od strony, kędy ku ziemi się chyli,/ 
-Widz drży, by czasem wiatr ją nie wywrócił:/ 
-Tak mi się wydał olbrzym i tak strwożył,/ 
-Gdy się nachylił i nas obu złożył/ 
-Na dnie otchłani, co razem pożera/ 
-Zdrajcy Judasza duch i Lucyfera;/ 
-Tak pochylony olbrzym w tym momencie/ 
-Podniósł się prosty jak maszt na okręcie.<end id="e1315908542558-1748055252"/></strofa> 
-
-
-
-
-
-<naglowek_rozdzial>Pieśń XXXII</naglowek_rozdzial> 
-
-
-
-<akap>(Krąg IX. Wieczne lody. 1. Kaina. Zdrajcy krewnych. 2. Antenora. Zdrajcy kraju rodzinnego.)</akap> 
-
-
-
-<strofa><begin id="b1315908856530-1720829267"/><motyw id="m1315908856530-1720829267">Poezja</motyw>Gdybym miał dzikie, tak chrapliwe rymy,/ 
-Co by do ciemnej tej studni przystały,/ 
-Na której stoją wszystkie inne skały<pr><slowo_obce>Co by do ciemnej tej studni przystały, na której stoją wszystkie inne skały</slowo_obce> --- Kamienna tama otaczająca krąg ósmy, dźwiga na sobie ciężar wyższych kręgów piekielnych, a szczególnie te skały, które wszerz przez oddziały rozmaite, czyli jamy kręgu ósmego, przechodząc, podróżującym poetom za łuki mostowe służyły.</pr>,/ 
-Wydałbym pełniej całą treść mej myśli;/ 
-Lecz gdy tej władzy w sobie nie widzimy,/ 
-Ważę się podnieść głos nie bez bojaźni./ 
-Nie jest to zamiar jak gra wyobraźni/ 
-Wylęgły w głowie, co dno świata kreśli<pr><slowo_obce>co dno świata kreśli</slowo_obce> --- Według systemu Ptolemeusza niebo ze wszystkimi planetami i gwiazdami krąży wokoło ziemi i dlatego to środkowy punkt ziemi uważa poeta za punkt środkowy całego wszechświata.</pr>,/ 
-Nie czyn języka, co ledwo szczebioce./ 
-Muzy, przyzywam ja wasze pomoce,/ 
-Dajcie mi skrzydła Amfijona lotu<pr><slowo_obce>Dajcie mi skrzydła Amfijona lotu</slowo_obce> --- Amfion zbudował mury tebańskie i siedem bram tegoż miasta. W tej olbrzymiej pracy dopomagały jemu Muzy; w Muzach szukał natchnienia do pieśni, na której dźwięk same kamienie ruszały się z miejsca i wiązały się w mury.</pr>, / 
-Aby nie niższy był wiersz od przedmiotu.<end id="e1315908856530-1720829267"/>/ 
-Mieszkańce miejsca, o przeklęte duchy!/ 
-Które opisać słowami najtrudniej,/ 
-Czemu was owcze nie kryją kożuchy/ 
-W tym chłodnym świecie albo kozie puchy?/ 
-Gdyśmy już byli w zrębach ciemnej studni<pr><slowo_obce>Gdyśmy już byli w zrębach ciemnej studni</slowo_obce> --- W lochu tej studni zdrajcy rozmaici karani są pogrążeniem ich na wieczność w bryłach piekielnego lodu. Najdalej są oni od Boga, źródła wszelkiego ciepła, światła i życia. Nawet łzy ich marzną, skrucha nie ma tu siły uśmierzającej. Lody powstają z rzeki Kocytu, którą tworzą odpływy gorącego Flegetonu, zlodowaciałe wskutek ruchu skrzydeł Lucyfera. Krąg ten dziewiąty podzielony jest na cztery oddziały: pierwszy, najbliższy nazywa się <wyroznienie>Kaina</wyroznienie>, od bratobójcy Kaina tak nazwany, gdzie są karani ci, co zdradzili bliźnich swoich, a szczególnie swoich krewnych. Po nim następuje oddział <wyroznienie>Antenora</wyroznienie>, gdzie spotykamy zdrajców ojczyzny, tak nazwany od trojańskiego Antenora, którego obwiniają, że brał udział w kryjomym porwaniu Palladium tegoż miasta. Trzeci oddział nazywa się <wyroznienie>Ptolomea</wyroznienie> od Ptolomeusza, króla Egiptu, który zdradził zaufanie wielkiego Pompejusza i prawo gościnności w czasie, kiedy był jego gościem. Oddział zaś czwarty nazywa się <wyroznienie>Judeki</wyroznienie> a od Judasza, który zdradził i zaprzedał Chrystusa Pana.</pr>,/ 
-Daleko niżej jak stopy olbrzyma,/ 
-Zrąb jej wysoki gdym mierzył oczyma,/ 
-«Ostrożnie stąpaj!» ktoś rzekł tymi słowy,/ 
-«Abyś omijał stopą nasze głowy»./ 
-Spojrzałem za się, zdziwion nie po mału<pe><slowo_obce>nie po mału</slowo_obce> --- niemało (daw. forma; por. po trochu).</pe>,/ 
-Zmarzłe jezioro leżało pode mną,/ 
-Lśniące jak szyba ogromna kryształu./ 
-Nigdy tak grubym lodem się nie ścina/ 
-Niemiecki Dunaj lub północna Dźwina;/ 
-Gdyby Tabernik albo Pietrapiana<pr><slowo_obce>Tabernik (...) Pietrapiana</slowo_obce> --- Góry, pierwsza w Sklawonii, druga w Toskanii</pr>/ 
-Spadły przypadkiem w tę otchłań podziemną,/ 
-Szyba tych lodów stałaby jak ściana./ 
-Jak żaby skrzecząc wystają znad wody/ 
-W porze, gdy myśli żniwiarka o żniwie,/ 
-Tak cienie blade, skarżące płaczliwie,/ 
-W lód pogrążone sterczały nad lody/ 
-Pół twarzą, gdzie wstyd czerwieni jagody./ 
-Z dzikim hałasem jak klekot bociani/ 
-Brzmiał zgrzyt ich zębów po całej otchłani;/ 
-Skrzepłe ich usta wzrok ponuro toczy,/ 
-Smutek ich serca zdradzały ich oczy./ 
-Gdy spojrzę na dół, pod mymi stopami/ 
-Dwa cienie z sobą zwarły się piersiami,/ 
-Aż czub ich włosów przytykał do czuba./ 
-«Kto wy jesteście?» krzyknąłem. Wtem cienie/ 
-<begin id="b1315909171203-607305320"/><motyw id="m1315909171203-607305320">Cierpienie</motyw>Ukośne ku mnie zwróciły spojrzenie,/ 
-Łzy, co w ich oczach przed chwilą pływały,/ 
-Spadły na rzęsy i chłodem stężały./ 
-Silniej nie ściska deskę z deską śruba,/ 
-Jak potępieńcy zwarli czoła swoje;/ 
-Jak dwa rogami bodące się byki,/ 
-Tak gniew ich obu był wielki i dziki./ 
-Cień, co od mrozu stracił uszu dwoje,/ 
-Schylając głowę rzekł do mnie łaskawie:<end id="e1315909171203-607305320"/>/ 
-«Dlaczego na nas patrzysz tak ciekawie?/ 
-<begin id="b1315909228370-492156331"/><motyw id="m1315909228370-492156331">Kara</motyw>Chcesz wiedzieć, ci dwaj jakiego są rodu<pr><slowo_obce>ci dwaj jakiego są rodu</slowo_obce> --- Aleksander i Napoleon degli Alberti po śmierci ojca robiąc dział między sobą, dwaj bracia, tak się skłócili, że szpadami nawzajem się przebili. Miejsce ich rodzinne leżało nad strumieniem Bisenzio, który przepływa dolinę Falterona między Lukką a Florencją.</pr>?/ 
-Gdzie zdrój Bisencjo wypływa, dolina,/ 
-Własność Albertich, ojczyzną ich była,/ 
-Taż sama, jedna matka ich rodziła./ 
-Nie znajdziesz w całym tym kręgu Kaina/ 
-Godniejszych siedzieć w bryłach tego lodu:/ 
-Nawet ten, którym wzdryga się natura,/ 
-Na wylot włócznią przebity Artura<pr><slowo_obce>Na wylot włócznią przebity Artura</slowo_obce> --- Morderek, syn bajecznego Artura, króla Brytanii, według podania czatował w zasadzce na swojego ojca, ażeby go zabić; lecz ten uprzedził zamach zbrodniarza i włócznią przebił swego syna na wylot, tak że przez otwór rany słońce świeciło.</pr>,/ 
-Ani Kancelieri, ani Maskeroni<pr><slowo_obce>Ani Kancelieri, ani Maskeroni</slowo_obce> --- Fokacia Kancelieri, rodem z Pistoi, odciął rękę swojemu wujowi, a potem go zabił. Maskeroni, florentyńczyk, też był zabójcą swojego krewnego.</pr>./ 
-Jeśliś Toskańczyk, musisz znać, kto oni./ 
-O jeśli głos mój uszu twych nie kazi,/ 
-Wiedz, że ja jestem Kamerione z Pazzi<pr><slowo_obce>Kamicione de Pazzi</slowo_obce> --- zabił Ubertina, swojego krewnego.</pr>,/ 
-Że oczekuję tu jeszcze Karlina<pr><slowo_obce>Karlino de Pazzi</slowo_obce> --- stronnik białych gwelfów; zdradą zdał czarnym gwelfom zamek obronny, leżący nad rzeką Arno za pewną sumę pieniężną.</pr>!»/ 
-Widziałem potem z tysiąc innych twarzy,/ 
-Na pół skostniałych, zsiniałych od chłodu/ 
-Myśl jeszcze z dreszczem o tych lodach marzy!/ 
-Gdy ku środkowi szukamy przechodu<pe><slowo_obce>przechód</slowo_obce> --- przejście.</pe>,/ 
-Gdzie każda ciężkość cięży swoim ciałem,/ 
-W tym wiecznym zmroku z przerażenia drżałem./ 
-Nie wiem, przypadkiem czy wyższym zrządzeniem/ 
-Śród głów sterczących stawiąc<pe><slowo_obce>stawiąc</slowo_obce> --- dziś popr. forma: stawiając.</pe> stopy z trwogą,/ 
-Jednemu na twarz nastąpiłem nogą.<end id="e1315909228370-492156331"/>/ 
-«Za co mnie depczesz?» rzekł duch z bólu blady,/ 
-«Czy mścisz się na mnie Montaperto zdrady?»/ 
-«Czekaj mnie, mistrzu,» mówiłem z wzruszeniem;/ 
-«Chcę mą wątpliwość objaśnić z tym cieniem,/ 
-Potem, jak zechcesz, jam śpieszyć gotowy»./ 
-Wódz stanął, a jam wyzwał do rozmowy/ 
-Ducha, co bluźnił, nie szczędząc klątw długich:/ 
-<begin id="b1315910532405-1765907429"/><motyw id="m1315910532405-1765907429">Kłótnia</motyw>«Powiedz, kto jesteś, ty, co fukasz drugich?»/ 
---- «Ty sam kto jesteś? Za co w Antenorze/ 
-Idąc, twe stopy drugich twarze depcą?/ 
-Za co naciskasz krokiem tak bolącym,/ 
-Za ciężkim, nawet gdybyś był żyjącym?»/ 
---- «Ja żyję,» rzekłem, «miło ci być może,/ 
-Gdy kochasz sławę lub wspomnienie skromne,/ 
-Wśród wielu imion twe imię przypomnę»./ 
-On na to: «Odejdź, natrętny pochlebco!/ 
-Pochlebne słowa, żal twego zachodu,/ 
-Nic nas nie grzeją na tych falach lodu»./ 
-Wziąwszy go za kark, rzekłem: «Jak cię zową<pe><slowo_obce>zową</slowo_obce> --- dziś popr. forma 3 os. lm: zwą.</pe>?/ 
-Mów, bo wnet łysą zaświecisz mi głową»./ 
---- «Rwij włos, nie powiem tobie, jak się zowię,/ 
-Choćbyś sto razy deptał mi po głowie»./ 
-Za czub schwyciłem jego w mgnieniu oka/ 
-I wyrywałem z czaszki włos garściami;/ 
-On słowa żadnej skargi nie wyrzucił,/ 
-Tylko wył dziko i oczy wywrócił.<end id="e1315910532405-1765907429"/>/ 
-Drugi cień krzyknął: «Co tobie, mój Bokka<pr><slowo_obce>Co tobie, mój Bokka</slowo_obce> --- Bocca degli Abbati; na początku bitwy pod Montaperti nad rzeką Arbią podstępem uciął rękę gwelfowi, Jakubowi Pazzi, który niósł sztandar stronnictwa; gwelfowie przerażeni upadkiem swojego sztandaru, który ich do boju prowadził, poszli w rozsypkę i bitwę przegrali. Dante, chociaż gibelin, potępia ten czyn nieszlachetny.</pr>?/ 
-Ty wyjesz, nie dosyć ci zgrzytać zębami,/ 
-Jakiż cię dręczy szatan tak zawzięty?»/ 
---- «Teraz,» mówiłem, «milcz, zdrajco przeklęty!/ 
-Abyś twe imię powiedział, nie proszę,/ 
-Sam je na wieczną twą hańbę ogłoszę»./ 
-A on: «<begin id="b1315910600773-2693856363"/><motyw id="m1315910600773-2693856363">Zbrodnia, Grzech, Kara</motyw>Baj sobie, co masz na języku,/ 
-Lecz nie zapomnij, idąc z Antenory,/ 
-Tego, co język miał w mowie za skory<pe><slowo_obce>skory</slowo_obce> --- szybki, prędki.</pe>;/ 
-Tu złoto Franków wtrąciło młokosa,/ 
-Możesz powiedzieć: widziałem Buosa<pr><slowo_obce>Buoso da Duera</slowo_obce> --- rodem kremończyk, przekupiony przez generała francuskiego, Gwidona de Montfort, dał mu przejść przez rzekę Oglio.</pr>/ 
-Tam, gdzie grzeszników jest w lodach bez liku./ 
-Gdy, kto jest więcej, spytają i za co?/ 
-Patrz, oto stoi Bekeria ladaco<pr><slowo_obce>Bekeria</slowo_obce> --- Bekeria, rodem z Pawii, opat Walombrozy; gdy odkryto spisek, za pośrednictwem którego miał Florencję oddać w ręce gibelinów, głowę mu ścięto.</pr>/ 
-Któremu głowę Florencyja ścięła,/ 
-A trochę dalej: Gianni, Ganello,/ 
-Otwierający wrogom Tribaldello<pr><slowo_obce>A trochę dalej: Gianni, Ganello, (...) Tribaldello</slowo_obce> --- Wszyscy ci trzej zdrajcy swojego kraju.</pr>/ 
-Faencę, kiedy snem głuchym usnęła»./ 
-Gdy z miejsc okropnych szukamy przechodu,/ 
-Dwóch potępieńców ujrzałem w parowie,/ 
-Wyższy niższemu głową legł na głowie;/ 
-A jak łakomie szarpiemy chleb z głodu,/ 
-Tak on zatopił kły w ciało sąsiada,/ 
-Tam kędy czaszka do barków przypada./ 
-Nie z takim gniewem Tydej zemstą ślepy<pr><slowo_obce>Tydej zemstą ślepy</slowo_obce> --- Tydeusz podczas wojny tebańskiej wyzwał Menalipa na włócznie i obaj ranili siebie śmiertelnie; Tydeusz, według Stacjusza, konając, z wściekłością gryzł czaszkę swojego wroga, który tylko co skonał.</pr>/ 
-Menalipowej głowy gryzł czerepy,/ 
-Jak on swą zdobycz żuje i wysysa.<end id="e1315910600773-2693856363"/>/ 
-«Człowieku,» rzekłem, «co paszczą tygrysa/ 
-Mścisz się nad wroga nienawistną głową,/ 
-Powiedz mi, jakie masz zemsty powody?/ 
-A ja ci moją odpłacę wymową,/ 
-Kiedyś, pomiędzy ziemskimi narody,/ 
-Jeśli mnie Pan Bóg żywcem stąd wydźwignie,/ 
-A język w ustach moich nie zastygnie».</strofa> 
-
-
-
-
-
-<naglowek_rozdzial>Pieśń XXXIII</naglowek_rozdzial> 
-
-
-
-
-
-<akap>(Krąg IX. 2. Antenora --- ciąg dalszy. 3. Ptolomea. Zdrajcy przyjaciół.)</akap> 
-
-
-
-<strofa><begin id="b1315910834677-1850152533"/><motyw id="m1315910834677-1850152533">Duch, Kara</motyw>Od strawy dzikiej oderwał paszczękę/ 
-Ów potępieniec i krew z ust ocierał/ 
-Włosami czaszki, której mózg pożerał./ 
-I mówi: «Srogą chcesz odnawiać mękę,/ 
-Serce mi pęka, nim usta otwieram./ 
-Lecz gdy ze słów mych jak z nasion dojrzeje/ 
-Hańba dla zdrajcy, którego pożeram,<end id="e1315910834677-1850152533"/>/ 
-Słuchaj, wypowiem, wypłaczę me dzieje./ 
-Nie wiem, kto jesteś, przez jaki cud nowy/ 
-Zaszedłeś do nas, lecz po dźwięku mowy/ 
-Poznaję w tobie Włocha, florentyna./ 
-Widzisz przed sobą hrabię Ugolina<pr><slowo_obce>Hrabia Ugolino</slowo_obce> --- pochodził ze starożytnej familii pizańskiej hrabiów della Gherardesca. Będąc podestą i naczelnikiem siły zbrojnej w Pizie, podniósł tę rzeczpospolitą do szczytu potęgi i sławy; potem gdy przegrał bitwę morską z genueńczykami, dla podtrzymania dawnej potęgi swojego kraju wiązał się traktatami z Florencją, której wskutek wzajemnej umowy kilka mało znaczących zamków ustąpił. Zazdrosny jego władzy, a więcej jeszcze sławy długoletnim rządem Ugolina nabytej, arcybiskup Rugieri posądził go o zdradę stanu; potem wsparty współdziałaniem hrabiów Gwalandi, Lafranki i Sismondi ze zbuntowanym ludem natarł na straż przyboczną Ugolina, uwięził go z dwoma synami i dwoma wnukami, a wszystkich zamknął w wieży na placu zwanym <wyroznienie>Degli Anziani</wyroznienie>. Bramę więzienia zamurowawszy, klucze od niej kazał wrzucić w rzekę Arno. Wieża ta od głodowej śmierci Ugolina nosi nazwę <wyroznienie>Wieży głodu</wyroznienie>. Było to 1289 r.</pr>/ 
-A ten, co teraz jest mej zemsty łupem,/ 
-Zwał się Rudżieri, był arcybiskupem./ 
-Jak mnie w zdradzieckie usidlono słowa,/ 
-Jak nieostrożnie wpadłem w jego ręce,/ 
-Nie warto mówić, bo rzecz nie jest nowa./ 
-Lecz o mym zgonie, o mej strasznej męce,/ 
-Jeśli nikt wieścią uszu twych nie skaził,/ 
-Słuchaj i osądź, czy on mnie obraził./ 
-<begin id="b1315910929473-1779874050"/><motyw id="m1315910929473-1779874050">Śmierć, Grzech, Kara, Głód</motyw>Jest w głębi wieży podziemna pieczara,/ 
-Sławna mym zgonem; dziś może w niej jęczy/ 
-Na nowo jaka niewinna ofiara./ 
-Tam okiem witym z żelaznych obręczy,/ 
-Widziałem mnogich księżyców oblicze,/ 
-Aż mnie raz we śnie przywidziana mara/ 
-Zdarła przyszłości chmury tajemnicze./ 
-Przyśniło mi się, że biskup zawzięty/ 
-Polował wilka z małymi wilczęty,/ 
-Na owej górze, co wzniosłymi szranki/ 
-Z pizańską ziemią i Lukką graniczy<pr><slowo_obce>Na owej górze, co wzniosłymi szranki z pizańską ziemią i Lukką graniczy</slowo_obce> --- Góra San Gitiliano na pograniczu Lukki i Pizy. Tam właśnie uwięziono Ugolina. Polityczna działalność Ugolina, w której ocenę tu bliżej wchodzić nie możemy, oczom wielu przedstawiała się jako zdrada kraju. Dlatego umieścił go Dante, który widocznie podzielał takie zdanie, tu, w Antenorze.</pr>./ 
-Już chudą psiarnię zemkniono ze smyczy,/ 
-Hrabia Gwalandi, Sismondi, Lanfranki/ 
-Szczują na czele, zdobycz będzie łatwa:/ 
-Już wilk znużone zatrzymuje kroki,/ 
-Upada wreszcie i ojciec, i dziatwa:/ 
-I widzę kłami rozprute ich boki./ 
-Budzę się! Jeszcze noc nie zeszła z nieba,/ 
-Już moje dziatki, wspólniki niewoli,/ 
-Szlochają przez sen i wołają: »chleba!«./ 
-O! Jeśli dotąd serce ci nie boli,/ 
-Kiedy pomyślisz, co się we mnie działo/ 
-I co me serce nadal przeczuwało,/ 
-Jeśli nie płaczesz, któż ci łzy wyciśnie?/ 
-Budzą się dzieci, wkrótce chwila błyśnie,/ 
-W której nam zwykle udzielano strawy,/ 
-Lecz na sen pomnąc truchlałem z obawy./ 
-Wtem z bram więzienia łoskot mnie doleci,/ 
-Zamurowano! --- spojrzałem na dzieci,/ 
-Spojrzałem z niemej wyrazem rozpaczy;/ 
-A w głębi serca czułem mróz jak w grobie./ 
-Gwido mój mały wołał: »Co to znaczy,/ 
-Tak dziko patrzysz? Ojcze mój, co tobie?«/ 
-Nie mogłem mówić, ni łzy z oczu dostać,/ 
-Milczałem długo --- aż do nocy końca./ 
-Nazajutrz do nas zbłądził promyk słońca/
-I w twarzach dzieci ujrzałem mą postać./ 
-Natenczas z bólu gryzłem obie ręce./ 
-Synowie myśląc, że mnie głód tak pali,/ 
-Łamiąc rączęta ze łzami wołali:/ 
-»Ojcze kochany, ulżyj twojej męce,/ 
-Zjedz twoje dzieci, tyś nas ubrał w ciało,/ 
-Tobie nas biednych rozebrać przystało«. ---/ 
-Musiałem milczeć i ból w sobie morzyć,/ 
-Wkrótce i mowa w ustach nam zamarła!/ 
-Jęczeć nie śmiałem, by dzieci nie trwożyć./ 
-O ziemio, czemuś ty nas nie pożarła!/ 
-Weszło czwartego dnia światło zabójcze,/ 
-Anzelmek mały przywlekł się pod nogi/ 
-I przerażony wolał: »Ojcze drogi!/ 
-Ach! Czemu ty nas nie ratujesz, ojcze?«/ 
-Wołał i skonał! --- Jak mnie tu widzicie,/ 
-Tak ja widziałem wszystkie dzieci moje/ 
-Jedno po drugim --- wszystkich było troje,/ 
-Wszystkie u nóg mych zakończyły życie./ 
-Od zwłok jednego do drugiego biegłem,/ 
-Ślepy na trupach potknąwszy się ległem./ 
-Dzień jeszcze siódmy do słońca zachodu,/ 
-Krzyczałem z żalu, a na koniec --- z głodu,/ 
-Bo głód był jeszcze sroższy od żałości<pr><slowo_obce>Krzyczałem z żalu (...) sroższy od żałości</slowo_obce> --- Cały ten ustęp głodowej śmierci Ugolina i jego dzieci dziwnie jest piękny i dramatyczny. Poeta tu jak w ustępie Franczeski umiał w porę zakląć okropność i na tym właśnie cała tajemnica dramatycznej sztuki zawisła. Wielu krytyków rozbiorowi tego ustępu Ugolina pod względem estetycznym i poetycznym swoje pióra poświęciło.</pr>»./ 
-Skończył i dziko wywróciwszy oczy,/ 
-Na nowo usta w krwawą czaszkę broczy,/ 
-I jak pies zębem zgrzytając, rwie kości. <end id="e1315910929473-1779874050"/>/ 
-O Pizo! hańbo pięknej ziemi włoskiej,/ 
-Kędy <slowo_obce>si</slowo_obce> dźwięczy<pr><slowo_obce>Kędy ,,si" dźwięczy</slowo_obce> --- Wyrażenie powyższe odnosi się do tego, że w języku włoskim <slowo_obce>si</slowo_obce> znaczy ,,tak".</pr> tak miękkimi głoski;/ 
-Gdy cię nie karzą leniwe sąsiady,/ 
-Niechaj się nagle wzruszą z swej posady/ 
-Sąsiednie wyspy, Kapraja, Gorgona<pr><slowo_obce>Kapraja, Gorgona</slowo_obce> --- małe wyspy blisko ujścia Arna do morza.</pr>,/ 
-Zahaczą Arnę, gdzie jej ujście kona;/ 
-Niech tak szeroko roztoczy swe tonie,/ 
-Aż wszystkich twoich mieszkańców pochłonie./ 
-Bo jeśli wrogom Ugolino hrabia/ 
-Zdał twoje zamki i winien tej zdrady,/ 
-O nowe Teby! Cóż twa złość wyrabia?/ 
-Jakaż twą zemstę czarna pamięć szpeci,/ 
-Głodem niewinne morzyć jego dzieci!/ 
-<begin id="b1315911041380-177550501"/><motyw id="m1315911041380-177550501">Duch, Cierpienie</motyw>Widziałem drugich potępieńców z bliska<pr><slowo_obce>Widziałem drugich potępieńców z bliska</slowo_obce> --- Tu poeci wstępują w trzeci oddział tegoż kręgu, zwany <slowo_obce>Ptolomea</slowo_obce>, gdzie są karani ci, co zdradzili swoich krewnych i przyjaciół.</pr>,/ 
-Jak lód ich swymi bryłami naciska;/ 
-Nie stali w lodach, lecz wznak wywróceni/ 
-Na lodowatej leżeli przestrzeni./ 
-Tam łza zamarza w chwili, gdy wybłyska,/ 
-Boleść jak robak po ich wnętrzach toczy,/ 
-Bo jej nie mogą wypłakać przez oczy./ 
-Jak hełm z kryształu, skrzepła łez powłoka/ 
-Kryje pod rzęsą całą wklęsłość oka./ 
-Choć jak stwardniała z mrozu skóra muła/ 
-Twarz moja prawie stała się nieczuła,/ 
-Wiatr jakiś, czułem, obwiał moje ciało.<end id="e1315911041380-177550501"/>/ 
-«Mistrzu mój,» rzekłem, «mów, co tu powiało?/ 
-Czy wiatr w tym chłodzie jeszcze nie zastygnął?»/ 
---- «Dowiesz się wkrótce, skąd wiatr aż tu śmignął,»/ 
-Mówił, «przyczynę gdy wzrok twój wyśledzi,/ 
-Oko wyręczy głos mej odpowiedzi»./ 
-<begin id="b1315911249556-852926870"/><motyw id="m1315911249556-852926870">Cierpienie, Duch</motyw>Z tych nieszczęśliwych jeden tymi słowy/ 
-Do nas przemówił z skorupy lodowej:/ 
-«Dusze uwięzłe w swego grzechu matni,/ 
-Który was wtrąca aż w ten krąg ostatni,/ 
-Zerwijcie z twarzy mej twardą zasłonę,/ 
-Ulżyjcie bólem serce przepełnione,/ 
-Niechaj wyleję choć jedną łzę ciepłą,/ 
-Bo już mi oko i serce zakrzepło».<end id="e1315911249556-852926870"/>/ 
-A ja: «Chcesz, abym ulżył ci w cierpieniu,/ 
-Kto jesteś, nazwij siebie po imieniu;/ 
-Gdy nie usunę twoich łez przeszkodę,/ 
-Bodajbym w lodach tych na wieki siedział!»/ 
-«Brat Alberigo jestem» odpowiedział<pr><slowo_obce>Brat Alberigo</slowo_obce> --- rodem z Faency, jeden z towarzystwa braci wesołych (zob. uwagę do pieśni XXIX), zaprosił krewnego swego z synkiem na ucztę i w chwili, kiedy podawano owoce i jagody, kazał ich zamordować. Stąd urosło włoskie przysłowie: ,,On kosztował owoców brata Alberigo".</pr>,/ 
-Zły owoc z mego wyrasta ogrodu/ 
-I tu za figę mam daktyl w nagrodę<pr><slowo_obce>I tu za figę mam daktyl w nagrodę</slowo_obce> --- Ironia. Daktyl jako owoc z cudzej krainy uchodził za mający wartość większą od krajowej figi.</pr>»./ 
-<begin id="b1333751257040-1120163977"/><motyw id="m1333751257040-1120163977">Zdrada, Piekło, Szatan, Śmierć, Upiór</motyw>--- «Czy już umarłeś?» rzekłem drżąc od chłodu,/ 
-A on: «Nic nie wiem, odkąd tu drętwieję,/ 
-Co z moim ciałem na świecie się dzieje./ 
-Bo Ptolomea ma te przywileje,/ 
-Że często dusza wpada w nią niebacznie,/ 
-Nim Parka przędzę rwać dni naszych zacznie./ 
-A gdybyś chętniej zdjął lód z moich powiek,/ 
-Wiedz, że gdy zdrady dopuści się człowiek<pr><slowo_obce>Wiedz, że gdy zdrady dopuści się człowiek</slowo_obce> --- Z dziwną ścisłością logiczną oznaczony tu stosunek między karą a występkiem. Kto zdradza tego, kto nam ufa, tego dusza w tej chwili staje się łupem kar piekielnych, do których jeszcze za życia wyrzuty sumienia, żal bezowocny i pogarda samego siebie wcześnie przygotowują. A ponieważ wynikająca stąd rozpacz zdrajcy często pędzi go na rozdroże jeszcze dzikszych i nikczemniejszych namiętności, przeto się zdaje, że w jego ciele już nie ludzka dusza, ale szatan zajął gospodę.</pr>,/ 
-Ciało dyjabłu odkazuje dusza,/ 
-Który nim rządzi, włada najzupełniej,/ 
-Nim się czas kary cielesnej wypełni;/ 
-Dusza zaś wpada aż w tę chłodną studnię./ 
-Może wam jeszcze jawi się w swym ciele/ 
-Cień, co tu ze mną w tych lodach się rusza:/ 
-Patrz, Branka d'Oria! Już lat przeszło wiele<pr><slowo_obce>Branka d'Oria</slowo_obce> --- zabił w czasie obiadu teścia swego, Michała Zankę, którego wyżej spotkaliśmy w smolnym jeziorze. Morderca ten rodem był genueńczyk.</pr>,/ 
-Odkąd tu siedzi, wśród brył tego lodu;/ 
-Musiałeś kiedyś znać jego za młodu»./ 
-«Kłamiesz!» doń rzekłem «lub świadczysz obłudnie,/ 
-Bo Branka d'Oria nie umarł, on żyje/ 
-Jeszcze na ziemi, dobrze je i pije»./ 
-On odpowiedział: «W jamie <slowo_obce>Złych Tłumoków</slowo_obce>,/ 
-Tam gdzie widziałeś war smolnych potoków,/ 
-Mógł jeszcze nie być cień Sanchy Michała/ 
-W chwili, gdy Branka d'Oria szatanowi/ 
-Ustąpił swego pomieszkanie ciała/ 
-Jako wiernemu zdrady spólnikowi./ 
-Teraz gdym całą ufność w tobie złożył,/ 
-Otwórz me oczy!» Jam ich nie otworzył,/ 
-Bo względem jego nieszczerość tą razą/ 
-Nie była żadną szczerości obrazą./ 
-Wrogi cnót wszystkich, o genueńczyki!/ 
-Wstyd wam, pomiędzy takimi grzeszniki/ 
-Jednego ziomka waszego spotkałem,/ 
-Który, co czyny jego świadczyć muszą,/ 
-Na dno Kocytu pogrążył się duszą,/ 
-Gdy jeszcze życie kłamie swoim ciałem.<end id="e1333751257040-1120163977"/></strofa> 
-
-
-
-<naglowek_rozdzial>Pieśń XXXIV</naglowek_rozdzial> 
-
-
-
-
-<akap>(Krąg IX. 4. Judekka. Najwięksi zdrajcy: Judasz, Brutus, Kasjusz, Szatan. Powrót do światła.)</akap> 
-
-
-
-
-
-<strofa>«<wyroznienie>Vexilla regis prodeunt inferni</wyroznienie><pr><slowo_obce>Vexilla regis prodeunt inferni</slowo_obce> --- co znaczy: Oto zbliżają się sztandary piekielnego króla. Słowa te (początek hymnu śpiewanego w Wielki Piątek) w tekście oryginału są po łacinie.</pr>/ 
-Wprost ku nam! Jeśli widzisz za pomroką»/ 
-Mistrz mówił, «patrzaj, wytężaj twe oko»./ 
-Kiedy noc naszą półsferę zaczerni/ 
-Lub ciemny tuman przedmioty powleka,/ 
-Myślim, że widzim młyn wietrzny z daleka;/ 
-Tak, zdało mi się, oko me postrzegło/ 
-Stojącą jakąś budowę odległą./ 
-<begin id="b1333751380702-1720694009"/><motyw id="m1333751380702-1720694009">Piekło, Szatan, Zima</motyw>Wtenczas od wiatru szukając ochrony,/ 
-Stanąłem za mym wodzem, bo zasłony/ 
-Nie było innej; tam wśród wiecznej zimy,/ 
-Już byłem w miejscu, gdzie cienie widziałem<pr><slowo_obce>Już byłem w miejscu, gdzie cienie widziałem</slowo_obce> --- Poeci wchodzą już w ostatni oddział kręgu ostatniego, nazwany od Judasza: <wyroznienie>Judykka</wyroznienie>, gdzie karani są ci, co dopuścili się zdrady przeciw swoim dobroczyńcom. W środku tego oddziału i całego wszechświata, spotykamy czterech głównych zdrajców tego rodzaju: Disa czyli Lucyfera, naczelnika zbuntowanych aniołów, Judasza Iskariotę, Kasjusza i Brutusa.</pr>,/ 
-A co z przestrachem wpisuję w te rymy/ 
-Oblane lodem jak ździebło kryształem./ 
-Ten w lodach leży na wznak rozciągnięty./ 
-Ci prosto stoją, drudzy wspak na głowie,/ 
-Ten łukiem twarz swą nagina do pięty./ 
-Gdyśmy do tyla zaszli w to pustkowie,/ 
-Że już mojemu mistrzowi się zdało,/ 
-Pokazać szpetne, niegdyś piękne ciało,/ 
-Zwrócił się do mnie i mówił z powagą/ 
-«Oto Lucyfer, oto krąg przeklęty!/ 
-Teraz się cały uzbrajaj odwagą».<end id="e1333751380702-1720694009"/>/ 
-<begin id="b1315911559022-544613790"/><motyw id="m1315911559022-544613790">Strach</motyw>Jakiem ja wtedy osłabnął i skolał<pe><slowo_obce>skolał</slowo_obce> --- skołowaciał; był niby zamieniony w kołek.</pe>,/ 
-Mój czytelniku, zamilczeć bym wolał,/ 
-Pod piórem moje zastygłoby słowo./ 
-Jeśli najmniejszy masz kwiat wyobraźni,/ 
-Wyobraź sobie, jak dręczy i drażni/ 
-Stan, w jakim całą duszą się zawarłem,/ 
-Zda się, pół żyłem, na poły umarłem.<end id="e1315911559022-544613790"/>/ 
-<begin id="b1315911576539-159505112"/><motyw id="m1315911576539-159505112">Szatan</motyw>Król piekielnego państwa jakby kawał/ 
-Głazu nad lody pół piersią wystawał;/ 
-Jak wzrost mój dosyć ogromny, nie kłamię,/ 
-Tak wielkie było jego jedno ramię./ 
-Zważ, jaka całość mogła być niemała,/ 
-Zastosowana do tej części ciała./ 
-Jeśli tak piękny był, jak teraz szpetny,/ 
-Kiedy od Stwórcy odwrócił wzrok świetny,/ 
-Grzew, wszelki zakał musi iść od niego./ 
-Dziw! Głowa jego kształtu potwornego,/ 
-Na trzech obliczach razem osadzona:<pr><slowo_obce>głowa jego kształtu potwornego, na trzech obliczach razem osadzona</slowo_obce> --- Dis albo Lucyfer, pierwszy rodzic grzechu. Widzimy go tutaj jako ukaranego i jako narzędzie kary. Ma on trzy głowy, które podług wykładu komentatorów Dantego symbolem są trzech ówcześnie znajomych części świata, a razem oznaczają powszechność grzechu i panowanie Lucyfera na ziemi. Barwa czerwona ma oznaczać Europę (której mieszkańcy mają cerę rumianą), żółta Azję, czarna Afrykę. Inni rozumieją przez potrójną barwę jego twarzy gniew, łakomstwo i lenistwo; wierzch zaś, czyli czub głowy, ma oznaczać pychę, przez którą szczególnie Lucyfer panowanie swoje rozszerzył i ugruntował.</pr>/ 
-Pierwsza twarz była jako żar czerwona,/ 
-Dwie zaś policzkiem do pierwszej przypadły;/ 
-Obie na środku dwóch ramion usiadły,/ 
-Schodząc się z sobą aż pod wierzchem głowy;/ 
-Oblicze prawe biało-żółtej barwy,/ 
-Jaką mieszkaniec dziwi nadnilowy./ 
-Pod każdą twarzą tej potwornej larwy,/ 
-Jak z okrętowych żagli płachta jaka,/ 
-<begin id="b1333753870142-1969947272"/><motyw id="m1333753870142-1969947272">Wiatr</motyw>W miarę wielkości tak dziwnego ptaka,/ 
-Sterczą dwa skrzydła, lecz bez piór, bez pierza,/ 
-Całe skórzane jak u nietoperza./ 
-I nieustannym swych skrzydeł trzepotem/ 
-Wiał na trzy strony trzy wiatry z łoskotem,/ 
-Od których marzły kocytowe lody./ 
-<end id="e1333753870142-1969947272"/>Sześcioro oczu miał, z tych każde oko/ 
-Nie łzami, krwawą płakało posoką,/ 
-Która spływała jak łza na trzy brody./ 
-I trzech grzeszników przeżuwał jak zwierze,/ 
-Każdego żuła osobna paszczęka,/ 
-Jako cierlica drze lniane paździerze./ 
-Lecz ząb łagodniej kąsał porównany/ 
-Z szponami, jakie zadawały rany,/ 
-Zdało się, skóra aż do kości pęka.<end id="e1315911576539-159505112"/>/ 
-«Duch, co największe bodaj cierpi męki./ 
-Którego wewnątrz czarnej paszczy głowa,/ 
-A sam na zewnątrz jej nogami miota»/ 
-Mistrz mówił, «oto Judasz Iskariota!/ 
-Dwaj, co głowami zwisają z paszczęki,/ 
-Pierwszy to Brutus! choć ból rzeczywisty/ 
-Szarpie go, jednak milczy jak niemowa;/ 
-Drugi, patrz dobrze, to Kasjusz barczysty<pr><slowo_obce>Pierwszy to Brutus (...) Drugi (...) to Kasjusz barczysty</slowo_obce> --- Brutus i Kasjusz, zdrajcy i zabójcy Cezara. Miejsce, jakie im poeta przed innymi zdrajcami tu oznacza, objaśnia własnym przekonaniem: że cesarstwo rzymskie ugruntowane było z bezpośredniej woli bożej, ażeby świeckie wszechwładztwo i poszanowanie dla niego zaszczepić na ziemi. To przekonanie poeta wyraził nie tylko w osobnym traktacie swoim o monarchii, ale i uczynił je jedną z myśli przewodnich swej <tytul_dziela>Boskiej komedii</tytul_dziela>.</pr>./ 
-<begin id="b1315911737980-303128279"/><motyw id="m1315911737980-303128279">Potwór, Diabeł</motyw>Noc już powraca, teraz czas iść dalej,/ 
-Bośmy już w piekle wszystko oglądali»./ 
-Jak chciał, jam jemu na szyi zawisnął./ 
-W chwili, gdy potwór swe skrzydła roztacza,/ 
-Szybki jak piorun, co już spadł, nim błysnął./ 
-Mistrz się uczepił do boków kudłacza,/ 
-Z kudłów na kudły śliznął się pięściami,/ 
-Między ich runem spadał a lodami./ 
-Gdyśmy już doszli do miejsca, o cudo!/ 
-Tam, gdzie pod biodra rozszerza się udo,/ 
-Mój wódz, jak gdyby wpadł na fortel nowy,/ 
-Gdzie były nogi, przewrócił wierzch głowy<pr><slowo_obce>Mistrz się uczepił do boków kudłacza (...) przewrócił wierzch głowy</slowo_obce> --- Sięgając wyobraźnią naszą aż do środka ziemi, znajdujemy stosowne, że Wergiliusz ślizgając się po kudłach Lucyfera, w tym punkcie obrócił tam nogi, gdzie był wierzch głowy, chociaż bez przerwy w tymże samym kierunku się wspinał. Równie znajdujemy stosowne, że środek Lucyfera jest zarazem środkowym punktem ziemi i że poeci wchodząc po nim, gdy doszli przeciwległego punktu, widzieli stopy Lucyfera wywrócone do góry. Moralne znaczenie tego plastycznego obrazu wygląda dość przeźroczysto: człowiek, który swój błąd lub grzech poznał, a potem pragnie z niego się wyzwolić, musi, chcąc dojść do pożądanego celu, w zupełnie przeciwległym kierunku postępować naprzód. Musi złą zasadę mieć pod sobą, a od chwili, w której ją poznał, wchodzić coraz wyżej.</pr>,/ 
-Piął się po kudłach, aż trzęsły mną dreszcze,/ 
-Myśląc, że nazad idę w piekło jeszcze.<end id="e1315911737980-303128279"/>/ 
-«Trzymaj się dobrze, tą chyba drabiną»/ 
-Mówił wódz, dysząc z trudu i pośpiechu,/ 
-«Możemy zstąpić z tego gniazda grzechu»./ 
-<begin id="b1315911771815-1415539463"/><motyw id="m1315911771815-1415539463">Strach</motyw>I wkrótce wyszedł skały rozpadliną,/ 
-Stanął, odetchnął piersią i co żywo/ 
-Roztropną stopę podstawił, i na nią/ 
-Rad mnie wysadził nad samą otchłanią./ 
-Podniosłem oczy i widziałem dziwo!/ 
-Wspak przewróconą postać Lucyfera,/ 
-Rzekłbyś, nogami w powietrze się wpiera./ 
-Czy byłem w strachu, niechaj ludzie prości/ 
-Zgadną, co nigdy z takiej wysokości/ 
-Schodzić nie mogli!<end id="e1315911771815-1415539463"/> Gdym ochłonął z trwogi,/ 
-Mistrz mówił do mnie: «Teraz wstań na nogi;/ 
-Droga daleka, a ścieżki nużące,/ 
-Już gwiazdy nocne, wschodząc, płoszy słońce»./ 
-Tam droga, którą miałem iść na nowo,/ 
-Nie była prostą ulicą zamkową,/ 
-Raczej jaskinią, co ma wejście krzywe,/ 
-Ściany chropawe, a światło wątpliwe./ 
-<begin id="b1333754529600-1431201780"/><motyw id="m1333754529600-1431201780">Szatan, Obraz świata</motyw>«Mistrzu mój,» rzekłem, «gdym wart twego względu,/ 
-O przemów do mnie, wyprowadź mnie z błędu,/ 
-Gdzie są te lody? Ich grubą powłoką/ 
-Jak tam Lucyfer zapadł tak głęboko?/ 
-I jak to słońce, szybkość niesłychana,/ 
-Przebiegło drogę od wczoraj do rana<pr><slowo_obce>jak to słońce, szybkość niesłychana, przebiegło drogę od wczoraj do rana</slowo_obce> --- Nim poeci przeszli środkowy punkt ziemi, Wergiliusz mówił: ,,Noc się przybliża" potem powiada: ,,I słońce wschodzące płoszy gwiazdy". Ta pozorna sprzeczność daje się tak tłumaczyć: uprzednio mówiąc, myślał Wergiliusz o wschodniej, a teraz, gdy przeszedł punkt środkowy ziemi, myśli o zachodniej półkuli, to jest o antypodach, u których już ranek świta, kiedy u nas noc nadchodzi.</pr>?»/ 
-A mistrz: «Myśl twoja jeszcze za punkt lata,/ 
-<begin id="b1333754362850-1149113674"/><motyw id="m1333754362850-1149113674">Robak</motyw>Gdzie stoi szczecią potwora kudłata,/ 
-Robak, co wierci i toczy rdzeń świata./ 
-<end id="e1333754362850-1149113674"/>Ilem w dół schodził, byłeś tam o tyle,/ 
-Gdym się obrócił, przeszedłeś w tę chwilę/ 
-Punkt, do którego ze wszech stron zebrane/ 
-Wszystkie ciężary ciężą pociągane./ 
-Ty pod półkulę zstąpiłeś z kolei,/ 
-Co przeciwległą jest względem Judei,/ 
-Wielkiej pustyni, wśród której oazy/ 
-Poczęty człowiek żył i zmarł bez zmazy<pr><slowo_obce>poczęty człowiek żył i zmarł bez zmazy</slowo_obce> --- Jezus Chrystus, którego święte imię poeta w piekle tylko przez peryfrazę wspomina.</pr>./ 
-Gdy tam jest wieczór, tu nam ranek świeci:/ 
-Ten, po którego szczeblowałem<pe><slowo_obce>szczeblować</slowo_obce> --- wchodzić po stopniach (szczeblach).</pe> szczeci<pe><slowo_obce>szczeć</slowo_obce> --- szczecina, sierść, włosy.</pe>,/
-Jak stał, tak stoi wbity między lody./ 
-Strącony, tędy snadź on z nieba spadał<pr><slowo_obce>Strącony, tędy snadź on z nieba spadał</slowo_obce> --- Tu Dante wyobraża sobie, że Lucyfer na nieznajomą jeszcze za jego czasów stronę kuli ziemskiej i której jasnowidzeniem swojego geniuszu mógł się domyślać, spadł z nieba, że ląd stały przestraszony jego upadkiem, skrył się pod powierzchnię Oceanu i wynurzył się z głębokości jego na wschodniej półkuli, na której Góra Syjon tworzy punkt przeciwległy. Niemniej wszakże poruszyła się przestraszona ziemia w swoich wnętrznościach, kiedy Lucyfer spadając aż do jej środka sam sobą ją przewiercił. Część ziemi, jaką wyrzucił wiercąc ją sobą, utworzyła górę czyśćcową, ląd jedyny, jaki według pomysłu poety na owej półkuli się znajduje. W środku zaś ziemi jest piekło, z którego poeci w tej chwili wychodzą.</pr>,/ 
-Ląd, co z tej strony pokazał się wprzódy,/ 
-Ze strachu pasem otoczył się wody;/ 
-Od Lucyfera uciekając może,/ 
-Gdy bliżej naszej półkuli osiadał,/ 
-Zostawił tutaj to próżne wydroże./ 
-Jest tam nieznane miejsce dla nas obu<pr><slowo_obce>Jest tam nieznane miejsce dla nas obu</slowo_obce> --- Tu wskazuje górę czyśćcową.</pr>,/ 
-Stąd tak odlegle, wzniesione wysoko,/ 
-Jak cały przestwór Belzebuba grobu./ 
-<end id="e1333754529600-1431201780"/>Kędy jest, trudno go poznać na oko,/ 
-Chyba po szmerze małego strumyka<pr><slowo_obce>Chyba po szmerze małego strumyka</slowo_obce> --- Ponieważ na przeciwległej stronie kuli ziemskiej nie ma innego lądu prócz góry czyśćcowej, więc stamtąd spływać musi ten strumień korytem pochyłym i krętym. W pieśni XIV widzieliśmy, że łzy spadają przez szczeliny olbrzymiego posągu stojącego w grocie góry Idy. Możemy stąd wnioskować, że z łez dusz pokutujących na górze czyśćcowej utworzył się ten strumień.</pr>,/ 
-Który otworem przez siebie wyrżniętym,/ 
-Środkiem tej skały swe fale pomyka,/ 
-Płynąc korytem pochyłym i krętym»./ 
-Wódz i ja w otwór wstąpiliśmy ciasny;/ 
-Zniecierpliwieni oglądać świat jasny,/ 
-Nic się nie troszcząc o trud naszej jazdy,/ 
-Szliśmy bez przerwy, on pierwszy, ja drugi;/ 
-Przez otwór błysły niebios piękne smugi,/ 
-W końcu wychodząc, witaliśmy gwiazdy<pr><slowo_obce>wychodząc witaliśmy gwiazdy</slowo_obce> --- Każda część <tytul_dziela>Boskiej komedii</tytul_dziela> kończy się słowem: <wyroznienie>gwiazda</wyroznienie>, która tu jest symbolem naszego nieśmiertelnego ducha wchodzącego coraz wyżej do najwyższego dobra i wiekuistej prawdy, jakimi są: Niebo i Bóg! Myśl ta w tym symbolu ukryta głównym jest celem i ostatnim wyrazem tej arcy chrześcijańskiej epopei Dantowskiej.</pr>.</strofa> 
-
-
-</liryka_l></utwor>
\ No newline at end of file
diff --git a/apps/wiki/tests/xslt/auto/data/unknown_tag.html b/apps/wiki/tests/xslt/auto/data/unknown_tag.html
deleted file mode 100644 (file)
index 9ce58f8..0000000
+++ /dev/null
@@ -1 +0,0 @@
-<span xmlns="http://www.w3.org/1999/xhtml" class="a unknown-tag" x-node="a" x-attr-value-idp14066432="c" x-attr-name-idp14066432="b" data-wlf-b="c">ala</span>
\ No newline at end of file
diff --git a/apps/wiki/tests/xslt/auto/data/unknown_tag.xml b/apps/wiki/tests/xslt/auto/data/unknown_tag.xml
deleted file mode 100644 (file)
index 91e05ac..0000000
+++ /dev/null
@@ -1 +0,0 @@
-<a b="c">d</a>
\ No newline at end of file
diff --git a/apps/wiki/urls.py b/apps/wiki/urls.py
deleted file mode 100644 (file)
index 0c73aed..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-# -*- coding: utf-8
-from django.conf.urls import patterns, url
-
-
-urlpatterns = patterns('wiki.views',
-    url(r'^edit/(?P<slug>[^/]+)/(?:(?P<chunk>[^/]+)/)?$',
-        'editor', name="wiki_editor"),
-
-    url(r'^readonly/(?P<slug>[^/]+)/(?:(?P<chunk>[^/]+)/)?$',
-        'editor_readonly', name="wiki_editor_readonly"),
-
-    url(r'^gallery/(?P<directory>[^/]+)/$',
-        'gallery', name="wiki_gallery"),
-
-    url(r'^history/(?P<chunk_id>\d+)/$',
-        'history', name="wiki_history"),
-
-    url(r'^rev/(?P<chunk_id>\d+)/$',
-        'revision', name="wiki_revision"),
-
-    url(r'^text/(?P<chunk_id>\d+)/$',
-        'text', name="wiki_text"),
-
-    url(r'^revert/(?P<chunk_id>\d+)/$',
-        'revert', name='wiki_revert'),
-
-    url(r'^diff/(?P<chunk_id>\d+)/$', 'diff', name="wiki_diff"),
-    url(r'^pubmark/(?P<chunk_id>\d+)/$', 'pubmark', name="wiki_pubmark"),
-
-    url(r'^themes$', 'themes', name="themes"),
-)
diff --git a/apps/wiki/views.py b/apps/wiki/views.py
deleted file mode 100644 (file)
index e1ef6ae..0000000
+++ /dev/null
@@ -1,315 +0,0 @@
-# -*- coding: utf-8 -*-
-from datetime import datetime
-import os
-import logging
-from time import mktime
-import urllib
-
-from django.conf import settings
-from django.core.urlresolvers import reverse
-from django import http
-from django.http import Http404, HttpResponseForbidden
-from django.middleware.gzip import GZipMiddleware
-from django.utils.decorators import decorator_from_middleware
-from django.utils.encoding import smart_unicode
-from django.utils.formats import localize
-from django.utils.translation import ugettext as _
-from django.views.decorators.http import require_POST, require_GET
-from django.shortcuts import get_object_or_404, render
-
-from catalogue.models import Book, Chunk
-import nice_diff
-from wiki import forms
-from wiki.helpers import (JSONResponse, JSONFormInvalid, JSONServerError,
-                ajax_require_permission)
-from wiki.models import Theme
-
-#
-# Quick hack around caching problems, TODO: use ETags
-#
-from django.views.decorators.cache import never_cache
-
-logger = logging.getLogger("fnp.wiki")
-
-MAX_LAST_DOCS = 10
-
-
-@never_cache
-def editor(request, slug, chunk=None, template_name='wiki/document_details.html'):
-    try:
-        chunk = Chunk.get(slug, chunk)
-    except Chunk.MultipleObjectsReturned:
-        # TODO: choice page
-        raise Http404
-    except Chunk.DoesNotExist:
-        if chunk is None:
-            try:
-                book = Book.objects.get(slug=slug)
-            except Book.DoesNotExist:
-                return http.HttpResponseRedirect(reverse("catalogue_create_missing", args=[slug]))
-        else:
-            raise Http404
-    if not chunk.book.accessible(request):
-        return HttpResponseForbidden("Not authorized.")
-
-    access_time = datetime.now()
-    last_books = request.session.get("wiki_last_books", {})
-    last_books[reverse(editor, args=[chunk.book.slug, chunk.slug])] = {
-        'time': mktime(access_time.timetuple()),
-        'title': chunk.pretty_name(),
-        }
-
-    if len(last_books) > MAX_LAST_DOCS:
-        oldest_key = min(last_books, key=lambda x: last_books[x]['time'])
-        del last_books[oldest_key]
-    request.session['wiki_last_books'] = last_books
-
-    return render(request, template_name, {
-        'chunk': chunk,
-        'forms': {
-            "text_save": forms.DocumentTextSaveForm(user=request.user, prefix="textsave"),
-            "text_revert": forms.DocumentTextRevertForm(prefix="textrevert"),
-            "pubmark": forms.DocumentPubmarkForm(prefix="pubmark"),
-        },
-        'can_pubmark': request.user.has_perm('catalogue.can_pubmark'),
-        'REDMINE_URL': settings.REDMINE_URL,
-    })
-
-
-@require_GET
-def editor_readonly(request, slug, chunk=None, template_name='wiki/document_details_readonly.html'):
-    try:
-        chunk = Chunk.get(slug, chunk)
-        revision = request.GET['revision']
-    except (Chunk.MultipleObjectsReturned, Chunk.DoesNotExist, KeyError):
-        raise Http404
-    if not chunk.book.accessible(request):
-        return HttpResponseForbidden("Not authorized.")
-
-    access_time = datetime.now()
-    last_books = request.session.get("wiki_last_books", {})
-    last_books[slug, chunk.slug] = {
-        'time': mktime(access_time.timetuple()),
-        'title': chunk.book.title,
-        }
-
-    if len(last_books) > MAX_LAST_DOCS:
-        oldest_key = min(last_books, key=lambda x: last_books[x]['time'])
-        del last_books[oldest_key]
-    request.session['wiki_last_books'] = last_books
-
-    return render(request, template_name, {
-        'chunk': chunk,
-        'revision': revision,
-        'readonly': True,
-        'REDMINE_URL': settings.REDMINE_URL,
-    })
-
-
-@never_cache
-@decorator_from_middleware(GZipMiddleware)
-def text(request, chunk_id):
-    doc = get_object_or_404(Chunk, pk=chunk_id)
-    if not doc.book.accessible(request):
-        return HttpResponseForbidden("Not authorized.")
-
-    if request.method == 'POST':
-        form = forms.DocumentTextSaveForm(request.POST, user=request.user, prefix="textsave")
-        if form.is_valid():
-            if request.user.is_authenticated():
-                author = request.user
-            else:
-                author = None
-            text = form.cleaned_data['text']
-            parent_revision = form.cleaned_data['parent_revision']
-            if parent_revision is not None:
-                parent = doc.at_revision(parent_revision)
-            else:
-                parent = None
-            stage = form.cleaned_data['stage_completed']
-            tags = [stage] if stage else []
-            publishable = (form.cleaned_data['publishable'] and
-                    request.user.has_perm('catalogue.can_pubmark'))
-            doc.commit(author=author,
-                       text=text,
-                       parent=parent,
-                       description=form.cleaned_data['comment'],
-                       tags=tags,
-                       author_name=form.cleaned_data['author_name'],
-                       author_email=form.cleaned_data['author_email'],
-                       publishable=publishable,
-                       )
-            revision = doc.revision()
-            return JSONResponse({
-                'text': doc.materialize() if parent_revision != revision else None,
-                'meta': {},
-                'revision': revision,
-            })
-        else:
-            return JSONFormInvalid(form)
-    else:
-        revision = request.GET.get("revision", None)
-        
-        try:
-            revision = int(revision)
-        except (ValueError, TypeError):
-            revision = doc.revision()
-
-        if revision is not None:
-            text = doc.at_revision(revision).materialize()
-        else:
-            text = ''
-
-        return JSONResponse({
-            'text': text,
-            'meta': {},
-            'revision': revision,
-        })
-
-
-@never_cache
-@require_POST
-def revert(request, chunk_id):
-    form = forms.DocumentTextRevertForm(request.POST, prefix="textrevert")
-    if form.is_valid():
-        doc = get_object_or_404(Chunk, pk=chunk_id)
-        if not doc.book.accessible(request):
-            return HttpResponseForbidden("Not authorized.")
-
-        revision = form.cleaned_data['revision']
-
-        comment = form.cleaned_data['comment']
-        comment += "\n#revert to %s" % revision
-
-        if request.user.is_authenticated():
-            author = request.user
-        else:
-            author = None
-
-        before = doc.revision()
-        logger.info("Reverting %s to %s", chunk_id, revision)
-        doc.at_revision(revision).revert(author=author, description=comment)
-
-        return JSONResponse({
-            'text': doc.materialize() if before != doc.revision() else None,
-            'meta': {},
-            'revision': doc.revision(),
-        })
-    else:
-        return JSONFormInvalid(form)
-
-
-@never_cache
-def gallery(request, directory):
-    try:
-        base_url = ''.join((
-                        smart_unicode(settings.MEDIA_URL),
-                        smart_unicode(settings.IMAGE_DIR),
-                        smart_unicode(directory)))
-
-        base_dir = os.path.join(
-                    smart_unicode(settings.MEDIA_ROOT),
-                    smart_unicode(settings.IMAGE_DIR),
-                    smart_unicode(directory))
-
-        def map_to_url(filename):
-            return urllib.quote(("%s/%s" % (base_url, smart_unicode(filename))).encode('utf-8'))
-
-        def is_image(filename):
-            return os.path.splitext(filename)[1].lower() in (u'.jpg', u'.jpeg', u'.png')
-
-        images = [map_to_url(f) for f in map(smart_unicode, os.listdir(base_dir)) if is_image(f)]
-        images.sort()
-
-        books = Book.objects.filter(gallery=directory)
-
-        if not all(book.public for book in books) and not request.user.is_authenticated():
-            return HttpResponseForbidden("Not authorized.")
-
-        return JSONResponse(images)
-    except (IndexError, OSError):
-        logger.exception("Unable to fetch gallery")
-        raise http.Http404
-
-
-@never_cache
-def diff(request, chunk_id):
-    revA = int(request.GET.get('from', 0))
-    revB = int(request.GET.get('to', 0))
-
-    if revA > revB:
-        revA, revB = revB, revA
-
-    if revB == 0:
-        revB = None
-
-    doc = get_object_or_404(Chunk, pk=chunk_id)
-    if not doc.book.accessible(request):
-        return HttpResponseForbidden("Not authorized.")
-
-    # allow diff from the beginning
-    if revA:
-        docA = doc.at_revision(revA).materialize()
-    else:
-        docA = ""
-    docB = doc.at_revision(revB).materialize()
-
-    return http.HttpResponse(nice_diff.html_diff_table(docA.splitlines(),
-                                         docB.splitlines(), context=3))
-
-
-@never_cache
-def revision(request, chunk_id):
-    doc = get_object_or_404(Chunk, pk=chunk_id)
-    if not doc.book.accessible(request):
-        return HttpResponseForbidden("Not authorized.")
-    return http.HttpResponse(str(doc.revision()))
-
-
-@never_cache
-def history(request, chunk_id):
-    # TODO: pagination
-    doc = get_object_or_404(Chunk, pk=chunk_id)
-    if not doc.book.accessible(request):
-        return HttpResponseForbidden("Not authorized.")
-
-    changes = []
-    for change in doc.history().reverse():
-        changes.append({
-                "version": change.revision,
-                "description": change.description,
-                "author": change.author_str(),
-                "date": localize(change.created_at),
-                "publishable": _("Publishable") + "\n" if change.publishable else "",
-                "tag": ',\n'.join(unicode(tag) for tag in change.tags.all()),
-                "published": _("Published") + ": " + \
-                    localize(change.publish_log.order_by('-book_record__timestamp')[0].book_record.timestamp) \
-                    if change.publish_log.exists() else "",
-            })
-    return JSONResponse(changes)
-
-
-@require_POST
-@ajax_require_permission('catalogue.can_pubmark')
-def pubmark(request, chunk_id):
-    form = forms.DocumentPubmarkForm(request.POST, prefix="pubmark")
-    if form.is_valid():
-        doc = get_object_or_404(Chunk, pk=chunk_id)
-        if not doc.book.accessible(request):
-            return HttpResponseForbidden("Not authorized.")
-
-        revision = form.cleaned_data['revision']
-        publishable = form.cleaned_data['publishable']
-        change = doc.at_revision(revision)
-        if publishable != change.publishable:
-            change.set_publishable(publishable)
-            return JSONResponse({"message": _("Revision marked")})
-        else:
-            return JSONResponse({"message": _("Nothing changed")})
-    else:
-        return JSONFormInvalid(form)
-
-
-def themes(request):
-    prefix = request.GET.get('q', '')
-    return http.HttpResponse('\n'.join([str(t) for t in Theme.objects.filter(name__istartswith=prefix)]))
diff --git a/apps/wiki_img/__init__.py b/apps/wiki_img/__init__.py
deleted file mode 100644 (file)
index c53f0e7..0000000
+++ /dev/null
@@ -1 +0,0 @@
-  # pragma: no cover
diff --git a/apps/wiki_img/forms.py b/apps/wiki_img/forms.py
deleted file mode 100644 (file)
index 555f264..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# This file is part of FNP-Redakcja, 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 wiki.forms import DocumentTextSaveForm
-from catalogue.models import Image
-
-
-class ImageSaveForm(DocumentTextSaveForm):
-    """Form for saving document's text."""
-
-    stage_completed = forms.ModelChoiceField(
-        queryset=Image.tag_model.objects.all(),
-        required=False,
-        label=_(u"Completed"),
-        help_text=_(u"If you completed a life cycle stage, select it."),
-    )
diff --git a/apps/wiki_img/locale/pl/LC_MESSAGES/django.mo b/apps/wiki_img/locale/pl/LC_MESSAGES/django.mo
deleted file mode 100644 (file)
index 3987728..0000000
Binary files a/apps/wiki_img/locale/pl/LC_MESSAGES/django.mo and /dev/null differ
diff --git a/apps/wiki_img/locale/pl/LC_MESSAGES/django.po b/apps/wiki_img/locale/pl/LC_MESSAGES/django.po
deleted file mode 100644 (file)
index 432d289..0000000
+++ /dev/null
@@ -1,307 +0,0 @@
-# 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: Platforma Redakcyjna\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2011-12-14 15:25+0100\n"
-"PO-Revision-Date: 2011-12-14 15:26+0100\n"
-"Last-Translator: Radek Czajka <radoslaw.czajka@nowoczesnapolska.org.pl>\n"
-"Language-Team: Fundacja Nowoczesna Polska <fundacja@nowoczesnapolska.org.pl>\n"
-"Language: \n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-
-#: forms.py:18
-msgid "Completed"
-msgstr "Ukończono"
-
-#: forms.py:19
-msgid "If you completed a life cycle stage, select it."
-msgstr "Jeśli został ukończony etap prac, wskaż go."
-
-#: views.py:126
-msgid "Publishable"
-msgstr "Gotowe do publikacji"
-
-#: templates/wiki_img/base.html:15
-msgid "Platforma Redakcyjna"
-msgstr ""
-
-#: templates/wiki_img/diff_table.html:5
-msgid "Old version"
-msgstr "Stara wersja"
-
-#: templates/wiki_img/diff_table.html:6
-msgid "New version"
-msgstr "Nowa wersja"
-
-#: templates/wiki_img/document_details_base.html:31
-msgid "Help"
-msgstr "Pomoc"
-
-#: templates/wiki_img/document_details_base.html:33
-msgid "Version"
-msgstr "Wersja"
-
-#: templates/wiki_img/document_details_base.html:33
-msgid "Unknown"
-msgstr "nieznana"
-
-#: templates/wiki_img/document_details_base.html:35
-#: templates/wiki_img/tag_dialog.html:15
-msgid "Save"
-msgstr "Zapisz"
-
-#: templates/wiki_img/document_details_base.html:36
-msgid "Save attempt in progress"
-msgstr "Trwa zapisywanie"
-
-#: templates/wiki_img/document_details_base.html:37
-msgid "There is a newer version of this document!"
-msgstr "Istnieje nowsza wersja tego dokumentu!"
-
-#: templates/wiki_img/tag_dialog.html:16
-msgid "Cancel"
-msgstr "Anuluj"
-
-#: templates/wiki_img/tabs/history_view.html:5
-msgid "Compare versions"
-msgstr "Porównaj wersje"
-
-#: templates/wiki_img/tabs/history_view.html:8
-msgid "Mark for publishing"
-msgstr "Oznacz do publikacji"
-
-#: templates/wiki_img/tabs/history_view.html:11
-msgid "Revert document"
-msgstr "Przywróć wersję"
-
-#: templates/wiki_img/tabs/history_view.html:14
-msgid "View version"
-msgstr "Zobacz wersję"
-
-#: templates/wiki_img/tabs/motifs_editor.html:4
-#: templates/wiki_img/tabs/motifs_editor_item.html:3
-msgid "Motifs"
-msgstr "Motywy"
-
-#: templates/wiki_img/tabs/motifs_editor.html:5
-#: templates/wiki_img/tabs/objects_editor.html:5
-msgid "Add"
-msgstr "Dodaj"
-
-#: templates/wiki_img/tabs/objects_editor.html:4
-msgid "Object name"
-msgstr "Nazwa obiektu"
-
-#: templates/wiki_img/tabs/objects_editor_item.html:3
-msgid "Objects"
-msgstr "Obiekty"
-
-#: templates/wiki_img/tabs/source_editor_item.html:5
-msgid "Source code"
-msgstr "Kod źródłowy"
-
-#: templates/wiki_img/tabs/summary_view.html:8
-msgid "Title"
-msgstr "Tytuł"
-
-#: templates/wiki_img/tabs/summary_view.html:13
-msgid "Document ID"
-msgstr "ID dokumentu"
-
-#: templates/wiki_img/tabs/summary_view.html:17
-msgid "Current version"
-msgstr "Aktualna wersja"
-
-#: templates/wiki_img/tabs/summary_view.html:20
-msgid "Last edited by"
-msgstr "Ostatnio edytowane przez"
-
-#: templates/wiki_img/tabs/summary_view_item.html:4
-msgid "Summary"
-msgstr "Podsumowanie"
-
-#~ msgid "First correction"
-#~ msgstr "Autokorekta"
-
-#~ msgid "Tagging"
-#~ msgstr "Tagowanie"
-
-#~ msgid "Initial Proofreading"
-#~ msgstr "Korekta"
-
-#~ msgid "Annotation Proofreading"
-#~ msgstr "Sprawdzenie przypisów źródła"
-
-#~ msgid "Modernisation"
-#~ msgstr "Uwspółcześnienie"
-
-#~ msgid "Annotations"
-#~ msgstr "Przypisy"
-
-#~ msgid "Themes"
-#~ msgstr "Motywy"
-
-#~ msgid "Editor's Proofreading"
-#~ msgstr "Ostateczna redakcja literacka"
-
-#~ msgid "Technical Editor's Proofreading"
-#~ msgstr "Ostateczna redakcja techniczna"
-
-#~ msgid "ZIP file"
-#~ msgstr "Plik ZIP"
-
-#~ msgid "Author"
-#~ msgstr "Autor"
-
-#~ msgid "Your name"
-#~ msgstr "Imię i nazwisko"
-
-#~ msgid "Author's email"
-#~ msgstr "E-mail autora"
-
-#~ msgid "Your email address, so we can show a gravatar :)"
-#~ msgstr "Adres e-mail, żebyśmy mogli pokazać gravatar :)"
-
-#~ msgid "Your comments"
-#~ msgstr "Twój komentarz"
-
-#~ msgid "Describe changes you made."
-#~ msgstr "Opisz swoje zmiany"
-
-#~ msgid "Finished stage: %s"
-#~ msgstr "Ukończony etap: %s"
-
-#~ msgid "name"
-#~ msgstr "nazwa"
-
-#~ msgid "theme"
-#~ msgstr "motyw"
-
-#~ msgid "themes"
-#~ msgstr "motywy"
-
-#~ msgid "Title already used for %s"
-#~ msgstr "Nazwa taka sama jak dla pliku %s"
-
-#~ msgid "Title already used in repository."
-#~ msgstr "Plik o tej nazwie już istnieje w repozytorium."
-
-#~ msgid "File should be UTF-8 encoded."
-#~ msgstr "Plik powinien mieć kodowanie UTF-8."
-
-#~ msgid "Tag added"
-#~ msgstr "Dodano tag"
-
-#~ msgid "Create document"
-#~ msgstr "Utwórz dokument"
-
-#~ msgid "Click to open/close gallery"
-#~ msgstr "Kliknij, aby (ro)zwinąć galerię"
-
-#~ msgid "Clear filter"
-#~ msgstr "Wyczyść filtr"
-
-#~ msgid "Your last edited documents"
-#~ msgstr "Twoje ostatnie edycje"
-
-#~ msgid "Bulk documents upload"
-#~ msgstr "Hurtowe dodawanie dokumentów"
-
-#~ msgid ""
-#~ "Please submit a ZIP with UTF-8 encoded XML files. Files not ending with "
-#~ "<code>.xml</code> will be ignored."
-#~ msgstr ""
-#~ "Proszę wskazać archiwum ZIP z plikami XML w kodowaniu UTF-8. Pliki nie "
-#~ "kończące się na <code>.xml</code> zostaną zignorowane."
-
-#~ msgid "Upload"
-#~ msgstr "Dodaj"
-
-#~ msgid ""
-#~ "There have been some errors. No files have been added to the repository."
-#~ msgstr "Wystąpiły błędy. Żadne pliki nie zostały dodane do repozytorium."
-
-#~ msgid "Offending files"
-#~ msgstr "Błędne pliki"
-
-#~ msgid "Correct files"
-#~ msgstr "Poprawne pliki"
-
-#~ msgid "Files have been successfully uploaded to the repository."
-#~ msgstr "Pliki zostały dodane do repozytorium."
-
-#~ msgid "Uploaded files"
-#~ msgstr "Dodane pliki"
-
-#~ msgid "Skipped files"
-#~ msgstr "Pominięte pliki"
-
-#~ msgid "Files skipped due to no <code>.xml</code> extension"
-#~ msgstr "Pliki pominięte z powodu braku rozszerzenia <code>.xml</code>."
-
-#~ msgid "Refresh"
-#~ msgstr "Odśwież"
-
-#~ msgid "Previous"
-#~ msgstr "Poprzednie"
-
-#~ msgid "Next"
-#~ msgstr "Następne"
-
-#~ msgid "Zoom in"
-#~ msgstr "Powiększ"
-
-#~ msgid "Zoom out"
-#~ msgstr "Zmniejsz"
-
-#~ msgid "Gallery"
-#~ msgstr "Galeria"
-
-#~ msgid "Mark version"
-#~ msgstr "Oznacz wersję"
-
-#~ msgid "History"
-#~ msgstr "Historia"
-
-#~ msgid "Search"
-#~ msgstr "Szukaj"
-
-#~ msgid "Replace with"
-#~ msgstr "Zamień na"
-
-#~ msgid "Replace"
-#~ msgstr "Zamień"
-
-#~ msgid "Options"
-#~ msgstr "Opcje"
-
-#~ msgid "Case sensitive"
-#~ msgstr "Rozróżniaj wielkość liter"
-
-#~ msgid "From cursor"
-#~ msgstr "Zacznij od kursora"
-
-#~ msgid "Search and replace"
-#~ msgstr "Znajdź i zamień"
-
-#~ msgid "Link to gallery"
-#~ msgstr "Link do galerii"
-
-#~ msgid "Insert theme"
-#~ msgstr "Wstaw motyw"
-
-#~ msgid "Insert annotation"
-#~ msgstr "Wstaw przypis"
-
-#~ msgid "Insert special character"
-#~ msgstr "Wstaw znak specjalny"
-
-#~ msgid "Visual editor"
-#~ msgstr "Edytor wizualny"
diff --git a/apps/wiki_img/models.py b/apps/wiki_img/models.py
deleted file mode 100644 (file)
index b685324..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
-# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
-#
diff --git a/apps/wiki_img/templates/wiki_img/diff_table.html b/apps/wiki_img/templates/wiki_img/diff_table.html
deleted file mode 100644 (file)
index 818c38c..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-{% load i18n %}
-<table class="diff_table">
-       <thead>
-               <tr>
-                       <th colspan="2">{% trans "Old version" %}</th>
-                       <th colspan="2">{% trans "New version" %}</th>
-               </tr>
-       </thead>
-<tbody>
-{% for an, a, bn, b, has_change in changes %}
-
-<tr class="{% if has_change %}change{% endif %}">
-<td>{{an}}</td>
-<td class="left">{{ a|safe }}&nbsp;</td>
-<td>{{bn}}</td>
-<td class="right">{{ b|safe }}&nbsp;</td>
-</tr>
-
-{% endfor %}
-</tbody>
-</table>
\ No newline at end of file
diff --git a/apps/wiki_img/templates/wiki_img/document_details.html b/apps/wiki_img/templates/wiki_img/document_details.html
deleted file mode 100644 (file)
index 0dfc9ae..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-{% extends "wiki_img/document_details_base.html" %}
-{% load i18n %}
-
-{% block extrabody %}
-{{ block.super }}
-<script src="{{ STATIC_URL }}js/lib/codemirror-0.8/codemirror.js" type="text/javascript" charset="utf-8">
-</script>
-<script src="{{ STATIC_URL }}js/wiki_img/loader.js" type="text/javascript" charset="utf-8"> </script>
-{% endblock %}
-
-{% block tabs-menu %}
-    {% include "wiki_img/tabs/summary_view_item.html" %}
-    {% include "wiki_img/tabs/motifs_editor_item.html" %}
-    {% include "wiki_img/tabs/objects_editor_item.html" %}
-    {% include "wiki_img/tabs/source_editor_item.html" %}
-    {% include "wiki/tabs/history_view_item.html" %}
-{% endblock %}
-
-{% block tabs-content %}
-    {% include "wiki_img/tabs/summary_view.html" %}
-    {% include "wiki_img/tabs/motifs_editor.html" %}
-    {% include "wiki_img/tabs/objects_editor.html" %}
-    {% include "wiki_img/tabs/source_editor.html" %}
-    {% include "wiki_img/tabs/history_view.html" %}
-{% endblock %}
-
-{% block dialogs %}
-    {% include "wiki/save_dialog.html" %}
-    {% include "wiki/revert_dialog.html" %}
-    {% if can_pubmark %}
-        {% include "wiki/pubmark_dialog.html" %}
-    {% endif %}
-{% endblock %}
-
-{% block editor-class %}
-    sideless
-{% endblock %}
-
diff --git a/apps/wiki_img/templates/wiki_img/document_details_base.html b/apps/wiki_img/templates/wiki_img/document_details_base.html
deleted file mode 100644 (file)
index 0bb58b2..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-{% extends "base.html" %}
-{% load toolbar_tags i18n %}
-
-{% block title %}{{ document.title }} - {{ block.super }}{% endblock %}
-{% block extrahead %}
-{% load pipeline %}
-{% stylesheet 'detail' %}
-{% endblock %}
-
-{% block extrabody %}
-<script type="text/javascript">
-    var STATIC_URL = '{{STATIC_URL}}';
-</script>
-{% javascript 'wiki_img' %}
-{% endblock %}
-
-{% block maincontent %}
-<div id="document-meta"
-       data-object-id="{{ document.pk }}" style="display:none">
-
-       <span data-key="revision">{{ revision }}</span>
-    <span data-key="diff">{{ request.GET.diff }}</span>
-
-       {% block meta-extra %} {% endblock %}
-</div>
-
-<div id="header">
-    <h1><a href="{% url 'catalogue_document_list' %}"><img alt="Home" src="{{STATIC_URL}}icons/go-home.png"/></a><a href="{% url 'catalogue_document_list' %}">Strona<br/>główna</a></h1>
-    <div id="tools">
-        <a href="{{ REDMINE_URL }}projects/wl-publikacje/wiki/Pomoc" target="_blank">
-        {% trans "Help" %}</a>
-        | {% include "registration/head_login.html" %}
-        | {% trans "Version" %}: <span id="document-revision">{% trans "Unknown" %}</span>
-               {% if not readonly %}
-            | <button style="margin-left: 6px" id="save-button">{% trans "Save" %}</button>
-                       <span id='save-attempt-info'>{% trans "Save attempt in progress" %}</span>
-            <span id='out-of-date-info'>{% trans "There is a newer version of this document!" %}</span>
-               {% endif %}
-    </div>
-    <ol id="tabs" class="tabs">
-       {% block tabs-menu %} {% endblock %}
-    </ol>
-</div>
-<div id="splitter">
-    <div id="editor" class="{% block editor-class %} {% endblock %}">
-       {% block tabs-content %} {% endblock %}
-    </div>
-</div>
-
-{% block dialogs %} {% endblock %}
-
-{% endblock %}
diff --git a/apps/wiki_img/templates/wiki_img/document_details_readonly.html b/apps/wiki_img/templates/wiki_img/document_details_readonly.html
deleted file mode 100644 (file)
index ca38838..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-{% extends "wiki_img/document_details_base.html" %}
-{% load i18n %}
-
-{% block extrabody %}
-{{ block.super }}
-<script src="{{ STATIC_URL }}js/lib/codemirror-0.8/codemirror.js" type="text/javascript" charset="utf-8">
-</script>
-<script src="{{ STATIC_URL }}js/wiki_img/loader_readonly.js" type="text/javascript" charset="utf-8"> </script>
-{% endblock %}
-
-{% block tabs-menu %}
-    {% include "wiki_img/tabs/motifs_editor_item.html" %}
-    {% include "wiki_img/tabs/objects_editor_item.html" %}
-    {% include "wiki_img/tabs/source_editor_item.html" %}
-{% endblock %}
-
-{% block tabs-content %}
-    {% include "wiki_img/tabs/motifs_editor.html" %}
-    {% include "wiki_img/tabs/objects_editor.html" %}
-    {% include "wiki_img/tabs/source_editor.html" %}
-{% endblock %}
-
-{% block editor-class %}
-    sideless
-{% endblock %}
-
diff --git a/apps/wiki_img/templates/wiki_img/tabs/history_view.html b/apps/wiki_img/templates/wiki_img/tabs/history_view.html
deleted file mode 100755 (executable)
index dcb62ec..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-{% load i18n %}
-<div id="history-view-editor" class="editor" style="display: none">
-    <div class="toolbar">
-       <button type="button" id="make-diff-button"
-                       data-enabled-when="2" disabled="disabled">{% trans "Compare versions" %}</button>
-        {% if can_pubmark %}
-               <button type="button" id="pubmark-changeset-button"
-                       data-enabled-when="1" disabled="disabled">{% trans "Mark for publishing" %}</button>
-        {% endif %}
-               <button type="button" id="doc-revert-button"
-                       data-enabled-when="1" disabled="disabled">{% trans "Revert document" %}</button>
-               <button id="open-preview-button" disabled="disabled"
-                       data-enabled-when="1"
-                       data-basehref="{% url 'wiki_img_editor_readonly' document.slug %}">{% trans "View version" %}</button>
-
-       </div>
-    <div id="history-view">
-        <p class="message-box" style="display:none;"></p>
-
-               <table id="changes-list-container">
-        <tbody id="changes-list">
-        </tbody>
-               <tbody style="display: none;">
-                       <tr class="entry row-stub">
-                       <td data-stub-value="version"></td>
-                       <td>
-                <span data-stub-value="date"></span>
-               <br/><span data-stub-value="author"></span>
-                               <br />
-                               <span data-stub-value="description"></span>
-                       </td>
-                       <td>
-                <div data-stub-value="publishable"></div>
-                <div data-stub-value="tag"></div>
-                       </td>
-               </tr>
-               </tbody>
-               </table>
-    </div>
-</div>
diff --git a/apps/wiki_img/templates/wiki_img/tabs/motifs_editor.html b/apps/wiki_img/templates/wiki_img/tabs/motifs_editor.html
deleted file mode 100644 (file)
index c064505..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-{% load i18n %}
-<div id="motifs-editor" class="editor" style="display: none">
-    <div class="toolbar">
-        <input class='tag-name' title='{% trans "Motifs" %}' />
-        <button class='add'>{% trans "Add" %}</button>
-
-        <span class="objects-list">
-        </span>
-
-        <div class="toolbar-end">
-        </div>
-    </div>
-
-    <div class='scrolled'>
-        <img src="{{ document.image.url }}" class='area-selectable' alt="Tagged image" />
-    </div>
-</div>
diff --git a/apps/wiki_img/templates/wiki_img/tabs/motifs_editor_item.html b/apps/wiki_img/templates/wiki_img/tabs/motifs_editor_item.html
deleted file mode 100644 (file)
index a5a3c34..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-{% load i18n %}
-<li id="MotifsPerspective" data-ui-related="motifs-editor" data-ui-jsclass="MotifsPerspective">
-    <a href="#">{% trans "Motifs" %}</a>
-</li>
diff --git a/apps/wiki_img/templates/wiki_img/tabs/objects_editor.html b/apps/wiki_img/templates/wiki_img/tabs/objects_editor.html
deleted file mode 100644 (file)
index b4149e2..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-{% load i18n %}
-<div id="objects-editor" class="editor" style="display: none">
-    <div class="toolbar">
-        <input class='tag-name' title='{% trans "Object name" %}' />
-        <button class='add'>{% trans "Add" %}</button>
-
-        <span class="objects-list">
-        </span>
-
-        <div class="toolbar-end">
-        </div>
-    </div>
-
-    <div class='scrolled'>
-        <img src="{{ document.image.url }}" class='area-selectable' alt="Tagged image" />
-    </div>
-</div>
diff --git a/apps/wiki_img/templates/wiki_img/tabs/objects_editor_item.html b/apps/wiki_img/templates/wiki_img/tabs/objects_editor_item.html
deleted file mode 100644 (file)
index 9fc3af9..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-{% load i18n %}
-<li id="ObjectsPerspective" data-ui-related="objects-editor" data-ui-jsclass="ObjectsPerspective">
-    <a href="#">{% trans "Objects" %}</a>
-</li>
diff --git a/apps/wiki_img/templates/wiki_img/tabs/source_editor.html b/apps/wiki_img/templates/wiki_img/tabs/source_editor.html
deleted file mode 100644 (file)
index a1316a7..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-{% load toolbar_tags i18n %}
-<div id="source-editor" class="editor">
-    <textarea id="codemirror_placeholder">&lt;br/&gt;</textarea>
-</div>
\ No newline at end of file
diff --git a/apps/wiki_img/templates/wiki_img/tabs/source_editor_item.html b/apps/wiki_img/templates/wiki_img/tabs/source_editor_item.html
deleted file mode 100644 (file)
index 22b6d66..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-{% load i18n %}
-<li id="CodeMirrorPerspective"
-       data-ui-related="source-editor"
-       data-ui-jsclass="CodeMirrorPerspective">
-    <a href="#">{% trans "Source code" %}</a>
-</li>
\ No newline at end of file
diff --git a/apps/wiki_img/templates/wiki_img/tabs/summary_view.html b/apps/wiki_img/templates/wiki_img/tabs/summary_view.html
deleted file mode 100644 (file)
index a908f55..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-{% load i18n %}
-{% load wiki %}
-<div id="summary-view-editor" class="editor" style="display: none">
-    <!-- <div class="toolbar">
-    </div> -->
-    <div id="summary-view">
-               <h2>
-                       <label for="title">{% trans "Title" %}:</label>
-                       <span data-ui-editable="true" data-edit-target="meta.displayTitle"
-                       >{{ document.name|wiki_title }}</span>
-               </h2>
-               <p>
-                       <label>{% trans "Document ID" %}:</label>
-                       <span>{{ document.name }}</span>
-               </p>
-               <p>
-                       <label>{% trans "Current version" %}:</label>
-                       {{ document_info.revision }} ({{document_info.date}})
-               <p>
-                       <label>{% trans "Last edited by" %}:</label>
-                       {{document_info.author}}
-               </p>
-       </div>
-</div>
diff --git a/apps/wiki_img/templates/wiki_img/tabs/summary_view_item.html b/apps/wiki_img/templates/wiki_img/tabs/summary_view_item.html
deleted file mode 100644 (file)
index bae3ea5..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-{% load i18n %}
-{% load wiki %}
-<li id="SummaryPerspective" data-ui-related="summary-view-editor" data-ui-jsclass="SummaryPerspective">
-    <a href="#">{% trans "Summary" %}</a>
-</li>
diff --git a/apps/wiki_img/tests.py b/apps/wiki_img/tests.py
deleted file mode 100644 (file)
index 6577737..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-from nose.tools import *
-import wiki.models as models
-import shutil
-import tempfile
-
-
-class TestStorageBase:
-    def setUp(self):
-        self.dirpath = tempfile.mkdtemp(prefix='nosetest_')
-
-    def tearDown(self):
-        shutil.rmtree(self.dirpath)
-
-
-class TestDocumentStorage(TestStorageBase):
-
-    def test_storage_empty(self):
-        storage = models.DocumentStorage(self.dirpath)
-        eq_(storage.all(), [])
diff --git a/apps/wiki_img/urls.py b/apps/wiki_img/urls.py
deleted file mode 100644 (file)
index 6a516f3..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-# -*- coding: utf-8
-from django.conf.urls import patterns, url
-
-
-urlpatterns = patterns('wiki_img.views',
-    url(r'^edit/(?P<slug>[^/]+)/$',
-        'editor', name="wiki_img_editor"),
-
-    url(r'^readonly/(?P<slug>[^/]+)/$',
-        'editor_readonly', name="wiki_img_editor_readonly"),
-
-    url(r'^text/(?P<image_id>\d+)/$',
-        'text', name="wiki_img_text"),
-
-    url(r'^history/(?P<object_id>\d+)/$',
-        'history', name="wiki_img_history"),
-
-    url(r'^revert/(?P<object_id>\d+)/$',
-        'revert', name='wiki_img_revert'),
-
-    url(r'^diff/(?P<object_id>\d+)/$', 'diff', name="wiki_img_diff"),
-    url(r'^pubmark/(?P<object_id>\d+)/$', 'pubmark', name="wiki_img_pubmark"),
-
-)
diff --git a/apps/wiki_img/views.py b/apps/wiki_img/views.py
deleted file mode 100644 (file)
index 2b8dd67..0000000
+++ /dev/null
@@ -1,213 +0,0 @@
-import os
-import functools
-import logging
-logger = logging.getLogger("fnp.wiki_img")
-
-from django.core.urlresolvers import reverse
-from wiki.helpers import (JSONResponse, JSONFormInvalid, JSONServerError,
-                ajax_require_permission)
-
-from django.http import Http404, HttpResponse, HttpResponseForbidden
-from django.shortcuts import get_object_or_404, render
-from django.views.decorators.http import require_GET, require_POST
-from django.conf import settings
-from django.utils.formats import localize
-from django.utils.translation import ugettext as _
-
-from catalogue.models import Image
-from wiki import forms
-from wiki import nice_diff
-from wiki_img.forms import ImageSaveForm
-
-#
-# Quick hack around caching problems, TODO: use ETags
-#
-from django.views.decorators.cache import never_cache
-
-
-@never_cache
-def editor(request, slug, template_name='wiki_img/document_details.html'):
-    doc = get_object_or_404(Image, slug=slug)
-
-    return render(request, template_name, {
-        'document': doc,
-        'forms': {
-            "text_save": ImageSaveForm(user=request.user, prefix="textsave"),
-            "text_revert": forms.DocumentTextRevertForm(prefix="textrevert"),
-            "pubmark": forms.DocumentPubmarkForm(prefix="pubmark"),
-        },
-        'can_pubmark': request.user.has_perm('catalogue.can_pubmark_image'),
-        'REDMINE_URL': settings.REDMINE_URL,
-    })
-
-
-@require_GET
-def editor_readonly(request, slug, template_name='wiki_img/document_details_readonly.html'):
-    doc = get_object_or_404(Image, slug=slug)
-    try:
-        revision = request.GET['revision']
-    except (KeyError):
-        raise Http404
-
-    return render(request, template_name, {
-        'document': doc,
-        'revision': revision,
-        'readonly': True,
-        'REDMINE_URL': settings.REDMINE_URL,
-    })
-
-
-@never_cache
-def text(request, image_id):
-    doc = get_object_or_404(Image, pk=image_id)
-    if request.method == 'POST':
-        form = ImageSaveForm(request.POST, user=request.user, prefix="textsave")
-        if form.is_valid():
-            if request.user.is_authenticated():
-                author = request.user
-            else:
-                author = None
-            text = form.cleaned_data['text']
-            parent_revision = form.cleaned_data['parent_revision']
-            if parent_revision is not None:
-                parent = doc.at_revision(parent_revision)
-            else:
-                parent = None
-            stage = form.cleaned_data['stage_completed']
-            tags = [stage] if stage else []
-            publishable = (form.cleaned_data['publishable'] and
-                    request.user.has_perm('catalogue.can_pubmark_image'))
-            doc.commit(author=author,
-                   text=text,
-                   parent=parent,
-                   description=form.cleaned_data['comment'],
-                   tags=tags,
-                   author_name=form.cleaned_data['author_name'],
-                   author_email=form.cleaned_data['author_email'],
-                   publishable=publishable,
-                )
-            revision = doc.revision()
-            return JSONResponse({
-                'text': doc.materialize() if parent_revision != revision else None,
-                'meta': {},
-                'revision': revision,
-            })
-        else:
-            return JSONFormInvalid(form)
-    else:
-        revision = request.GET.get("revision", None)
-        
-        try:
-            revision = int(revision)
-        except (ValueError, TypeError):
-            revision = doc.revision()
-
-        if revision is not None:
-            text = doc.at_revision(revision).materialize()
-        else:
-            text = ''
-
-        return JSONResponse({
-            'text': text,
-            'meta': {},
-            'revision': revision,
-        })
-
-
-@never_cache
-def history(request, object_id):
-    # TODO: pagination
-    doc = get_object_or_404(Image, pk=object_id)
-    if not doc.accessible(request):
-        return HttpResponseForbidden("Not authorized.")
-
-    changes = []
-    for change in doc.history().reverse():
-        changes.append({
-                "version": change.revision,
-                "description": change.description,
-                "author": change.author_str(),
-                "date": localize(change.created_at),
-                "publishable": _("Publishable") + "\n" if change.publishable else "",
-                "tag": ',\n'.join(unicode(tag) for tag in change.tags.all()),
-            })
-    return JSONResponse(changes)
-
-
-@never_cache
-@require_POST
-def revert(request, object_id):
-    form = forms.DocumentTextRevertForm(request.POST, prefix="textrevert")
-    if form.is_valid():
-        doc = get_object_or_404(Image, pk=object_id)
-        if not doc.accessible(request):
-            return HttpResponseForbidden("Not authorized.")
-
-        revision = form.cleaned_data['revision']
-
-        comment = form.cleaned_data['comment']
-        comment += "\n#revert to %s" % revision
-
-        if request.user.is_authenticated():
-            author = request.user
-        else:
-            author = None
-
-        before = doc.revision()
-        logger.info("Reverting %s to %s", object_id, revision)
-        doc.at_revision(revision).revert(author=author, description=comment)
-
-        return JSONResponse({
-            'text': doc.materialize() if before != doc.revision() else None,
-            'meta': {},
-            'revision': doc.revision(),
-        })
-    else:
-        return JSONFormInvalid(form)
-
-
-@never_cache
-def diff(request, object_id):
-    revA = int(request.GET.get('from', 0))
-    revB = int(request.GET.get('to', 0))
-
-    if revA > revB:
-        revA, revB = revB, revA
-
-    if revB == 0:
-        revB = None
-
-    doc = get_object_or_404(Image, pk=object_id)
-    if not doc.accessible(request):
-        return HttpResponseForbidden("Not authorized.")
-
-    # allow diff from the beginning
-    if revA:
-        docA = doc.at_revision(revA).materialize()
-    else:
-        docA = ""
-    docB = doc.at_revision(revB).materialize()
-
-    return HttpResponse(nice_diff.html_diff_table(docA.splitlines(),
-                                         docB.splitlines(), context=3))
-
-
-@require_POST
-@ajax_require_permission('catalogue.can_pubmark_image')
-def pubmark(request, object_id):
-    form = forms.DocumentPubmarkForm(request.POST, prefix="pubmark")
-    if form.is_valid():
-        doc = get_object_or_404(Image, pk=object_id)
-        if not doc.accessible(request):
-            return HttpResponseForbidden("Not authorized.")
-
-        revision = form.cleaned_data['revision']
-        publishable = form.cleaned_data['publishable']
-        change = doc.at_revision(revision)
-        if publishable != change.publishable:
-            change.set_publishable(publishable)
-            return JSONResponse({"message": _("Revision marked")})
-        else:
-            return JSONResponse({"message": _("Nothing changed")})
-    else:
-        return JSONFormInvalid(form)
diff --git a/manage.py b/manage.py
deleted file mode 100755 (executable)
index 44d4b1a..0000000
--- a/manage.py
+++ /dev/null
@@ -1,15 +0,0 @@
-#!/usr/bin/env python
-import os
-import sys
-
-ROOT = os.path.dirname(os.path.abspath(__file__))
-sys.path = [
-    os.path.join(ROOT, 'apps'),
-] + sys.path
-
-if __name__ == "__main__":
-    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "redakcja.settings")
-
-    from django.core.management import execute_from_command_line
-
-    execute_from_command_line(sys.argv)
diff --git a/redakcja/__init__.py b/redakcja/__init__.py
deleted file mode 100644 (file)
index b64e43e..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-from __future__ import absolute_import
-
-# This will make sure the app is always imported when
-# Django starts so that shared_task will use this app.
-from .celery import app as celery_app
diff --git a/redakcja/celery.py b/redakcja/celery.py
deleted file mode 100644 (file)
index a2fed69..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-from __future__ import absolute_import
-
-import os
-import sys
-
-ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
-sys.path = [
-    os.path.join(ROOT, 'apps'),
-    os.path.join(ROOT, 'lib'),
-    os.path.join(ROOT, 'lib/librarian'),
-] + sys.path
-
-os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'redakcja.localsettings')
-
-from celery import Celery
-from django.conf import settings
-
-app = Celery('redakcja')
-
-# Using a string here means the worker will not have to
-# pickle the object when using Windows.
-app.config_from_object('django.conf:settings')
-app.autodiscover_tasks(lambda: settings.INSTALLED_APPS)
diff --git a/redakcja/context_processors.py b/redakcja/context_processors.py
deleted file mode 100644 (file)
index 5e3372e..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-# -*- coding: utf-8
-
-def settings(request):
-    from django.conf import settings
-
-    if settings.SHOW_APP_VERSION:
-        import subprocess
-        process = subprocess.Popen(["git", "show", "--oneline"], stdout=subprocess.PIPE)
-        data, _err = process.communicate()
-        # get app version 
-        VERSION = data.splitlines()[0].split()[0]
-    else:
-        VERSION = ''
-
-    return {
-        'MEDIA_URL': settings.MEDIA_URL,
-        'STATIC_URL': settings.STATIC_URL,
-        'APP_VERSION': VERSION,
-    }
diff --git a/redakcja/locale/pl/LC_MESSAGES/django.mo b/redakcja/locale/pl/LC_MESSAGES/django.mo
deleted file mode 100644 (file)
index 7f35966..0000000
Binary files a/redakcja/locale/pl/LC_MESSAGES/django.mo and /dev/null differ
diff --git a/redakcja/locale/pl/LC_MESSAGES/django.po b/redakcja/locale/pl/LC_MESSAGES/django.po
deleted file mode 100644 (file)
index 916f4db..0000000
+++ /dev/null
@@ -1,145 +0,0 @@
-# Polskie tłumaczenie dla platformy wolnych lektur.
-# Copyright (C) 2009
-# This file is distributed under the same license as the 'platforma' package.
-# lrekucki@gmail.com, 2009.
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: PACKAGE VERSION\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2011-10-18 11:18+0200\n"
-"PO-Revision-Date: 2011-10-18 11:19+0100\n"
-"Last-Translator: Radek Czajka <radoslaw.czajka@nowoczesnapolska.org.pl>\n"
-"Language-Team: LANGUAGE <LL@li.org>\n"
-"Language: \n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-
-#: templates/404.html:8
-msgid "Page not found"
-msgstr "Strona nie została znaleziona"
-
-#: templates/404.html:10
-#, python-format
-msgid ""
-"The page you're trying\n"
-"to reach (<tt>%(p)s</tt>) could not be found. If it's a document, try\n"
-"looking for it on the <a href=\"/\">document list</a>."
-msgstr "Strona, do której próbujesz dotrzeć (<tt>%(p)s</tt>), nie istnieje. Spróbuj poszukać jej na <a href=\"/\">liście dokumentów</a>."
-
-#: templates/404.html:15
-#, python-format
-msgid ""
-"If you\n"
-"still can't find what you're looking for, please\n"
-"<a href=\"%(m)s\">contact the administrator</a>."
-msgstr "Jeśli nadal nie możesz znaleźć tego, czego szukasz, <a href=\"%(m)s\">skontaktuj się z administratorem</a>."
-
-#: templates/404.html:22
-#, python-format
-msgid ""
-"If you're coming from Redmine, please note that\n"
-"work is no longer managed there. \n"
-"Go to the <a href=\"/\">document list</a>\n"
-"or to <a href=\"%(m)s\">your page</a> instead."
-msgstr ""
-"Jeśli skierował Cię tu Redmine, zwróć uwagę, że nie służy on już do koordynowania prac redakcyjnych. Możesz za to przejść do <a href=\"/\">listy dokumentów</a>\n"
-"albo do <a href=\"%(m)s\">swojej strony</a>."
-
-#: templates/base.html:7
-msgid "Platforma Redakcyjna"
-msgstr ""
-
-#: templates/base.html:16
-msgid "Loading"
-msgstr "Ładowanie"
-
-#: templates/admin/index.html:21
-#, python-format
-msgid "Models available in the %(name)s application."
-msgstr ""
-
-#: templates/admin/index.html:22
-#, python-format
-msgid "%(name)s"
-msgstr ""
-
-#: templates/admin/index.html:32
-msgid "Add"
-msgstr ""
-
-#: templates/admin/index.html:38
-msgid "Change"
-msgstr ""
-
-#: templates/admin/index.html:48
-msgid "You don't have permission to edit anything."
-msgstr ""
-
-#: templates/admin/index.html:56
-msgid "Recent Actions"
-msgstr ""
-
-#: templates/admin/index.html:57
-msgid "My Actions"
-msgstr ""
-
-#: templates/admin/index.html:61
-msgid "None available"
-msgstr ""
-
-#: templates/admin/index.html:75
-msgid "Unknown content"
-msgstr ""
-
-#: templates/pagination/pagination.html:5
-#: templates/pagination/pagination.html:7
-msgid "previous"
-msgstr "poprzednie"
-
-#: templates/pagination/pagination.html:21
-#: templates/pagination/pagination.html:23
-msgid "next"
-msgstr "następne"
-
-#: templates/registration/head_login.html:5
-msgid "Log Out"
-msgstr "Wyloguj"
-
-#: templates/registration/head_login.html:9
-msgid "Log In"
-msgstr "Logowanie"
-
-#~ msgid "Refresh panel"
-#~ msgstr "Odśwież panel"
-
-#~ msgid "Print version"
-#~ msgstr "Wersja do druku"
-
-#~ msgid "Next page"
-#~ msgstr "Następna strona"
-
-#~ msgid "Zoom in"
-#~ msgstr "Powiększ"
-
-#~ msgid "Zoom out"
-#~ msgstr "Zmniejsz"
-
-#~ msgid "Reset zoom"
-#~ msgstr "Oryginalny rozmiar"
-
-#~ msgid "History"
-#~ msgstr "Historia"
-
-#~ msgid "Push"
-#~ msgstr "Zatwierdź"
-
-#~ msgid "Pull"
-#~ msgstr "Uaktualnij"
-
-#~ msgid "Save"
-#~ msgstr "Zapisz"
-
-#~ msgid "Quick save"
-#~ msgstr "Szybki zapis"
diff --git a/redakcja/localsettings.sample b/redakcja/localsettings.sample
deleted file mode 100644 (file)
index a0eb426..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-# 
-#  localsettings template for Platforma
-#
-#  Duplicate this file as localsettings.py and change it to your liking.
-#  Settings defined in localsettings.py will override settings from 
-#  settings.py file. localsettings.py should never be commited 
-#  to a version control system. Please make changes to settings.py 
-#  or localsettings.sample instead.
-#
-
-# THIS IS REQUIRED
-from redakcja.settings import *
-
-# Path to repository with managed documents
-CATALOGUE_REPO_PATH = '/srv/redakcja/books'
-
-LOGGING_CONFIG_FILE = "/srv/redakcja/logging.cfg.dev"
-
-STATIC_ROOT = '/srv/redakcja/static/'
-MEDIA_ROOT = '/srv/redakcja/media/'
-
-# Subdirectory of MEDIA_ROOT containing images
-IMAGE_DIR = 'images/'
-
-CAS_SERVER_URL = 'http://logowanie.nowoczesnapolska.org.pl/cas/'
-CAS_VERSION = '3'
-
-REDMINE_URL = 'http://redmine.nowoczesnapolska.org.pl/'
-DEBUG = True
-MAINTENANCE_MODE = False
-COMPRESS = False
-
-APICLIENT_WL_CONSUMER_KEY = None
-APICLIENT_WL_CONSUMER_SECRET = None
-
-CELERY_ALWAYS_EAGER = False
-
-SECRET_KEY = ''
diff --git a/redakcja/settings/__init__.py b/redakcja/settings/__init__.py
deleted file mode 100644 (file)
index 4d4fe8f..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-from __future__ import absolute_import
-from os import path
-from redakcja.settings.common import *
-
-DATABASES = {
-    'default': {
-        'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
-        'NAME': path.join(PROJECT_ROOT, 'dev.sqlite'), # Or path to database file if using sqlite3.
-        'USER': '',                      # Not used with sqlite3.
-        'PASSWORD': '',                  # Not used with sqlite3.
-        'HOST': '',                      # Set to empty string for localhost. Not used with sqlite3.
-        'PORT': '',                      # Set to empty string for default. Not used with sqlite3.
-    }
-}
-
-try:
-    LOGGING_CONFIG_FILE
-except NameError:
-    LOGGING_CONFIG_FILE = os.path.join(PROJECT_ROOT, 'config',
-                                ('logging.cfg' if not DEBUG else 'logging.cfg.dev'))
-try:
-    import logging
-
-    if os.path.isfile(LOGGING_CONFIG_FILE):
-        import logging.config
-        logging.config.fileConfig(LOGGING_CONFIG_FILE)
-    else:
-        import sys
-        logging.basicConfig(stream=sys.stderr)
-except (ImportError,), exc:
-    raise
-
-
-try:
-    from redakcja.localsettings import *
-except ImportError:
-    pass
diff --git a/redakcja/settings/common.py b/redakcja/settings/common.py
deleted file mode 100644 (file)
index 6c53d44..0000000
+++ /dev/null
@@ -1,154 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import absolute_import
-import os.path
-
-PROJECT_ROOT = os.path.realpath(os.path.dirname(os.path.dirname(__file__)))
-
-DEBUG = False
-TEMPLATE_DEBUG = DEBUG
-
-MAINTENANCE_MODE = False
-
-ADMINS = (
-    (u'Radek Czajka', 'radoslaw.czajka@nowoczesnapolska.org.pl'),
-)
-
-MANAGERS = ADMINS
-
-# Local time zone for this installation. Choices can be found here:
-# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
-# although not all choices may be available on all operating systems.
-# If running in a Windows environment this must be set to the same as your
-# system time zone.
-TIME_ZONE = 'Europe/Warsaw'
-
-# Language code for this installation. All choices can be found here:
-# http://www.i18nguy.com/unicode/language-identifiers.html
-LANGUAGE_CODE = 'pl'
-
-#import locale
-#locale.setlocale(locale.LC_ALL, '')
-
-SITE_ID = 1
-
-# If you set this to False, Django will make some optimizations so as not
-# to load the internationalization machinery.
-USE_I18N = True
-USE_L10N = True
-
-
-# Absolute path to the directory that holds media.
-# Example: "/home/media/media.lawrence.com/"
-MEDIA_ROOT = PROJECT_ROOT + '/media/dynamic'
-STATIC_ROOT = PROJECT_ROOT + '/../static/'
-
-STATICFILES_DIRS = [
-    PROJECT_ROOT + '/static/'
-]
-
-# URL that handles the media served from MEDIA_ROOT. Make sure to use a
-# trailing slash if there is a path component (optional in other cases).
-# Examples: "http://media.lawrence.com", "http://example.com/media/"
-MEDIA_URL = '/media/dynamic/'
-STATIC_URL = '/media/static/'
-
-SESSION_COOKIE_NAME = "redakcja_sessionid"
-
-# List of callables that know how to import templates from various sources.
-TEMPLATE_LOADERS = (
-    'django.template.loaders.filesystem.Loader',
-    'django.template.loaders.app_directories.Loader',
-)
-
-TEMPLATE_CONTEXT_PROCESSORS = (
-    "django.contrib.auth.context_processors.auth",
-    "django.core.context_processors.debug",
-    "django.core.context_processors.i18n",
-    "redakcja.context_processors.settings", # this is instead of media
-    'django.core.context_processors.csrf',
-    "django.core.context_processors.request",
-)
-
-
-MIDDLEWARE_CLASSES = (
-    'django.middleware.common.CommonMiddleware',
-    'django.middleware.csrf.CsrfViewMiddleware',
-    'django.contrib.sessions.middleware.SessionMiddleware',
-    'django.contrib.messages.middleware.MessageMiddleware',
-
-    'django.contrib.auth.middleware.AuthenticationMiddleware',
-    'django_cas.middleware.CASMiddleware',
-
-    'django.contrib.admindocs.middleware.XViewMiddleware',
-    'pagination.middleware.PaginationMiddleware',
-    'maintenancemode.middleware.MaintenanceModeMiddleware',
-)
-
-AUTHENTICATION_BACKENDS = (
-    'django.contrib.auth.backends.ModelBackend',
-    'fnpdjango.auth_backends.AttrCASBackend',
-)
-
-ROOT_URLCONF = 'redakcja.urls'
-
-TEMPLATE_DIRS = (
-    PROJECT_ROOT + '/templates',
-)
-
-FIREPYTHON_LOGGER_NAME = "fnp"
-
-INSTALLED_APPS = (
-    'django.contrib.auth',
-    'django.contrib.contenttypes',
-    'django.contrib.sessions',
-    'django.contrib.messages',
-    'django.contrib.staticfiles',
-    'django.contrib.sites',
-    'django.contrib.admin',
-    'django.contrib.admindocs',
-    'django.contrib.comments',
-    'raven.contrib.django.raven_compat',
-
-    'south',
-    'sorl.thumbnail',
-    'pagination',
-    'django_gravatar',
-    'fileupload',
-    'kombu.transport.django',
-    'pipeline',
-    'fnpdjango',
-
-    'catalogue',
-    'cover',
-    'dvcs',
-    'wiki',
-    'wiki_img',
-    'toolbar',
-    'apiclient',
-    'email_mangler',
-)
-
-LOGIN_REDIRECT_URL = '/documents/user'
-
-CAS_USER_ATTRS_MAP = {
-    'email': 'email', 'firstname': 'first_name', 'lastname': 'last_name'}
-
-# REPOSITORY_PATH = '/Users/zuber/Projekty/platforma/files/books'
-
-IMAGE_DIR = 'images/'
-
-
-BROKER_URL = 'django://'
-CELERY_EAGER_PROPAGATES_EXCEPTIONS = True
-CELERY_SEND_TASK_ERROR_EMAILS = True
-CELERY_ACCEPT_CONTENT = ['pickle']  # Remove when all tasks jsonable.
-
-SHOW_APP_VERSION = False
-
-MIN_COVER_SIZE = (915, 1270)
-
-try:
-    from redakcja.settings.compress import *
-except ImportError:
-    pass
-
diff --git a/redakcja/settings/compress.py b/redakcja/settings/compress.py
deleted file mode 100644 (file)
index 9bee670..0000000
+++ /dev/null
@@ -1,146 +0,0 @@
-STATICFILES_FINDERS = (
-    'django.contrib.staticfiles.finders.FileSystemFinder',
-    'django.contrib.staticfiles.finders.AppDirectoriesFinder',
-#    'django.contrib.staticfiles.finders.DefaultStorageFinder',
-    'pipeline.finders.PipelineFinder',
-)
-
-
-STATICFILES_STORAGE = 'pipeline.storage.PipelineCachedStorage'
-PIPELINE_CSS_COMPRESSOR = None
-PIPELINE_JS_COMPRESSOR = None
-PIPELINE_STORAGE = 'pipeline.storage.PipelineFinderStorage'
-
-
-# CSS and JS files to compress
-PIPELINE_CSS = {
-    'detail': {
-         'source_filenames': (
-            'css/master.css',
-            'css/toolbar.css',
-            'css/gallery.css',
-            'css/history.css',
-            'css/summary.css',
-            'css/html.css',
-            'css/jquery.autocomplete.css',
-            'css/imgareaselect-default.css', #img!
-            'css/dialogs.css',
-        ),
-        'output_filename': 'compressed/detail_styles.css',
-    },
-    'catalogue': {
-        'source_filenames': (
-            'css/filelist.css',
-        ),
-        'output_filename': 'compressed/catalogue_styles.css',
-     },
-     'book': {
-        'source_filenames': (
-            'css/book.css',
-        ),
-        'output_filename': 'compressed/book.css',
-    },
-    'book_list': {
-        'source_filenames': (
-            'contextmenu/jquery.contextMenu.css',
-            'css/book_list.css',
-        ),
-        'output_filename': 'compressed/book_list.css',
-    },
-}
-
-PIPELINE_JS = {
-    # everything except codemirror
-    'detail': {
-        'source_filenames': (
-                # libraries
-                'js/lib/jquery/jquery.autocomplete.js',
-                'js/lib/jquery/jquery.blockui.js',
-                'js/lib/jquery/jquery.elastic.js',
-                'js/lib/jquery/jquery.xmlns.js',
-                'js/button_scripts.js',
-                'js/slugify.js',
-
-                # wiki scripts
-                'js/wiki/wikiapi.js',
-                'js/wiki/xslt.js',
-
-                # base UI
-                'js/wiki/base.js',
-                'js/wiki/toolbar.js',
-
-                # dialogs
-                'js/wiki/dialog_save.js',
-                'js/wiki/dialog_revert.js',
-                'js/wiki/dialog_pubmark.js',
-
-                # views
-                'js/wiki/view_history.js',
-                'js/wiki/view_summary.js',
-                'js/wiki/view_editor_source.js',
-                'js/wiki/view_editor_wysiwyg.js',
-                'js/wiki/view_gallery.js',
-                'js/wiki/view_annotations.js',
-                'js/wiki/view_search.js',
-                'js/wiki/view_column_diff.js',
-        ),
-        'output_filename': 'compressed/detail_scripts.js',
-     },
-    'wiki_img': {
-        'source_filenames': (
-                # libraries
-                'js/lib/jquery-1.4.2.min.js',
-                'js/lib/jquery/jquery.autocomplete.js',
-                'js/lib/jquery/jquery.blockui.js',
-                'js/lib/jquery/jquery.elastic.js',
-                'js/lib/jquery/jquery.imgareaselect.js',
-                'js/button_scripts.js',
-                'js/slugify.js',
-
-                # wiki scripts
-                'js/wiki_img/wikiapi.js',
-
-                # base UI
-                'js/wiki_img/base.js',
-                'js/wiki/toolbar.js',
-
-                # dialogs
-                'js/wiki/dialog_save.js',
-                'js/wiki/dialog_revert.js',
-                'js/wiki/dialog_pubmark.js',
-
-                # views
-                'js/wiki_img/view_editor_objects.js',
-                'js/wiki_img/view_editor_motifs.js',
-                'js/wiki/view_editor_source.js',
-                'js/wiki/view_history.js',
-                'js/wiki/view_column_diff.js',
-        ),
-        'output_filename': 'compressed/detail_img_scripts.js',
-     },
-    'catalogue': {
-        'source_filenames': (
-                'js/catalogue/catalogue.js',
-                'js/slugify.js',
-                'email_mangler/email_mangler.js',
-        ),
-        'output_filename': 'compressed/catalogue_scripts.js',
-     },
-     'book': {
-        'source_filenames': (
-            'js/book_text/jquery.eventdelegation.js',
-            'js/book_text/jquery.scrollto.js',
-            'js/book_text/jquery.highlightfade.js',
-            'js/book_text/book.js',
-        ),
-        'output_filename': 'compressed/book.js',
-         },
-    'book_list': {
-        'source_filenames': (
-            'contextmenu/jquery.ui.position.js',
-            'contextmenu/jquery.contextMenu.js',
-            'js/catalogue/book_list.js',
-        ),
-        'output_filename': 'compressed/book_list.js',
-    }
-}
diff --git a/redakcja/settings/integration_test.py b/redakcja/settings/integration_test.py
deleted file mode 100644 (file)
index ba477bb..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-from redakcja.settings.test import *
-
-NOSE_ARGS = ()
-
-STATIC_ROOT_SYMLINK = os.path.dirname(STATIC_ROOT) + '_test'
-STATICFILES_DIRS.append(STATIC_ROOT_SYMLINK)
-
diff --git a/redakcja/settings/test.py b/redakcja/settings/test.py
deleted file mode 100644 (file)
index cd13178..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-#
-# Nose tests
-#
-
-from redakcja.settings.common import *
-
-# ROOT_URLCONF = 'yourapp.settings.test.urls'
-
-DATABASES = {
-    'default': {
-        'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
-        'NAME': '', # Or path to database file if using sqlite3.
-        'USER': '',                      # Not used with sqlite3.
-        'PASSWORD': '',                  # Not used with sqlite3.
-        'HOST': '',                      # Set to empty string for localhost. Not used with sqlite3.
-        'PORT': '',                      # Set to empty string for default. Not used with sqlite3.
-    }
-}
-
-import tempfile
-
-CATALOGUE_REPO_PATH = tempfile.mkdtemp(prefix='redakcja-repo')
-CATALOGUE_IMAGE_REPO_PATH = tempfile.mkdtemp(prefix='redakcja-repo-img')
-MEDIA_ROOT = tempfile.mkdtemp(prefix='media-root')
-CELERY_ALWAYS_EAGER = True
-
-INSTALLED_APPS += ('django_nose', 'dvcs.tests')
-
-TEST_RUNNER = 'django_nose.NoseTestSuiteRunner'
-TEST_MODULES = ('catalogue', 'cover', 'dvcs.tests', 'wiki', 'toolbar')
-COVER_APPS = ('catalogue', 'cover', 'dvcs', 'wiki', 'toolbar')
-NOSE_ARGS = (
-    '--tests=' + ','.join(TEST_MODULES),
-    '--cover-package=' + ','.join(COVER_APPS),
-    '-d',
-    '--with-doctest',
-    '--with-xunit',
-    '--with-xcoverage',
-)
-
-SECRET_KEY = "not-so-secret"
-
-
-LITERARY_DIRECTOR_USERNAME = 'Kaowiec'
diff --git a/redakcja/static/contextmenu/images/cut.png b/redakcja/static/contextmenu/images/cut.png
deleted file mode 100755 (executable)
index f215d6f..0000000
Binary files a/redakcja/static/contextmenu/images/cut.png and /dev/null differ
diff --git a/redakcja/static/contextmenu/images/door.png b/redakcja/static/contextmenu/images/door.png
deleted file mode 100755 (executable)
index 369fc46..0000000
Binary files a/redakcja/static/contextmenu/images/door.png and /dev/null differ
diff --git a/redakcja/static/contextmenu/images/page_white_copy.png b/redakcja/static/contextmenu/images/page_white_copy.png
deleted file mode 100755 (executable)
index a9f31a2..0000000
Binary files a/redakcja/static/contextmenu/images/page_white_copy.png and /dev/null differ
diff --git a/redakcja/static/contextmenu/images/page_white_delete.png b/redakcja/static/contextmenu/images/page_white_delete.png
deleted file mode 100755 (executable)
index af1ecaf..0000000
Binary files a/redakcja/static/contextmenu/images/page_white_delete.png and /dev/null differ
diff --git a/redakcja/static/contextmenu/images/page_white_edit.png b/redakcja/static/contextmenu/images/page_white_edit.png
deleted file mode 100755 (executable)
index b93e776..0000000
Binary files a/redakcja/static/contextmenu/images/page_white_edit.png and /dev/null differ
diff --git a/redakcja/static/contextmenu/images/page_white_paste.png b/redakcja/static/contextmenu/images/page_white_paste.png
deleted file mode 100755 (executable)
index 5b2cbb3..0000000
Binary files a/redakcja/static/contextmenu/images/page_white_paste.png and /dev/null differ
diff --git a/redakcja/static/contextmenu/index.html b/redakcja/static/contextmenu/index.html
deleted file mode 100644 (file)
index f099c95..0000000
+++ /dev/null
@@ -1,200 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">\r
-\r
-<html xmlns="http://www.w3.org/1999/xhtml">\r
-\r
-       <head>\r
-               <title>jQuery Context Menu Plugin Demo</title>\r
-               <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />\r
-               \r
-               <style type="text/css">\r
-                       BODY,\r
-                       HTML {\r
-                               padding: 0px;\r
-                               margin: 0px;\r
-                       }\r
-                       BODY {\r
-                               font-family: Verdana, Arial, Helvetica, sans-serif;\r
-                               font-size: 11px;\r
-                               background: #FFF;\r
-                               padding: 15px;\r
-                       }\r
-                       \r
-                       H1 {\r
-                               font-family: Georgia, serif;\r
-                               font-size: 20px;\r
-                               font-weight: normal;\r
-                       }\r
-                       \r
-                       H2 {\r
-                               font-family: Georgia, serif;\r
-                               font-size: 16px;\r
-                               font-weight: normal;\r
-                               margin: 0px 0px 10px 0px;\r
-                       }\r
-                       \r
-                       #myDiv {\r
-                               width: 150px;\r
-                               border: solid 1px #2AA7DE;\r
-                               background: #6CC8EF;\r
-                               text-align: center;\r
-                               padding: 4em .5em;\r
-                               margin: 1em;\r
-                               float: left;\r
-                       }\r
-                       \r
-                       #myList {\r
-                               margin: 1em;\r
-                               float: left;\r
-                       }\r
-                       \r
-                       #myList UL {\r
-                               padding: 0px;\r
-                               margin: 0em 1em;\r
-                       }\r
-                       \r
-                       #myList LI {\r
-                               width: 100px;\r
-                               border: solid 1px #2AA7DE;\r
-                               background: #6CC8EF;\r
-                               padding: 5px 5px;\r
-                               margin: 2px 0px;\r
-                               list-style: none;\r
-                       }\r
-                       \r
-                       #options {\r
-                               clear: left;\r
-                       }\r
-                       \r
-                       #options INPUT {\r
-                               font-family: Verdana, Arial, Helvetica, sans-serif;\r
-                               font-size: 11px;\r
-                               width: 150px;\r
-                       }\r
-                       \r
-               </style>                \r
-               \r
-               <script src="../js/lib/jquery-1.7.2.min.js" type="text/javascript"></script>\r
-               <script src="jquery.contextMenu.js" type="text/javascript"></script>\r
-               <link href="jquery.contextMenu.css" rel="stylesheet" type="text/css" />\r
-               \r
-               <script type="text/javascript">\r
-                       \r
-                       $(document).ready( function() {\r
-                               \r
-                               // Show menu when #myDiv is clicked\r
-                               $("#myDiv").contextMenu({\r
-                                       menu: 'myMenu'\r
-                               },\r
-                                       function(action, el, pos) {\r
-                                       alert(\r
-                                               'Action: ' + action + '\n\n' +\r
-                                               'Element ID: ' + $(el).attr('id') + '\n\n' + \r
-                                               'X: ' + pos.x + '  Y: ' + pos.y + ' (relative to element)\n\n' + \r
-                                               'X: ' + pos.docX + '  Y: ' + pos.docY+ ' (relative to document)'\r
-                                               );\r
-                               });\r
-                               \r
-                               // Show menu when a list item is clicked\r
-                               $("#myList UL LI").contextMenu({\r
-                                       menu: 'myMenu'\r
-                               }, function(action, el, pos) {\r
-                                       alert(\r
-                                               'Action: ' + action + '\n\n' +\r
-                                               'Element text: ' + $(el).text() + '\n\n' + \r
-                                               'X: ' + pos.x + '  Y: ' + pos.y + ' (relative to element)\n\n' + \r
-                                               'X: ' + pos.docX + '  Y: ' + pos.docY+ ' (relative to document)'\r
-                                               );\r
-                               });\r
-                               \r
-                               // Disable menus\r
-                               $("#disableMenus").click( function() {\r
-                                       $('#myDiv, #myList UL LI').disableContextMenu();\r
-                                       $(this).attr('disabled', true);\r
-                                       $("#enableMenus").attr('disabled', false);\r
-                               });\r
-                               \r
-                               // Enable menus\r
-                               $("#enableMenus").click( function() {\r
-                                       $('#myDiv, #myList UL LI').enableContextMenu();\r
-                                       $(this).attr('disabled', true);\r
-                                       $("#disableMenus").attr('disabled', false);\r
-                               });\r
-                               \r
-                               // Disable cut/copy\r
-                               $("#disableItems").click( function() {\r
-                                       $('#myMenu').disableContextMenuItems('#cut,#copy');\r
-                                       $(this).attr('disabled', true);\r
-                                       $("#enableItems").attr('disabled', false);\r
-                               });\r
-                               \r
-                               // Enable cut/copy\r
-                               $("#enableItems").click( function() {\r
-                                       $('#myMenu').enableContextMenuItems('#cut,#copy');\r
-                                       $(this).attr('disabled', true);\r
-                                       $("#disableItems").attr('disabled', false);\r
-                               });                             \r
-                               \r
-                       });\r
-                       \r
-               </script>\r
-       </head>\r
-       \r
-       <body>\r
-               \r
-               <h1>jQuery Context Menu Plugin Demo</h1>\r
-               <p>\r
-                       This plugin lets you add context menu functionality to your web applications.\r
-               </p>\r
-               \r
-               <p>\r
-                       <strong>Tip:</strong> Try using your keyboard to make a selection.\r
-               </p>\r
-               \r
-               <p>\r
-                       <a href="http://abeautifulsite.net/2008/09/jquery-context-menu-plugin/">Back to the project page</a>\r
-               </p>\r
-               \r
-               <h2>Demo</h2>\r
-               \r
-               <div id="myDiv">\r
-                       Right click to view the context menu\r
-               </div>\r
-               \r
-               <div id="myList">\r
-                       <ul>\r
-                               <li>Item 1</li>\r
-                               <li>Item 2</li>\r
-                               <li>Item 3</li>\r
-                               <li>Item 4</li>\r
-                               <li>Item 5</li>\r
-                               <li>Item 6</li>\r
-                       </ul>\r
-               </div>\r
-               \r
-               <div id="options">\r
-                       <p>\r
-                               <input type="button" id="disableItems" value="Disable Cut/Copy" />\r
-                               <input type="button" id="enableItems" value="Enable Cut/Copy" disabled="disabled" />\r
-                       </p>\r
-                       \r
-                       <p>\r
-                               <input type="button" id="disableMenus" value="Disable Context Menus" />\r
-                               <input type="button" id="enableMenus" value="Enable Context Menus" disabled="disabled" />\r
-                       </p>\r
-               </div>\r
-               \r
-               <ul id="myMenu" class="contextMenu">\r
-                       <li class="edit"><a href="#edit">Edit</a></li>\r
-                       <li class="cut separator"><a href="#cut">Cut</a>\r
-                         <ul>\r
-                           <li class="pe"><a href="#pe">Pe</a></li>\r
-                         </ul>\r
-                       </li>\r
-                       <li class="copy"><a href="#copy">Copy</a></li>\r
-                       <li class="paste"><a href="#paste">Paste</a></li>\r
-                       <li class="delete"><a href="#delete">Delete</a></li>\r
-                       <li class="quit separator"><a href="#quit">Quit</a></li>\r
-               </ul>\r
-               \r
-       </body>\r
-</html>\r
diff --git a/redakcja/static/contextmenu/jquery.contextMenu.css b/redakcja/static/contextmenu/jquery.contextMenu.css
deleted file mode 100755 (executable)
index 46af248..0000000
+++ /dev/null
@@ -1,134 +0,0 @@
-/*!\r
- * jQuery contextMenu - Plugin for simple contextMenu handling\r
- *\r
- * Version: 1.5.13\r
- *\r
- * Authors: Rodney Rehm, Addy Osmani (patches for FF)\r
- * Web: http://medialize.github.com/jQuery-contextMenu/\r
- *\r
- * Licensed under\r
- *   MIT License http://www.opensource.org/licenses/mit-license\r
- *   GPL v3 http://opensource.org/licenses/GPL-3.0\r
- *\r
- */\r
-\r
-.context-menu-list {\r
-    margin:0; \r
-    padding:0;\r
-    \r
-    min-width: 120px;\r
-    max-width: 250px;\r
-    display: inline-block;\r
-    position: absolute;\r
-    list-style-type: none;\r
-    \r
-    border: 1px solid #DDD;\r
-    background: #EEE;\r
-    \r
-    -webkit-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.5);\r
-       -moz-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.5);\r
-        -ms-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.5);\r
-         -o-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.5);\r
-            box-shadow: 0 2px 5px rgba(0, 0, 0, 0.5);\r
-    \r
-    font-family: Verdana, Arial, Helvetica, sans-serif;\r
-    font-size: 11px;\r
-}\r
-\r
-.context-menu-item {\r
-    padding: 2px 2px 2px 24px;\r
-    background-color: #EEE;\r
-    position: relative;\r
-    -moz-user-select: -moz-none;\r
-}\r
-\r
-.context-menu-separator {\r
-    padding-bottom:0;\r
-    border-bottom: 1px solid #DDD;\r
-}\r
-\r
-.context-menu-item > label {\r
-    -moz-user-select: text;\r
-}\r
-\r
-.context-menu-item.hover {\r
-    cursor: pointer;\r
-    background-color: #39F;\r
-}\r
-\r
-.context-menu-item.disabled {\r
-    color: #666;\r
-}\r
-\r
-.context-menu-input.hover,\r
-.context-menu-item.disabled.hover {\r
-    cursor: default;\r
-    background-color: #EEE;\r
-}\r
-\r
-.context-menu-submenu:after {\r
-    content: ">";\r
-    color: #666;\r
-    position: absolute;\r
-    top: 0;\r
-    right: 3px;\r
-    z-index: 1;\r
-}\r
-\r
-/* icons\r
-    #protip:\r
-    In case you want to use sprites for icons (which I would suggest you do) have a look at\r
-    http://css-tricks.com/13224-pseudo-spriting/ to get an idea of how to implement \r
-    .context-menu-item.icon:before {}\r
- */\r
-.context-menu-item.icon { min-height: 18px; background-repeat: no-repeat; background-position: 4px 2px; }\r
-.context-menu-item.icon-edit { background-image: url(images/page_white_edit.png); }\r
-.context-menu-item.icon-cut { background-image: url(images/cut.png); }\r
-.context-menu-item.icon-copy { background-image: url(images/page_white_copy.png); }\r
-.context-menu-item.icon-paste { background-image: url(images/page_white_paste.png); }\r
-.context-menu-item.icon-delete { background-image: url(images/page_white_delete.png); }\r
-.context-menu-item.icon-quit { background-image: url(images/door.png); }\r
-\r
-/* vertically align inside labels */\r
-.context-menu-input > label > * { vertical-align: top; }\r
-\r
-/* position checkboxes and radios as icons */\r
-.context-menu-input > label > input[type="checkbox"],\r
-.context-menu-input > label > input[type="radio"] {\r
-    margin-left: -17px;\r
-}\r
-.context-menu-input > label > span {\r
-    margin-left: 5px;\r
-}\r
-\r
-.context-menu-input > label,\r
-.context-menu-input > label > input[type="text"],\r
-.context-menu-input > label > textarea,\r
-.context-menu-input > label > select {\r
-    display: block;\r
-    width: 100%;\r
-    \r
-    -webkit-box-sizing: border-box;\r
-       -moz-box-sizing: border-box;\r
-        -ms-box-sizing: border-box;\r
-         -o-box-sizing: border-box;\r
-            box-sizing: border-box;\r
-}\r
-\r
-.context-menu-input > label > textarea {\r
-    height: 100px;\r
-}\r
-.context-menu-item > .context-menu-list {\r
-    display: none;\r
-    /* re-positioned by js */\r
-    right: -5px;\r
-    top: 5px;\r
-}\r
-\r
-.context-menu-item.hover > .context-menu-list {\r
-    display: block;\r
-}\r
-\r
-.context-menu-accesskey {\r
-    text-decoration: underline;\r
-}\r
diff --git a/redakcja/static/contextmenu/jquery.contextMenu.js b/redakcja/static/contextmenu/jquery.contextMenu.js
deleted file mode 100755 (executable)
index 53c3fe9..0000000
+++ /dev/null
@@ -1,1485 +0,0 @@
-/*!\r
- * jQuery contextMenu - Plugin for simple contextMenu handling\r
- *\r
- * Version: 1.5.13\r
- *\r
- * Authors: Rodney Rehm, Addy Osmani (patches for FF)\r
- * Web: http://medialize.github.com/jQuery-contextMenu/\r
- *\r
- * Licensed under\r
- *   MIT License http://www.opensource.org/licenses/mit-license\r
- *   GPL v3 http://opensource.org/licenses/GPL-3.0\r
- *\r
- */\r
-\r
-(function($, undefined){\r
-    \r
-    // TODO: -\r
-        // ARIA stuff: menuitem, menuitemcheckbox und menuitemradio\r
-        // create <menu> structure if $.support[htmlCommand || htmlMenuitem] and !opt.disableNative\r
-\r
-// determine html5 compatibility\r
-$.support.htmlMenuitem = ('HTMLMenuItemElement' in window);\r
-$.support.htmlCommand = ('HTMLCommandElement' in window);\r
-\r
-var // currently active contextMenu trigger\r
-    $currentTrigger = null,\r
-    // is contextMenu initialized with at least one menu?\r
-    initialized = false,\r
-    // window handle\r
-    $win = $(window),\r
-    // number of registered menus\r
-    counter = 0,\r
-    // mapping selector to namespace\r
-    namespaces = {},\r
-    // mapping namespace to options\r
-    menus = {},\r
-    // custom command type handlers\r
-    types = {},\r
-    // default values\r
-    defaults = {\r
-        // selector of contextMenu trigger\r
-        selector: null,\r
-        // where to append the menu to\r
-        appendTo: null,\r
-        // method to trigger context menu ["right", "left", "hover"]\r
-        trigger: "right",\r
-        // hide menu when mouse leaves trigger / menu elements\r
-        autoHide: false,\r
-        // ms to wait before showing a hover-triggered context menu\r
-        delay: 200,\r
-        // determine position to show menu at\r
-        determinePosition: function($menu) {\r
-            // position to the lower middle of the trigger element\r
-            if ($.ui && $.ui.position) {\r
-                // .position() is provided as a jQuery UI utility\r
-                // (...and it won't work on hidden elements)\r
-                $menu.css('display', 'block').position({\r
-                    my: "center top",\r
-                    at: "center bottom",\r
-                    of: this,\r
-                    offset: "0 5",\r
-                    collision: "fit"\r
-                }).css('display', 'none');\r
-            } else {\r
-                // determine contextMenu position\r
-                var offset = this.offset();\r
-                offset.top += this.outerHeight();\r
-                offset.left += this.outerWidth() / 2 - $menu.outerWidth() / 2;\r
-                $menu.css(offset);\r
-            }\r
-        },\r
-        // position menu\r
-        position: function(opt, x, y) {\r
-            var $this = this,\r
-                offset;\r
-            // determine contextMenu position\r
-            if (!x && !y) {\r
-                opt.determinePosition.call(this, opt.$menu);\r
-                return;\r
-            } else if (x === "maintain" && y === "maintain") {\r
-                // x and y must not be changed (after re-show on command click)\r
-                offset = opt.$menu.position();\r
-            } else {\r
-                // x and y are given (by mouse event)\r
-                var triggerIsFixed = opt.$trigger.parents().andSelf()\r
-                    .filter(function() {\r
-                        return $(this).css('position') == "fixed";\r
-                    }).length;\r
-\r
-                if (triggerIsFixed) {\r
-                    y -= $win.scrollTop();\r
-                    x -= $win.scrollLeft();\r
-                }\r
-                offset = {top: y, left: x};\r
-            }\r
-            \r
-            // correct offset if viewport demands it\r
-            var bottom = $win.scrollTop() + $win.height(),\r
-                right = $win.scrollLeft() + $win.width(),\r
-                height = opt.$menu.height(),\r
-                width = opt.$menu.width();\r
-            \r
-            if (offset.top + height > bottom) {\r
-                offset.top -= height;\r
-            }\r
-            \r
-            if (offset.left + width > right) {\r
-                offset.left -= width;\r
-            }\r
-            \r
-            opt.$menu.css(offset);\r
-        },\r
-        // position the sub-menu\r
-        positionSubmenu: function($menu) {\r
-            if ($.ui && $.ui.position) {\r
-                // .position() is provided as a jQuery UI utility\r
-                // (...and it won't work on hidden elements)\r
-                $menu.css('display', 'block').position({\r
-                    my: "left top",\r
-                    at: "right top",\r
-                    of: this,\r
-                    collision: "fit"\r
-                }).css('display', '');\r
-            } else {\r
-                // determine contextMenu position\r
-                var offset = this.offset();\r
-                offset.top += 0;\r
-                offset.left += this.outerWidth();\r
-                $menu.css(offset);\r
-            }\r
-        },\r
-        // offset to add to zIndex\r
-        zIndex: 1,\r
-        // show hide animation settings\r
-        animation: {\r
-            duration: 50,\r
-            show: 'slideDown',\r
-            hide: 'slideUp'\r
-        },\r
-        // events\r
-        events: {\r
-            show: $.noop,\r
-            hide: $.noop\r
-        },\r
-        // default callback\r
-        callback: null,\r
-        // list of contextMenu items\r
-        items: {}\r
-    },\r
-    // mouse position for hover activation\r
-    hoveract = {\r
-        timer: null,\r
-        pageX: null,\r
-        pageY: null\r
-    },\r
-    // determine zIndex\r
-    zindex = function($t) {\r
-        var zin = 0,\r
-            $tt = $t;\r
-\r
-        while (true) {\r
-            zin = Math.max(zin, parseInt($tt.css('z-index'), 10) || 0);\r
-            $tt = $tt.parent();\r
-            if (!$tt || !$tt.length || $tt.prop('nodeName').toLowerCase() == 'body') {\r
-                break;\r
-            }\r
-        }\r
-        \r
-        return zin;\r
-    },\r
-    // event handlers\r
-    handle = {\r
-        // abort anything\r
-        abortevent: function(e){\r
-            e.preventDefault();\r
-            e.stopImmediatePropagation();\r
-        },\r
-        \r
-        // contextmenu show dispatcher\r
-        contextmenu: function(e) {\r
-            var $this = $(this);\r
-            \r
-            // disable actual context-menu\r
-            e.preventDefault();\r
-            e.stopImmediatePropagation();\r
-            \r
-            // abort native-triggered events unless we're triggering on right click\r
-            if (e.data.trigger != 'right' && e.originalEvent) {\r
-                return;\r
-            }\r
-            \r
-            if (!$this.hasClass('context-menu-disabled')) {\r
-                // theoretically need to fire a show event at <menu>\r
-                // http://www.whatwg.org/specs/web-apps/current-work/multipage/interactive-elements.html#context-menus\r
-                // var evt = jQuery.Event("show", { data: data, pageX: e.pageX, pageY: e.pageY, relatedTarget: this });\r
-                // e.data.$menu.trigger(evt);\r
-                \r
-                $currentTrigger = $this;\r
-                if (e.data.build) {\r
-                    var built = e.data.build($currentTrigger, e);\r
-                    // abort if build() returned false\r
-                    if (built === false) {\r
-                        return;\r
-                    }\r
-                    \r
-                    // dynamically build menu on invocation\r
-                    e.data = $.extend(true, defaults, e.data, built || {});\r
-\r
-                    // abort if there are no items to display\r
-                    if (!e.data.items || $.isEmptyObject(e.data.items)) {\r
-                        // Note: jQuery captures and ignores errors from event handlers\r
-                        if (window.console) {\r
-                            (console.error || console.log)("No items specified to show in contextMenu");\r
-                        }\r
-                        \r
-                        throw new Error('No Items sepcified');\r
-                    }\r
-                    \r
-                    // backreference for custom command type creation\r
-                    e.data.$trigger = $currentTrigger;\r
-                    \r
-                    op.create(e.data);\r
-                }\r
-                // show menu\r
-                op.show.call($this, e.data, e.pageX, e.pageY);\r
-            }\r
-        },\r
-        // contextMenu left-click trigger\r
-        click: function(e) {\r
-            e.preventDefault();\r
-            e.stopImmediatePropagation();\r
-            $(this).trigger(jQuery.Event("contextmenu", { data: e.data, pageX: e.pageX, pageY: e.pageY }));\r
-        },\r
-        // contextMenu right-click trigger\r
-        mousedown: function(e) {\r
-            // register mouse down\r
-            var $this = $(this);\r
-            \r
-            // hide any previous menus\r
-            if ($currentTrigger && $currentTrigger.length && !$currentTrigger.is($this)) {\r
-                $currentTrigger.data('contextMenu').$menu.trigger('contextmenu:hide');\r
-            }\r
-            \r
-            // activate on right click\r
-            if (e.button == 2) {\r
-                $currentTrigger = $this.data('contextMenuActive', true);\r
-            }\r
-        },\r
-        // contextMenu right-click trigger\r
-        mouseup: function(e) {\r
-            // show menu\r
-            var $this = $(this);\r
-            if ($this.data('contextMenuActive') && $currentTrigger && $currentTrigger.length && $currentTrigger.is($this) && !$this.hasClass('context-menu-disabled')) {\r
-                e.preventDefault();\r
-                e.stopImmediatePropagation();\r
-                $currentTrigger = $this;\r
-                $this.trigger(jQuery.Event("contextmenu", { data: e.data, pageX: e.pageX, pageY: e.pageY }));\r
-            }\r
-            \r
-            $this.removeData('contextMenuActive');\r
-        },\r
-        // contextMenu hover trigger\r
-        mouseenter: function(e) {\r
-            var $this = $(this),\r
-                $related = $(e.relatedTarget),\r
-                $document = $(document);\r
-            \r
-            // abort if we're coming from a menu\r
-            if ($related.is('.context-menu-list') || $related.closest('.context-menu-list').length) {\r
-                return;\r
-            }\r
-            \r
-            // abort if a menu is shown\r
-            if ($currentTrigger && $currentTrigger.length) {\r
-                return;\r
-            }\r
-            \r
-            hoveract.pageX = e.pageX;\r
-            hoveract.pageY = e.pageY;\r
-            hoveract.data = e.data;\r
-            $document.on('mousemove.contextMenuShow', handle.mousemove);\r
-            hoveract.timer = setTimeout(function() {\r
-                hoveract.timer = null;\r
-                $document.off('mousemove.contextMenuShow');\r
-                $currentTrigger = $this;\r
-                $this.trigger(jQuery.Event("contextmenu", { data: hoveract.data, pageX: hoveract.pageX, pageY: hoveract.pageY }));\r
-            }, e.data.delay );\r
-        },\r
-        // contextMenu hover trigger\r
-        mousemove: function(e) {\r
-            hoveract.pageX = e.pageX;\r
-            hoveract.pageY = e.pageY;\r
-        },\r
-        // contextMenu hover trigger\r
-        mouseleave: function(e) {\r
-            // abort if we're leaving for a menu\r
-            var $related = $(e.relatedTarget);\r
-            if ($related.is('.context-menu-list') || $related.closest('.context-menu-list').length) {\r
-                return;\r
-            }\r
-            \r
-            try {\r
-                clearTimeout(hoveract.timer);\r
-            } catch(e) {}\r
-            \r
-            hoveract.timer = null;\r
-        },\r
-        \r
-        // click on layer to hide contextMenu\r
-        layerClick: function(e) {\r
-            var $this = $(this),\r
-                root = $this.data('contextMenuRoot');\r
-                \r
-            e.preventDefault();\r
-            e.stopImmediatePropagation();\r
-            \r
-            if ((root.trigger == 'left' && e.button == 0) || (root.trigger == 'right' && e.button == 2)) {\r
-                var offset = root.$trigger.offset();\r
-                \r
-                // while this looks kinda awful, it's the best way to avoid\r
-                // unnecessarily calculating any positions\r
-                offset.top += $(window).scrollTop();\r
-                if (offset.top <= e.pageY) {\r
-                    offset.left += $(window).scrollLeft();\r
-                    if (offset.left <= e.pageX) {\r
-                        offset.bottom = offset.top + root.$trigger.outerHeight();\r
-                        if (offset.bottom >= e.pageY) {\r
-                            offset.right = offset.left + root.$trigger.outerWidth();\r
-                            if (offset.right >= e.pageX) {\r
-                                // reposition\r
-                                root.position.call(root.$trigger, root, e.pageX, e.pageY);\r
-                                return;\r
-                            }\r
-                        }\r
-                    }\r
-                }\r
-            } \r
-            \r
-            // remove only after mouseup has completed\r
-            $this.on('mouseup', function(e) {\r
-                e.preventDefault();\r
-                e.stopImmediatePropagation();\r
-                root.$menu.trigger('contextmenu:hide');\r
-            });\r
-        },\r
-        // key handled :hover\r
-        keyStop: function(e, opt) {\r
-            if (!opt.isInput) {\r
-                e.preventDefault();\r
-            }\r
-            \r
-            e.stopPropagation();\r
-        },\r
-        key: function(e) {\r
-            var opt = $currentTrigger.data('contextMenu') || {},\r
-                $children = opt.$menu.children(),\r
-                $round;\r
-\r
-            switch (e.keyCode) {\r
-                case 9:\r
-                case 38: // up\r
-                    handle.keyStop(e, opt);\r
-                    // if keyCode is [38 (up)] or [9 (tab) with shift]\r
-                    if (opt.isInput) {\r
-                        if (e.keyCode == 9 && e.shiftKey) {\r
-                            e.preventDefault();\r
-                            opt.$selected && opt.$selected.find('input, textarea, select').blur();\r
-                            opt.$menu.trigger('prevcommand');\r
-                            return;\r
-                        } else if (e.keyCode == 38 && opt.$selected.find('input, textarea, select').prop('type') == 'checkbox') {\r
-                            // checkboxes don't capture this key\r
-                            e.preventDefault();\r
-                            return;\r
-                        }\r
-                    } else if (e.keyCode != 9 || e.shiftKey) {\r
-                        opt.$menu.trigger('prevcommand');\r
-                        return;\r
-                    }\r
-                    \r
-                case 9: // tab\r
-                case 40: // down\r
-                    handle.keyStop(e, opt);\r
-                    if (opt.isInput) {\r
-                        if (e.keyCode == 9) {\r
-                            e.preventDefault();\r
-                            opt.$selected && opt.$selected.find('input, textarea, select').blur();\r
-                            opt.$menu.trigger('nextcommand');\r
-                            return;\r
-                        } else if (e.keyCode == 40 && opt.$selected.find('input, textarea, select').prop('type') == 'checkbox') {\r
-                            // checkboxes don't capture this key\r
-                            e.preventDefault();\r
-                            return;\r
-                        }\r
-                    } else {\r
-                        opt.$menu.trigger('nextcommand');\r
-                        return;\r
-                    }\r
-                    break;\r
-                \r
-                case 37: // left\r
-                    handle.keyStop(e, opt);\r
-                    if (opt.isInput || !opt.$selected || !opt.$selected.length) {\r
-                        break;\r
-                    }\r
-                \r
-                    if (!opt.$selected.parent().hasClass('context-menu-root')) {\r
-                        var $parent = opt.$selected.parent().parent();\r
-                        opt.$selected.trigger('contextmenu:blur');\r
-                        opt.$selected = $parent;\r
-                        return;\r
-                    }\r
-                    break;\r
-                    \r
-                case 39: // right\r
-                    handle.keyStop(e, opt);\r
-                    if (opt.isInput || !opt.$selected || !opt.$selected.length) {\r
-                        break;\r
-                    }\r
-                    \r
-                    var itemdata = opt.$selected.data('contextMenu') || {};\r
-                    if (itemdata.$menu && opt.$selected.hasClass('context-menu-submenu')) {\r
-                        opt.$selected = null;\r
-                        itemdata.$selected = null;\r
-                        itemdata.$menu.trigger('nextcommand');\r
-                        return;\r
-                    }\r
-                    break;\r
-                \r
-                case 35: // end\r
-                case 36: // home\r
-                    if (opt.$selected && opt.$selected.find('input, textarea, select').length) {\r
-                        return;\r
-                    } else {\r
-                        (opt.$selected && opt.$selected.parent() || opt.$menu)\r
-                            .children(':not(.disabled, .not-selectable)')[e.keyCode == 36 ? 'first' : 'last']()\r
-                            .trigger('contextmenu:focus');\r
-                        e.preventDefault();\r
-                        return;\r
-                    }\r
-                    break;\r
-                    \r
-                case 13: // enter\r
-                    handle.keyStop(e, opt);\r
-                    if (opt.isInput) {\r
-                        if (opt.$selected && !opt.$selected.is('textarea, select')) {\r
-                            e.preventDefault();\r
-                            return;\r
-                        }\r
-                        break;\r
-                    }\r
-                    opt.$selected && opt.$selected.trigger('mouseup');\r
-                    return;\r
-                    \r
-                case 32: // space\r
-                case 33: // page up\r
-                case 34: // page down\r
-                    // prevent browser from scrolling down while menu is visible\r
-                    handle.keyStop(e, opt);\r
-                    return;\r
-                    \r
-                case 27: // esc\r
-                    handle.keyStop(e, opt);\r
-                    opt.$menu.trigger('contextmenu:hide');\r
-                    return;\r
-                    \r
-                default: // 0-9, a-z\r
-                    var k = (String.fromCharCode(e.keyCode)).toUpperCase();\r
-                    if (opt.accesskeys[k]) {\r
-                        // according to the specs accesskeys must be invoked immediately\r
-                        opt.accesskeys[k].$node.trigger(opt.accesskeys[k].$menu\r
-                            ? 'contextmenu:focus'\r
-                            : 'mouseup'\r
-                        );\r
-                        return;\r
-                    }\r
-                    break;\r
-            }\r
-            // pass event to selected item, \r
-            // stop propagation to avoid endless recursion\r
-            e.stopPropagation();\r
-            opt.$selected && opt.$selected.trigger(e);\r
-        },\r
-\r
-        // select previous possible command in menu\r
-        prevItem: function(e) {\r
-            e.stopPropagation();\r
-            var opt = $(this).data('contextMenu') || {};\r
-\r
-            // obtain currently selected menu\r
-            if (opt.$selected) {\r
-                var $s = opt.$selected;\r
-                opt = opt.$selected.parent().data('contextMenu') || {};\r
-                opt.$selected = $s;\r
-            }\r
-            \r
-            var $children = opt.$menu.children(),\r
-                $prev = !opt.$selected || !opt.$selected.prev().length ? $children.last() : opt.$selected.prev(),\r
-                $round = $prev;\r
-            \r
-            // skip disabled\r
-            while ($prev.hasClass('disabled') || $prev.hasClass('not-selectable')) {\r
-                if ($prev.prev().length) {\r
-                    $prev = $prev.prev();\r
-                } else {\r
-                    $prev = $children.last();\r
-                }\r
-                if ($prev.is($round)) {\r
-                    // break endless loop\r
-                    return;\r
-                }\r
-            }\r
-            \r
-            // leave current\r
-            if (opt.$selected) {\r
-                handle.itemMouseleave.call(opt.$selected.get(0), e);\r
-            }\r
-            \r
-            // activate next\r
-            handle.itemMouseenter.call($prev.get(0), e);\r
-            \r
-            // focus input\r
-            var $input = $prev.find('input, textarea, select');\r
-            if ($input.length) {\r
-                $input.focus();\r
-            }\r
-        },\r
-        // select next possible command in menu\r
-        nextItem: function(e) {\r
-            e.stopPropagation();\r
-            var opt = $(this).data('contextMenu') || {};\r
-\r
-            // obtain currently selected menu\r
-            if (opt.$selected) {\r
-                var $s = opt.$selected;\r
-                opt = opt.$selected.parent().data('contextMenu') || {};\r
-                opt.$selected = $s;\r
-            }\r
-\r
-            var $children = opt.$menu.children(),\r
-                $next = !opt.$selected || !opt.$selected.next().length ? $children.first() : opt.$selected.next(),\r
-                $round = $next;\r
-\r
-            // skip disabled\r
-            while ($next.hasClass('disabled') || $next.hasClass('not-selectable')) {\r
-                if ($next.next().length) {\r
-                    $next = $next.next();\r
-                } else {\r
-                    $next = $children.first();\r
-                }\r
-                if ($next.is($round)) {\r
-                    // break endless loop\r
-                    return;\r
-                }\r
-            }\r
-            \r
-            // leave current\r
-            if (opt.$selected) {\r
-                handle.itemMouseleave.call(opt.$selected.get(0), e);\r
-            }\r
-            \r
-            // activate next\r
-            handle.itemMouseenter.call($next.get(0), e);\r
-            \r
-            // focus input\r
-            var $input = $next.find('input, textarea, select');\r
-            if ($input.length) {\r
-                $input.focus();\r
-            }\r
-        },\r
-        \r
-        // flag that we're inside an input so the key handler can act accordingly\r
-        focusInput: function(e) {\r
-            var $this = $(this).closest('.context-menu-item'),\r
-                data = $this.data(),\r
-                opt = data.contextMenu,\r
-                root = data.contextMenuRoot;\r
-\r
-            root.$selected = opt.$selected = $this;\r
-            root.isInput = opt.isInput = true;\r
-        },\r
-        // flag that we're inside an input so the key handler can act accordingly\r
-        blurInput: function(e) {\r
-            var $this = $(this).closest('.context-menu-item'),\r
-                data = $this.data(),\r
-                opt = data.contextMenu,\r
-                root = data.contextMenuRoot;\r
-\r
-            root.isInput = opt.isInput = false;\r
-        },\r
-        \r
-        // :hover on menu\r
-        menuMouseenter: function(e) {\r
-            var root = $(this).data().contextMenuRoot;\r
-            root.hovering = true;\r
-        },\r
-        // :hover on menu\r
-        menuMouseleave: function(e) {\r
-            var root = $(this).data().contextMenuRoot;\r
-            if (root.$layer && root.$layer.is(e.relatedTarget)) {\r
-                root.hovering = false;\r
-            }\r
-        },\r
-        \r
-        // :hover done manually so key handling is possible\r
-        itemMouseenter: function(e) {\r
-            var $this = $(this),\r
-                data = $this.data(),\r
-                opt = data.contextMenu,\r
-                root = data.contextMenuRoot;\r
-            \r
-            root.hovering = true;\r
-\r
-            // abort if we're re-entering\r
-            if (e && root.$layer && root.$layer.is(e.relatedTarget)) {\r
-                e.preventDefault();\r
-                e.stopImmediatePropagation();\r
-            }\r
-\r
-            // make sure only one item is selected\r
-            (opt.$menu ? opt : root).$menu\r
-                .children('.hover').trigger('contextmenu:blur');\r
-\r
-            if ($this.hasClass('disabled') || $this.hasClass('not-selectable')) {\r
-                opt.$selected = null;\r
-                return;\r
-            }\r
-            \r
-            $this.trigger('contextmenu:focus');\r
-        },\r
-        // :hover done manually so key handling is possible\r
-        itemMouseleave: function(e) {\r
-            var $this = $(this),\r
-                data = $this.data(),\r
-                opt = data.contextMenu,\r
-                root = data.contextMenuRoot;\r
-\r
-            if (root !== opt && root.$layer && root.$layer.is(e.relatedTarget)) {\r
-                root.$selected && root.$selected.trigger('contextmenu:blur');\r
-                e.preventDefault();\r
-                e.stopImmediatePropagation();\r
-                root.$selected = opt.$selected = opt.$node;\r
-                return;\r
-            }\r
-            \r
-            $this.trigger('contextmenu:blur');\r
-        },\r
-        // contextMenu item click\r
-        itemClick: function(e) {\r
-            var $this = $(this),\r
-                data = $this.data(),\r
-                opt = data.contextMenu,\r
-                root = data.contextMenuRoot,\r
-                key = data.contextMenuKey,\r
-                callback;\r
-\r
-            // abort if the key is unknown or disabled or is a menu\r
-            if (!opt.items[key] || $this.hasClass('disabled') || $this.hasClass('context-menu-submenu')) {\r
-                return;\r
-            }\r
-\r
-            e.preventDefault();\r
-            e.stopImmediatePropagation();\r
-\r
-            if ($.isFunction(root.callbacks[key])) {\r
-                // item-specific callback\r
-                callback = root.callbacks[key];\r
-            } else if ($.isFunction(root.callback)) {\r
-                // default callback\r
-                callback = root.callback;                \r
-            } else {\r
-                // no callback, no action\r
-                return;\r
-            }\r
-\r
-            // hide menu if callback doesn't stop that\r
-            if (callback.call(root.$trigger, key, root) !== false) {\r
-                root.$menu.trigger('contextmenu:hide');\r
-            } else {\r
-                op.update.call(root.$trigger, root);\r
-            }\r
-        },\r
-        // ignore click events on input elements\r
-        inputClick: function(e) {\r
-            e.stopImmediatePropagation();\r
-        },\r
-        \r
-        // hide <menu>\r
-        hideMenu: function(e) {\r
-            var root = $(this).data('contextMenuRoot');\r
-            op.hide.call(root.$trigger, root);\r
-        },\r
-        // focus <command>\r
-        focusItem: function(e) {\r
-            e.stopPropagation();\r
-            var $this = $(this),\r
-                data = $this.data(),\r
-                opt = data.contextMenu,\r
-                root = data.contextMenuRoot;\r
-\r
-            $this.addClass('hover')\r
-                .siblings('.hover').trigger('contextmenu:blur');\r
-            \r
-            // remember selected\r
-            opt.$selected = root.$selected = $this;\r
-            \r
-            // position sub-menu - do after show so dumb $.ui.position can keep up\r
-            if (opt.$node) {\r
-                root.positionSubmenu.call(opt.$node, opt.$menu);\r
-            }\r
-        },\r
-        // blur <command>\r
-        blurItem: function(e) {\r
-            e.stopPropagation();\r
-            var $this = $(this),\r
-                data = $this.data(),\r
-                opt = data.contextMenu,\r
-                root = data.contextMenuRoot;\r
-            \r
-            $this.removeClass('hover');\r
-            opt.$selected = null;\r
-        }\r
-    },\r
-    // operations\r
-    op = {\r
-        show: function(opt, x, y) {\r
-            var $this = $(this),\r
-                offset,\r
-                css = {};\r
-\r
-            // hide any open menus\r
-            $('#context-menu-layer').trigger('mousedown');\r
-\r
-            // backreference for callbacks\r
-            opt.$trigger = $this;\r
-\r
-            // show event\r
-            if (opt.events.show.call($this, opt) === false) {\r
-                $currentTrigger = null;\r
-                return;\r
-            }\r
-            \r
-            // create or update context menu\r
-            op.update.call($this, opt);\r
-            \r
-            // position menu\r
-            opt.position.call($this, opt, x, y);\r
-\r
-            // make sure we're in front\r
-            if (opt.zIndex) {\r
-                css.zIndex = zindex($this) + opt.zIndex;\r
-            }\r
-            \r
-            // add layer\r
-            op.layer.call(opt.$menu, opt, css.zIndex);\r
-            \r
-            // adjust sub-menu zIndexes\r
-            opt.$menu.find('ul').css('zIndex', css.zIndex + 1);\r
-            \r
-            // position and show context menu\r
-            opt.$menu.css( css )[opt.animation.show](opt.animation.duration);\r
-            // make options available\r
-            $this.data('contextMenu', opt);\r
-            // register key handler\r
-            $(document).off('keydown.contextMenu').on('keydown.contextMenu', handle.key);\r
-            // register autoHide handler\r
-            if (opt.autoHide) {\r
-                // trigger element coordinates\r
-                var pos = $this.position();\r
-                pos.right = pos.left + $this.outerWidth();\r
-                pos.bottom = pos.top + this.outerHeight();\r
-                // mouse position handler\r
-                $(document).on('mousemove.contextMenuAutoHide', function(e) {\r
-                    if (opt.$layer && !opt.hovering && (!(e.pageX >= pos.left && e.pageX <= pos.right) || !(e.pageY >= pos.top && e.pageY <= pos.bottom))) {\r
-                        // if mouse in menu...\r
-                        opt.$layer.trigger('mousedown');\r
-                    }\r
-                });\r
-            }\r
-        },\r
-        hide: function(opt) {\r
-            var $this = $(this);\r
-            if (!opt) {\r
-                opt = $this.data('contextMenu') || {};\r
-            }\r
-            \r
-            // hide event\r
-            if (opt.events && opt.events.hide.call($this, opt) === false) {\r
-                return;\r
-            }\r
-            \r
-            if (opt.$layer) {\r
-                // keep layer for a bit so the contextmenu event can be aborted properly by opera\r
-                setTimeout((function($layer){ return function(){\r
-                        $layer.remove();\r
-                    };\r
-                })(opt.$layer), 10);\r
-                \r
-                try {\r
-                    delete opt.$layer;\r
-                } catch(e) {\r
-                    opt.$layer = null;\r
-                }\r
-            }\r
-            \r
-            // remove handle\r
-            $currentTrigger = null;\r
-            // remove selected\r
-            opt.$menu.find('.hover').trigger('contextmenu:blur');\r
-            opt.$selected = null;\r
-            // unregister key and mouse handlers\r
-            //$(document).off('.contextMenuAutoHide keydown.contextMenu'); // http://bugs.jquery.com/ticket/10705\r
-            $(document).off('.contextMenuAutoHide').off('keydown.contextMenu');\r
-            // hide menu\r
-            opt.$menu && opt.$menu[opt.animation.hide](opt.animation.duration);\r
-            \r
-            // tear down dynamically built menu\r
-            if (opt.build) {\r
-                opt.$menu.remove();\r
-                $.each(opt, function(key, value) {\r
-                    switch (key) {\r
-                        case 'ns':\r
-                        case 'selector':\r
-                        case 'build':\r
-                        case 'trigger':\r
-                            return true;\r
-\r
-                        default:\r
-                            opt[key] = undefined;\r
-                            try {\r
-                                delete opt[key];\r
-                            } catch (e) {}\r
-                            return true;\r
-                   }\r
-                });\r
-            }\r
-        },\r
-        create: function(opt, root) {\r
-            if (root === undefined) {\r
-                root = opt;\r
-            }\r
-            // create contextMenu\r
-            opt.$menu = $('<ul class="context-menu-list ' + (opt.className || "") + '"></ul>').data({\r
-                'contextMenu': opt,\r
-                'contextMenuRoot': root\r
-            });\r
-            \r
-            $.each(['callbacks', 'commands', 'inputs'], function(i,k){\r
-                opt[k] = {};\r
-                if (!root[k]) {\r
-                    root[k] = {};\r
-                }\r
-            });\r
-            \r
-            root.accesskeys || (root.accesskeys = {});\r
-            \r
-            // create contextMenu items\r
-            $.each(opt.items, function(key, item){\r
-                var $t = $('<li class="context-menu-item ' + (item.className || "") +'"></li>'),\r
-                    $label = null,\r
-                    $input = null;\r
-                \r
-                item.$node = $t.data({\r
-                    'contextMenu': opt,\r
-                    'contextMenuRoot': root,\r
-                    'contextMenuKey': key\r
-                });\r
-                \r
-                // register accesskey\r
-                // NOTE: the accesskey attribute should be applicable to any element, but Safari5 and Chrome13 still can't do that\r
-                if (item.accesskey) {\r
-                    var aks = splitAccesskey(item.accesskey);\r
-                    for (var i=0, ak; ak = aks[i]; i++) {\r
-                        if (!root.accesskeys[ak]) {\r
-                            root.accesskeys[ak] = item;\r
-                            item._name = item.name.replace(new RegExp('(' + ak + ')', 'i'), '<span class="context-menu-accesskey">$1</span>');\r
-                            break;\r
-                        }\r
-                    }\r
-                }\r
-                \r
-                if (typeof item == "string") {\r
-                    $t.addClass('context-menu-separator not-selectable');\r
-                } else if (item.type && types[item.type]) {\r
-                    // run custom type handler\r
-                    types[item.type].call($t, item, opt, root);\r
-                    // register commands\r
-                    $.each([opt, root], function(i,k){\r
-                        k.commands[key] = item;\r
-                        if ($.isFunction(item.callback)) {\r
-                            k.callbacks[key] = item.callback;\r
-                        }\r
-                    });\r
-                } else {\r
-                    // add label for input\r
-                    if (item.type == 'html') {\r
-                        $t.addClass('context-menu-html not-selectable');\r
-                    } else if (item.type) {\r
-                        $label = $('<label></label>').appendTo($t);\r
-                        $('<span></span>').html(item._name || item.name).appendTo($label);\r
-                        $t.addClass('context-menu-input');\r
-                        opt.hasTypes = true;\r
-                        $.each([opt, root], function(i,k){\r
-                            k.commands[key] = item;\r
-                            k.inputs[key] = item;\r
-                        });\r
-                    } else if (item.items) {\r
-                        item.type = 'sub';\r
-                    }\r
-                \r
-                    switch (item.type) {\r
-                        case 'text':\r
-                            $input = $('<input type="text" value="1" name="context-menu-input-'+ key +'" value="">')\r
-                                .val(item.value || "").appendTo($label);\r
-                            break;\r
-                    \r
-                        case 'textarea':\r
-                            $input = $('<textarea name="context-menu-input-'+ key +'"></textarea>')\r
-                                .val(item.value || "").appendTo($label);\r
-\r
-                            if (item.height) {\r
-                                $input.height(item.height);\r
-                            }\r
-                            break;\r
-\r
-                        case 'checkbox':\r
-                            $input = $('<input type="checkbox" value="1" name="context-menu-input-'+ key +'" value="">')\r
-                                .val(item.value || "").prop("checked", !!item.selected).prependTo($label);\r
-                            break;\r
-\r
-                        case 'radio':\r
-                            $input = $('<input type="radio" value="1" name="context-menu-input-'+ item.radio +'" value="">')\r
-                                .val(item.value || "").prop("checked", !!item.selected).prependTo($label);\r
-                            break;\r
-                    \r
-                        case 'select':\r
-                            $input = $('<select name="context-menu-input-'+ key +'">').appendTo($label);\r
-                            if (item.options) {\r
-                                $.each(item.options, function(value, text) {\r
-                                    $('<option></option>').val(value).text(text).appendTo($input);\r
-                                });\r
-                                $input.val(item.selected);\r
-                            }\r
-                            break;\r
-                        \r
-                        case 'sub':\r
-                            $('<span></span>').html(item._name || item.name).appendTo($t);\r
-                            item.appendTo = item.$node;\r
-                            op.create(item, root);\r
-                            $t.data('contextMenu', item).addClass('context-menu-submenu');\r
-                            item.callback = null;\r
-                            break;\r
-                        \r
-                        case 'html':\r
-                            $(item.html).appendTo($t);\r
-                            break;\r
-                        \r
-                        default:\r
-                            $.each([opt, root], function(i,k){\r
-                                k.commands[key] = item;\r
-                                if ($.isFunction(item.callback)) {\r
-                                    k.callbacks[key] = item.callback;\r
-                                }\r
-                            });\r
-                            \r
-                            $('<span></span>').html(item._name || item.name || "").appendTo($t);\r
-                            break;\r
-                    }\r
-                    \r
-                    // disable key listener in <input>\r
-                    if (item.type && item.type != 'sub' && item.type != 'html') {\r
-                        $input\r
-                            .on('focus', handle.focusInput)\r
-                            .on('blur', handle.blurInput);\r
-                        \r
-                        if (item.events) {\r
-                            $input.on(item.events);\r
-                        }\r
-                    }\r
-                \r
-                    // add icons\r
-                    if (item.icon) {\r
-                        $t.addClass("icon icon-" + item.icon);\r
-                    }\r
-                }\r
-                \r
-                // cache contained elements\r
-                item.$input = $input;\r
-                item.$label = $label;\r
-\r
-                // attach item to menu\r
-                $t.appendTo(opt.$menu);\r
-                \r
-                // Disable text selection\r
-                if (!opt.hasTypes) {\r
-                    if($.browser.msie) {\r
-                        $t.on('selectstart.disableTextSelect', handle.abortevent);\r
-                    } else if(!$.browser.mozilla) {\r
-                        $t.on('mousedown.disableTextSelect', handle.abortevent);\r
-                    }\r
-                }\r
-            });\r
-            // attach contextMenu to <body> (to bypass any possible overflow:hidden issues on parents of the trigger element)\r
-            if (!opt.$node) {\r
-                opt.$menu.css('display', 'none').addClass('context-menu-root');\r
-            }\r
-            opt.$menu.appendTo(opt.appendTo || document.body);\r
-        },\r
-        update: function(opt, root) {\r
-            var $this = this;\r
-            if (root === undefined) {\r
-                root = opt;\r
-                // determine widths of submenus, as CSS won't grow them automatically\r
-                // position:absolute > position:absolute; min-width:100; max-width:200; results in width: 100;\r
-                // kinda sucks hard...\r
-                opt.$menu.find('ul').andSelf().css({position: 'static', display: 'block'}).each(function(){\r
-                    var $this = $(this);\r
-                    $this.width($this.css('position', 'absolute').width())\r
-                        .css('position', 'static');\r
-                }).css({position: '', display: ''});\r
-            }\r
-            // re-check disabled for each item\r
-            opt.$menu.children().each(function(){\r
-                var $item = $(this),\r
-                    key = $item.data('contextMenuKey'),\r
-                    item = opt.items[key],\r
-                    disabled = ($.isFunction(item.disabled) && item.disabled.call($this, key, root)) || item.disabled === true;\r
-\r
-                // dis- / enable item\r
-                $item[disabled ? 'addClass' : 'removeClass']('disabled');\r
-                \r
-                if (item.type) {\r
-                    // dis- / enable input elements\r
-                    $item.find('input, select, textarea').prop('disabled', disabled);\r
-                    \r
-                    // update input states\r
-                    switch (item.type) {\r
-                        case 'text':\r
-                        case 'textarea':\r
-                            item.$input.val(item.value || "");\r
-                            break;\r
-                            \r
-                        case 'checkbox':\r
-                        case 'radio':\r
-                            item.$input.val(item.value || "").prop('checked', !!item.selected);\r
-                            break;\r
-                            \r
-                        case 'select':\r
-                            item.$input.val(item.selected || "");\r
-                            break;\r
-                    }\r
-                }\r
-                \r
-                if (item.$menu) {\r
-                    // update sub-menu\r
-                    op.update.call($this, item, root);\r
-                }\r
-            });\r
-        },\r
-        layer: function(opt, zIndex) {\r
-            // add transparent layer for click area\r
-            // filter and background for Internet Explorer, Issue #23\r
-            return opt.$layer = $('<div id="context-menu-layer" style="position:fixed; z-index:' + zIndex + '; top:0; left:0; opacity: 0; filter: alpha(opacity=0); background-color: #000;"></div>')\r
-                .css({height: $win.height(), width: $win.width(), display: 'block'})\r
-                .data('contextMenuRoot', opt)\r
-                .insertBefore(this)\r
-                .on('contextmenu', handle.abortevent)\r
-                .on('mousedown', handle.layerClick);\r
-        }\r
-    };\r
-\r
-// split accesskey according to http://www.whatwg.org/specs/web-apps/current-work/multipage/editing.html#assigned-access-key\r
-function splitAccesskey(val) {\r
-    var t = val.split(/\s+/),\r
-        keys = [];\r
-        \r
-    for (var i=0, k; k = t[i]; i++) {\r
-        k = k[0].toUpperCase(); // first character only\r
-        // theoretically non-accessible characters should be ignored, but different systems, different keyboard layouts, ... screw it.\r
-        // a map to look up already used access keys would be nice\r
-        keys.push(k);\r
-    }\r
-    \r
-    return keys;\r
-}\r
-\r
-// handle contextMenu triggers\r
-$.fn.contextMenu = function(operation) {\r
-    if (operation === undefined) {\r
-        this.first().trigger('contextmenu');\r
-    } else if (operation.x && operation.y) {\r
-        this.first().trigger(jQuery.Event("contextmenu", {pageX: operation.x, pageY: operation.y}));\r
-    } else if (operation === "hide") {\r
-        var $menu = this.data('contextMenu').$menu;\r
-        $menu && $menu.trigger('contextmenu:hide');\r
-    } else if (operation) {\r
-        this.removeClass('context-menu-disabled');\r
-    } else if (!operation) {\r
-        this.addClass('context-menu-disabled');\r
-    }\r
-    \r
-    return this;\r
-};\r
-\r
-// manage contextMenu instances\r
-$.contextMenu = function(operation, options) {\r
-    if (typeof operation != 'string') {\r
-        options = operation;\r
-        operation = 'create';\r
-    }\r
-    \r
-    if (typeof options == 'string') {\r
-        options = {selector: options};\r
-    } else if (options === undefined) {\r
-        options = {};\r
-    }\r
-    \r
-    // merge with default options\r
-    var o = $.extend(true, {}, defaults, options || {}),\r
-        $body = $body = $(document);\r
-    \r
-    switch (operation) {\r
-        case 'create':\r
-            // no selector no joy\r
-            if (!o.selector) {\r
-                throw new Error('No selector specified');\r
-            }\r
-            // make sure internal classes are not bound to\r
-            if (o.selector.match(/.context-menu-(list|item|input)($|\s)/)) {\r
-                throw new Error('Cannot bind to selector "' + o.selector + '" as it contains a reserved className');\r
-            }\r
-            if (!o.build && (!o.items || $.isEmptyObject(o.items))) {\r
-                throw new Error('No Items sepcified');\r
-            }\r
-            counter ++;\r
-            o.ns = '.contextMenu' + counter;\r
-            namespaces[o.selector] = o.ns;\r
-            menus[o.ns] = o;\r
-            \r
-            if (!initialized) {\r
-                // make sure item click is registered first\r
-                $body\r
-                    .on({\r
-                        'contextmenu:hide.contextMenu': handle.hideMenu,\r
-                        'prevcommand.contextMenu': handle.prevItem,\r
-                        'nextcommand.contextMenu': handle.nextItem,\r
-                        'contextmenu.contextMenu': handle.abortevent,\r
-                        'mouseenter.contextMenu': handle.menuMouseenter,\r
-                        'mouseleave.contextMenu': handle.menuMouseleave\r
-                    }, '.context-menu-list')\r
-                    .on('mouseup.contextMenu', '.context-menu-input', handle.inputClick)\r
-                    .on({\r
-                        'mouseup.contextMenu': handle.itemClick,\r
-                        'contextmenu:focus.contextMenu': handle.focusItem,\r
-                        'contextmenu:blur.contextMenu': handle.blurItem,\r
-                        'contextmenu.contextMenu': handle.abortevent,\r
-                        'mouseenter.contextMenu': handle.itemMouseenter,\r
-                        'mouseleave.contextMenu': handle.itemMouseleave\r
-                    }, '.context-menu-item');\r
-\r
-                initialized = true;\r
-            }\r
-            \r
-            // engage native contextmenu event\r
-            $body\r
-                .on('contextmenu' + o.ns, o.selector, o, handle.contextmenu);\r
-            \r
-            switch (o.trigger) {\r
-                case 'hover':\r
-                        $body\r
-                            .on('mouseenter' + o.ns, o.selector, o, handle.mouseenter)\r
-                            .on('mouseleave' + o.ns, o.selector, o, handle.mouseleave);                    \r
-                    break;\r
-                    \r
-                case 'left':\r
-                        $body.on('click' + o.ns, o.selector, o, handle.click);\r
-                    break;\r
-                /*\r
-                default:\r
-                    // http://www.quirksmode.org/dom/events/contextmenu.html\r
-                    $body\r
-                        .on('mousedown' + o.ns, o.selector, o, handle.mousedown)\r
-                        .on('mouseup' + o.ns, o.selector, o, handle.mouseup);\r
-                    break;\r
-                */\r
-            }\r
-            \r
-            // create menu\r
-            if (!o.build) {\r
-                op.create(o);\r
-            }\r
-            break;\r
-        \r
-        case 'destroy':\r
-            if (!o.selector) {\r
-                $body.off('.contextMenu .contextMenuAutoHide');\r
-                $.each(namespaces, function(key, value) {\r
-                    $body.off(value);\r
-                });\r
-                \r
-                namespaces = {};\r
-                menus = {};\r
-                counter = 0;\r
-                initialized = false;\r
-                \r
-                $('.context-menu-list').remove();\r
-            } else if (namespaces[o.selector]) {\r
-                try {\r
-                    if (menus[namespaces[o.selector]].$menu) {\r
-                        menus[namespaces[o.selector]].$menu.remove();\r
-                    }\r
-                    \r
-                    delete menus[namespaces[o.selector]];\r
-                } catch(e) {\r
-                    menus[namespaces[o.selector]] = null;\r
-                }\r
-                \r
-                $body.off(namespaces[o.selector]);\r
-            }\r
-            break;\r
-        \r
-        case 'html5':\r
-            // if <command> or <menuitem> are not handled by the browser,\r
-            // or options was a bool true,\r
-            // initialize $.contextMenu for them\r
-            if ((!$.support.htmlCommand && !$.support.htmlMenuitem) || (typeof options == "boolean" && options)) {\r
-                $('menu[type="context"]').each(function() {\r
-                    if (this.id) {\r
-                        $.contextMenu({\r
-                            selector: '[contextmenu=' + this.id +']',\r
-                            items: $.contextMenu.fromMenu(this)\r
-                        });\r
-                    }\r
-                }).css('display', 'none');\r
-            }\r
-            break;\r
-        \r
-        default:\r
-            throw new Error('Unknown operation "' + operation + '"');\r
-    }\r
-    \r
-    return this;\r
-};\r
-\r
-// import values into <input> commands\r
-$.contextMenu.setInputValues = function(opt, data) {\r
-    if (data === undefined) {\r
-        data = {};\r
-    }\r
-    \r
-    $.each(opt.inputs, function(key, item) {\r
-        switch (item.type) {\r
-            case 'text':\r
-            case 'textarea':\r
-                item.value = data[key] || "";\r
-                break;\r
-\r
-            case 'checkbox':\r
-                item.selected = data[key] ? true : false;\r
-                break;\r
-                \r
-            case 'radio':\r
-                item.selected = (data[item.radio] || "") == item.value ? true : false;\r
-                break;\r
-            \r
-            case 'select':\r
-                item.selected = data[key] || "";\r
-                break;\r
-        }\r
-    });\r
-};\r
-\r
-// export values from <input> commands\r
-$.contextMenu.getInputValues = function(opt, data) {\r
-    if (data === undefined) {\r
-        data = {};\r
-    }\r
-    \r
-    $.each(opt.inputs, function(key, item) {\r
-        switch (item.type) {\r
-            case 'text':\r
-            case 'textarea':\r
-            case 'select':\r
-                data[key] = item.$input.val();\r
-                break;\r
-\r
-            case 'checkbox':\r
-                data[key] = item.$input.prop('checked');\r
-                break;\r
-                \r
-            case 'radio':\r
-                if (item.$input.prop('checked')) {\r
-                    data[item.radio] = item.value;\r
-                }\r
-                break;\r
-        }\r
-    });\r
-    \r
-    return data;\r
-};\r
-\r
-// find <label for="xyz">\r
-function inputLabel(node) {\r
-    return (node.id && $('label[for="'+ node.id +'"]').val()) || node.name;\r
-}\r
-\r
-// convert <menu> to items object\r
-function menuChildren(items, $children, counter) {\r
-    if (!counter) {\r
-        counter = 0;\r
-    }\r
-    \r
-    $children.each(function() {\r
-        var $node = $(this),\r
-            node = this,\r
-            nodeName = this.nodeName.toLowerCase(),\r
-            label,\r
-            item;\r
-        \r
-        // extract <label><input>\r
-        if (nodeName == 'label' && $node.find('input, textarea, select').length) {\r
-            label = $node.text();\r
-            $node = $node.children().first();\r
-            node = $node.get(0);\r
-            nodeName = node.nodeName.toLowerCase();\r
-        }\r
-        \r
-        /*\r
-         * <menu> accepts flow-content as children. that means <embed>, <canvas> and such are valid menu items.\r
-         * Not being the sadistic kind, $.contextMenu only accepts:\r
-         * <command>, <menuitem>, <hr>, <span>, <p> <input [text, radio, checkbox]>, <textarea>, <select> and of course <menu>.\r
-         * Everything else will be imported as an html node, which is not interfaced with contextMenu.\r
-         */\r
-        \r
-        // http://www.whatwg.org/specs/web-apps/current-work/multipage/commands.html#concept-command\r
-        switch (nodeName) {\r
-            // http://www.whatwg.org/specs/web-apps/current-work/multipage/interactive-elements.html#the-menu-element\r
-            case 'menu':\r
-                item = {name: $node.attr('label'), items: {}};\r
-                menuChildren(item.items, $node.children(), counter);\r
-                break;\r
-            \r
-            // http://www.whatwg.org/specs/web-apps/current-work/multipage/commands.html#using-the-a-element-to-define-a-command\r
-            case 'a':\r
-            // http://www.whatwg.org/specs/web-apps/current-work/multipage/commands.html#using-the-button-element-to-define-a-command\r
-            case 'button':\r
-                item = {\r
-                    name: $node.text(),\r
-                    disabled: !!$node.attr('disabled'),\r
-                    callback: (function(){ return function(){ $node.click(); }; })()\r
-                };\r
-                break;\r
-            \r
-            // http://www.whatwg.org/specs/web-apps/current-work/multipage/commands.html#using-the-command-element-to-define-a-command\r
-\r
-            case 'menuitem':\r
-            case 'command':\r
-                switch ($node.attr('type')) {\r
-                    case undefined:\r
-                    case 'command':\r
-                    case 'menuitem':\r
-                        item = {\r
-                            name: $node.attr('label'),\r
-                            disabled: !!$node.attr('disabled'),\r
-                            callback: (function(){ return function(){ $node.click(); }; })()\r
-                        };\r
-                        break;\r
-                        \r
-                    case 'checkbox':\r
-                        item = {\r
-                            type: 'checkbox',\r
-                            disabled: !!$node.attr('disabled'),\r
-                            name: $node.attr('label'),\r
-                            selected: !!$node.attr('checked')\r
-                        };\r
-                        break;\r
-                        \r
-                    case 'radio':\r
-                        item = {\r
-                            type: 'radio',\r
-                            disabled: !!$node.attr('disabled'),\r
-                            name: $node.attr('label'),\r
-                            radio: $node.attr('radiogroup'),\r
-                            value: $node.attr('id'),\r
-                            selected: !!$node.attr('checked')\r
-                        };\r
-                        break;\r
-                        \r
-                    default:\r
-                        item = undefined;\r
-                }\r
-                break;\r
\r
-            case 'hr':\r
-                item = '-------';\r
-                break;\r
-                \r
-            case 'input':\r
-                switch ($node.attr('type')) {\r
-                    case 'text':\r
-                        item = {\r
-                            type: 'text',\r
-                            name: label || inputLabel(node),\r
-                            disabled: !!$node.attr('disabled'),\r
-                            value: $node.val()\r
-                        };\r
-                        break;\r
-                        \r
-                    case 'checkbox':\r
-                        item = {\r
-                            type: 'checkbox',\r
-                            name: label || inputLabel(node),\r
-                            disabled: !!$node.attr('disabled'),\r
-                            selected: !!$node.attr('checked')\r
-                        };\r
-                        break;\r
-                        \r
-                    case 'radio':\r
-                        item = {\r
-                            type: 'radio',\r
-                            name: label || inputLabel(node),\r
-                            disabled: !!$node.attr('disabled'),\r
-                            radio: !!$node.attr('name'),\r
-                            value: $node.val(),\r
-                            selected: !!$node.attr('checked')\r
-                        };\r
-                        break;\r
-                    \r
-                    default:\r
-                        item = undefined;\r
-                        break;\r
-                }\r
-                break;\r
-                \r
-            case 'select':\r
-                item = {\r
-                    type: 'select',\r
-                    name: label || inputLabel(node),\r
-                    disabled: !!$node.attr('disabled'),\r
-                    selected: $node.val(),\r
-                    options: {}\r
-                };\r
-                $node.children().each(function(){\r
-                    item.options[this.value] = $(this).text();\r
-                });\r
-                break;\r
-                \r
-            case 'textarea':\r
-                item = {\r
-                    type: 'textarea',\r
-                    name: label || inputLabel(node),\r
-                    disabled: !!$node.attr('disabled'),\r
-                    value: $node.val()\r
-                };\r
-                break;\r
-            \r
-            case 'label':\r
-                break;\r
-            \r
-            default:\r
-                item = {type: 'html', html: $node.clone(true)};\r
-                break;\r
-        }\r
-        \r
-        if (item) {\r
-            counter++;\r
-            items['key' + counter] = item;\r
-        }\r
-    });\r
-}\r
-\r
-// convert html5 menu\r
-$.contextMenu.fromMenu = function(element) {\r
-    var $this = $(element),\r
-        items = {};\r
-        \r
-    menuChildren(items, $this.children());\r
-    \r
-    return items;\r
-};\r
-\r
-// make defaults accessible\r
-$.contextMenu.defaults = defaults;\r
-$.contextMenu.types = types;\r
-\r
-})(jQuery);\r
diff --git a/redakcja/static/contextmenu/jquery.ui.position.js b/redakcja/static/contextmenu/jquery.ui.position.js
deleted file mode 100755 (executable)
index cafdf02..0000000
+++ /dev/null
@@ -1,252 +0,0 @@
-/*!
- * jQuery UI Position 1.8.13
- *
- * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
- * Dual licensed under the MIT or GPL Version 2 licenses.
- * http://jquery.org/license
- *
- * http://docs.jquery.com/UI/Position
- */
-(function( $, undefined ) {
-
-$.ui = $.ui || {};
-
-var horizontalPositions = /left|center|right/,
-       verticalPositions = /top|center|bottom/,
-       center = "center",
-       _position = $.fn.position,
-       _offset = $.fn.offset;
-
-$.fn.position = function( options ) {
-       if ( !options || !options.of ) {
-               return _position.apply( this, arguments );
-       }
-
-       // make a copy, we don't want to modify arguments
-       options = $.extend( {}, options );
-
-       var target = $( options.of ),
-               targetElem = target[0],
-               collision = ( options.collision || "flip" ).split( " " ),
-               offset = options.offset ? options.offset.split( " " ) : [ 0, 0 ],
-               targetWidth,
-               targetHeight,
-               basePosition;
-
-       if ( targetElem.nodeType === 9 ) {
-               targetWidth = target.width();
-               targetHeight = target.height();
-               basePosition = { top: 0, left: 0 };
-       // TODO: use $.isWindow() in 1.9
-       } else if ( targetElem.setTimeout ) {
-               targetWidth = target.width();
-               targetHeight = target.height();
-               basePosition = { top: target.scrollTop(), left: target.scrollLeft() };
-       } else if ( targetElem.preventDefault ) {
-               // force left top to allow flipping
-               options.at = "left top";
-               targetWidth = targetHeight = 0;
-               basePosition = { top: options.of.pageY, left: options.of.pageX };
-       } else {
-               targetWidth = target.outerWidth();
-               targetHeight = target.outerHeight();
-               basePosition = target.offset();
-       }
-
-       // force my and at to have valid horizontal and veritcal positions
-       // if a value is missing or invalid, it will be converted to center 
-       $.each( [ "my", "at" ], function() {
-               var pos = ( options[this] || "" ).split( " " );
-               if ( pos.length === 1) {
-                       pos = horizontalPositions.test( pos[0] ) ?
-                               pos.concat( [center] ) :
-                               verticalPositions.test( pos[0] ) ?
-                                       [ center ].concat( pos ) :
-                                       [ center, center ];
-               }
-               pos[ 0 ] = horizontalPositions.test( pos[0] ) ? pos[ 0 ] : center;
-               pos[ 1 ] = verticalPositions.test( pos[1] ) ? pos[ 1 ] : center;
-               options[ this ] = pos;
-       });
-
-       // normalize collision option
-       if ( collision.length === 1 ) {
-               collision[ 1 ] = collision[ 0 ];
-       }
-
-       // normalize offset option
-       offset[ 0 ] = parseInt( offset[0], 10 ) || 0;
-       if ( offset.length === 1 ) {
-               offset[ 1 ] = offset[ 0 ];
-       }
-       offset[ 1 ] = parseInt( offset[1], 10 ) || 0;
-
-       if ( options.at[0] === "right" ) {
-               basePosition.left += targetWidth;
-       } else if ( options.at[0] === center ) {
-               basePosition.left += targetWidth / 2;
-       }
-
-       if ( options.at[1] === "bottom" ) {
-               basePosition.top += targetHeight;
-       } else if ( options.at[1] === center ) {
-               basePosition.top += targetHeight / 2;
-       }
-
-       basePosition.left += offset[ 0 ];
-       basePosition.top += offset[ 1 ];
-
-       return this.each(function() {
-               var elem = $( this ),
-                       elemWidth = elem.outerWidth(),
-                       elemHeight = elem.outerHeight(),
-                       marginLeft = parseInt( $.curCSS( this, "marginLeft", true ) ) || 0,
-                       marginTop = parseInt( $.curCSS( this, "marginTop", true ) ) || 0,
-                       collisionWidth = elemWidth + marginLeft +
-                               ( parseInt( $.curCSS( this, "marginRight", true ) ) || 0 ),
-                       collisionHeight = elemHeight + marginTop +
-                               ( parseInt( $.curCSS( this, "marginBottom", true ) ) || 0 ),
-                       position = $.extend( {}, basePosition ),
-                       collisionPosition;
-
-               if ( options.my[0] === "right" ) {
-                       position.left -= elemWidth;
-               } else if ( options.my[0] === center ) {
-                       position.left -= elemWidth / 2;
-               }
-
-               if ( options.my[1] === "bottom" ) {
-                       position.top -= elemHeight;
-               } else if ( options.my[1] === center ) {
-                       position.top -= elemHeight / 2;
-               }
-
-               // prevent fractions (see #5280)
-               position.left = Math.round( position.left );
-               position.top = Math.round( position.top );
-
-               collisionPosition = {
-                       left: position.left - marginLeft,
-                       top: position.top - marginTop
-               };
-
-               $.each( [ "left", "top" ], function( i, dir ) {
-                       if ( $.ui.position[ collision[i] ] ) {
-                               $.ui.position[ collision[i] ][ dir ]( position, {
-                                       targetWidth: targetWidth,
-                                       targetHeight: targetHeight,
-                                       elemWidth: elemWidth,
-                                       elemHeight: elemHeight,
-                                       collisionPosition: collisionPosition,
-                                       collisionWidth: collisionWidth,
-                                       collisionHeight: collisionHeight,
-                                       offset: offset,
-                                       my: options.my,
-                                       at: options.at
-                               });
-                       }
-               });
-
-               if ( $.fn.bgiframe ) {
-                       elem.bgiframe();
-               }
-               elem.offset( $.extend( position, { using: options.using } ) );
-       });
-};
-
-$.ui.position = {
-       fit: {
-               left: function( position, data ) {
-                       var win = $( window ),
-                               over = data.collisionPosition.left + data.collisionWidth - win.width() - win.scrollLeft();
-                       position.left = over > 0 ? position.left - over : Math.max( position.left - data.collisionPosition.left, position.left );
-               },
-               top: function( position, data ) {
-                       var win = $( window ),
-                               over = data.collisionPosition.top + data.collisionHeight - win.height() - win.scrollTop();
-                       position.top = over > 0 ? position.top - over : Math.max( position.top - data.collisionPosition.top, position.top );
-               }
-       },
-
-       flip: {
-               left: function( position, data ) {
-                       if ( data.at[0] === center ) {
-                               return;
-                       }
-                       var win = $( window ),
-                               over = data.collisionPosition.left + data.collisionWidth - win.width() - win.scrollLeft(),
-                               myOffset = data.my[ 0 ] === "left" ?
-                                       -data.elemWidth :
-                                       data.my[ 0 ] === "right" ?
-                                               data.elemWidth :
-                                               0,
-                               atOffset = data.at[ 0 ] === "left" ?
-                                       data.targetWidth :
-                                       -data.targetWidth,
-                               offset = -2 * data.offset[ 0 ];
-                       position.left += data.collisionPosition.left < 0 ?
-                               myOffset + atOffset + offset :
-                               over > 0 ?
-                                       myOffset + atOffset + offset :
-                                       0;
-               },
-               top: function( position, data ) {
-                       if ( data.at[1] === center ) {
-                               return;
-                       }
-                       var win = $( window ),
-                               over = data.collisionPosition.top + data.collisionHeight - win.height() - win.scrollTop(),
-                               myOffset = data.my[ 1 ] === "top" ?
-                                       -data.elemHeight :
-                                       data.my[ 1 ] === "bottom" ?
-                                               data.elemHeight :
-                                               0,
-                               atOffset = data.at[ 1 ] === "top" ?
-                                       data.targetHeight :
-                                       -data.targetHeight,
-                               offset = -2 * data.offset[ 1 ];
-                       position.top += data.collisionPosition.top < 0 ?
-                               myOffset + atOffset + offset :
-                               over > 0 ?
-                                       myOffset + atOffset + offset :
-                                       0;
-               }
-       }
-};
-
-// offset setter from jQuery 1.4
-if ( !$.offset.setOffset ) {
-       $.offset.setOffset = function( elem, options ) {
-               // set position first, in-case top/left are set even on static elem
-               if ( /static/.test( $.curCSS( elem, "position" ) ) ) {
-                       elem.style.position = "relative";
-               }
-               var curElem   = $( elem ),
-                       curOffset = curElem.offset(),
-                       curTop    = parseInt( $.curCSS( elem, "top",  true ), 10 ) || 0,
-                       curLeft   = parseInt( $.curCSS( elem, "left", true ), 10)  || 0,
-                       props     = {
-                               top:  (options.top  - curOffset.top)  + curTop,
-                               left: (options.left - curOffset.left) + curLeft
-                       };
-               
-               if ( 'using' in options ) {
-                       options.using.call( elem, props );
-               } else {
-                       curElem.css( props );
-               }
-       };
-
-       $.fn.offset = function( options ) {
-               var elem = this[ 0 ];
-               if ( !elem || !elem.ownerDocument ) { return null; }
-               if ( options ) { 
-                       return this.each(function() {
-                               $.offset.setOffset( this, options );
-                       });
-               }
-               return _offset.call( this );
-       };
-}
-
-}( jQuery ));
diff --git a/redakcja/static/contextmenu2/images/cut.png b/redakcja/static/contextmenu2/images/cut.png
deleted file mode 100644 (file)
index f215d6f..0000000
Binary files a/redakcja/static/contextmenu2/images/cut.png and /dev/null differ
diff --git a/redakcja/static/contextmenu2/images/door.png b/redakcja/static/contextmenu2/images/door.png
deleted file mode 100644 (file)
index 369fc46..0000000
Binary files a/redakcja/static/contextmenu2/images/door.png and /dev/null differ
diff --git a/redakcja/static/contextmenu2/images/page_white_copy.png b/redakcja/static/contextmenu2/images/page_white_copy.png
deleted file mode 100644 (file)
index a9f31a2..0000000
Binary files a/redakcja/static/contextmenu2/images/page_white_copy.png and /dev/null differ
diff --git a/redakcja/static/contextmenu2/images/page_white_delete.png b/redakcja/static/contextmenu2/images/page_white_delete.png
deleted file mode 100644 (file)
index af1ecaf..0000000
Binary files a/redakcja/static/contextmenu2/images/page_white_delete.png and /dev/null differ
diff --git a/redakcja/static/contextmenu2/images/page_white_edit.png b/redakcja/static/contextmenu2/images/page_white_edit.png
deleted file mode 100644 (file)
index b93e776..0000000
Binary files a/redakcja/static/contextmenu2/images/page_white_edit.png and /dev/null differ
diff --git a/redakcja/static/contextmenu2/images/page_white_paste.png b/redakcja/static/contextmenu2/images/page_white_paste.png
deleted file mode 100644 (file)
index 5b2cbb3..0000000
Binary files a/redakcja/static/contextmenu2/images/page_white_paste.png and /dev/null differ
diff --git a/redakcja/static/contextmenu2/index.html b/redakcja/static/contextmenu2/index.html
deleted file mode 100644 (file)
index 4efa62b..0000000
+++ /dev/null
@@ -1,200 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">\r
-\r
-<html xmlns="http://www.w3.org/1999/xhtml">\r
-\r
-       <head>\r
-               <title>jQuery Context Menu Plugin Demo</title>\r
-               <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />\r
-               \r
-               <style type="text/css">\r
-                       BODY,\r
-                       HTML {\r
-                               padding: 0px;\r
-                               margin: 0px;\r
-                       }\r
-                       BODY {\r
-                               font-family: Verdana, Arial, Helvetica, sans-serif;\r
-                               font-size: 11px;\r
-                               background: #FFF;\r
-                               padding: 15px;\r
-                       }\r
-                       \r
-                       H1 {\r
-                               font-family: Georgia, serif;\r
-                               font-size: 20px;\r
-                               font-weight: normal;\r
-                       }\r
-                       \r
-                       H2 {\r
-                               font-family: Georgia, serif;\r
-                               font-size: 16px;\r
-                               font-weight: normal;\r
-                               margin: 0px 0px 10px 0px;\r
-                       }\r
-                       \r
-                       #myDiv {\r
-                               width: 150px;\r
-                               border: solid 1px #2AA7DE;\r
-                               background: #6CC8EF;\r
-                               text-align: center;\r
-                               padding: 4em .5em;\r
-                               margin: 1em;\r
-                               float: left;\r
-                       }\r
-                       \r
-                       #myList {\r
-                               margin: 1em;\r
-                               float: left;\r
-                       }\r
-                       \r
-                       #myList UL {\r
-                               padding: 0px;\r
-                               margin: 0em 1em;\r
-                       }\r
-                       \r
-                       #myList LI {\r
-                               width: 100px;\r
-                               border: solid 1px #2AA7DE;\r
-                               background: #6CC8EF;\r
-                               padding: 5px 5px;\r
-                               margin: 2px 0px;\r
-                               list-style: none;\r
-                       }\r
-                       \r
-                       #options {\r
-                               clear: left;\r
-                       }\r
-                       \r
-                       #options INPUT {\r
-                               font-family: Verdana, Arial, Helvetica, sans-serif;\r
-                               font-size: 11px;\r
-                               width: 150px;\r
-                       }\r
-                       \r
-               </style>                \r
-               \r
-               <script src="../js/lib/jquery-1.4.2.min.js" type="text/javascript"></script>\r
-               <script src="jquery.contextMenu.js" type="text/javascript"></script>\r
-               <link href="jquery.contextMenu.css" rel="stylesheet" type="text/css" />\r
-               \r
-               <script type="text/javascript">\r
-                       \r
-                       $(document).ready( function() {\r
-                               \r
-                               // Show menu when #myDiv is clicked\r
-                               $("#myDiv").contextMenu({\r
-                                       menu: 'myMenu'\r
-                               },\r
-                                       function(action, el, pos) {\r
-                                       alert(\r
-                                               'Action: ' + action + '\n\n' +\r
-                                               'Element ID: ' + $(el).attr('id') + '\n\n' + \r
-                                               'X: ' + pos.x + '  Y: ' + pos.y + ' (relative to element)\n\n' + \r
-                                               'X: ' + pos.docX + '  Y: ' + pos.docY+ ' (relative to document)'\r
-                                               );\r
-                               });\r
-                               \r
-                               // Show menu when a list item is clicked\r
-                               $("#myList UL LI").contextMenu({\r
-                                       menu: 'myMenu'\r
-                               }, function(action, el, pos) {\r
-                                       alert(\r
-                                               'Action: ' + action + '\n\n' +\r
-                                               'Element text: ' + $(el).text() + '\n\n' + \r
-                                               'X: ' + pos.x + '  Y: ' + pos.y + ' (relative to element)\n\n' + \r
-                                               'X: ' + pos.docX + '  Y: ' + pos.docY+ ' (relative to document)'\r
-                                               );\r
-                               });\r
-                               \r
-                               // Disable menus\r
-                               $("#disableMenus").click( function() {\r
-                                       $('#myDiv, #myList UL LI').disableContextMenu();\r
-                                       $(this).attr('disabled', true);\r
-                                       $("#enableMenus").attr('disabled', false);\r
-                               });\r
-                               \r
-                               // Enable menus\r
-                               $("#enableMenus").click( function() {\r
-                                       $('#myDiv, #myList UL LI').enableContextMenu();\r
-                                       $(this).attr('disabled', true);\r
-                                       $("#disableMenus").attr('disabled', false);\r
-                               });\r
-                               \r
-                               // Disable cut/copy\r
-                               $("#disableItems").click( function() {\r
-                                       $('#myMenu').disableContextMenuItems('#cut,#copy');\r
-                                       $(this).attr('disabled', true);\r
-                                       $("#enableItems").attr('disabled', false);\r
-                               });\r
-                               \r
-                               // Enable cut/copy\r
-                               $("#enableItems").click( function() {\r
-                                       $('#myMenu').enableContextMenuItems('#cut,#copy');\r
-                                       $(this).attr('disabled', true);\r
-                                       $("#disableItems").attr('disabled', false);\r
-                               });                             \r
-                               \r
-                       });\r
-                       \r
-               </script>\r
-       </head>\r
-       \r
-       <body>\r
-               \r
-               <h1>jQuery Context Menu Plugin Demo</h1>\r
-               <p>\r
-                       This plugin lets you add context menu functionality to your web applications.\r
-               </p>\r
-               \r
-               <p>\r
-                       <strong>Tip:</strong> Try using your keyboard to make a selection.\r
-               </p>\r
-               \r
-               <p>\r
-                       <a href="http://abeautifulsite.net/2008/09/jquery-context-menu-plugin/">Back to the project page</a>\r
-               </p>\r
-               \r
-               <h2>Demo</h2>\r
-               \r
-               <div id="myDiv">\r
-                       Right click to view the context menu\r
-               </div>\r
-               \r
-               <div id="myList">\r
-                       <ul>\r
-                               <li>Item 1</li>\r
-                               <li>Item 2</li>\r
-                               <li>Item 3</li>\r
-                               <li>Item 4</li>\r
-                               <li>Item 5</li>\r
-                               <li>Item 6</li>\r
-                       </ul>\r
-               </div>\r
-               \r
-               <div id="options">\r
-                       <p>\r
-                               <input type="button" id="disableItems" value="Disable Cut/Copy" />\r
-                               <input type="button" id="enableItems" value="Enable Cut/Copy" disabled="disabled" />\r
-                       </p>\r
-                       \r
-                       <p>\r
-                               <input type="button" id="disableMenus" value="Disable Context Menus" />\r
-                               <input type="button" id="enableMenus" value="Enable Context Menus" disabled="disabled" />\r
-                       </p>\r
-               </div>\r
-               \r
-               <ul id="myMenu" class="contextMenu">\r
-                       <li class="edit"><a href="#edit">Edit</a></li>\r
-                       <li class="cut separator"><a href="#cut">Cut</a>\r
-                         <ul>\r
-                           <li class="pe"><a href="#pe">Pe</a></li>\r
-                         </ul>\r
-                       </li>\r
-                       <li class="copy"><a href="#copy">Copy</a></li>\r
-                       <li class="paste"><a href="#paste">Paste</a></li>\r
-                       <li class="delete"><a href="#delete">Delete</a></li>\r
-                       <li class="quit separator"><a href="#quit">Quit</a></li>\r
-               </ul>\r
-               \r
-       </body>\r
-</html>\r
diff --git a/redakcja/static/contextmenu2/jquery.contextMenu.css b/redakcja/static/contextmenu2/jquery.contextMenu.css
deleted file mode 100644 (file)
index 5b2dd90..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-/* Generic context menu styles */\r
-.contextMenu {\r
-       position: absolute;\r
-       width: 120px;\r
-       z-index: 99999;\r
-       border: solid 1px #CCC;\r
-       background: #EEE;\r
-       padding: 0px;\r
-       margin: 0px;\r
-       display: none;\r
-}\r
-\r
-.contextMenu LI {\r
-       list-style: none;\r
-       padding: 0px;\r
-       margin: 0px;\r
-}\r
-\r
-.contextMenu A {\r
-       color: #333;\r
-       text-decoration: none;\r
-       display: block;\r
-       line-height: 20px;\r
-       height: 20px;\r
-       background-position: 6px center;\r
-       background-repeat: no-repeat;\r
-       outline: none;\r
-       padding: 1px 5px;\r
-       padding-left: 28px;\r
-}\r
-\r
-.contextMenu LI.hover A {\r
-       color: #FFF;\r
-       background-color: #3399FF;\r
-}\r
-\r
-.contextMenu LI.disabled A {\r
-       color: #AAA;\r
-       cursor: default;\r
-}\r
-\r
-.contextMenu LI.hover.disabled A {\r
-       background-color: transparent;\r
-}\r
-\r
-.contextMenu LI.separator {\r
-       border-top: solid 1px #CCC;\r
-}\r
-\r
-/*\r
-       Adding Icons\r
-       \r
-       You can add icons to the context menu by adding\r
-       classes to the respective LI element(s)\r
-*/\r
-\r
-.contextMenu LI.edit A { background-image: url(images/page_white_edit.png); }\r
-.contextMenu LI.cut A { background-image: url(images/cut.png); }\r
-.contextMenu LI.copy A { background-image: url(images/page_white_copy.png); }\r
-.contextMenu LI.paste A { background-image: url(images/page_white_paste.png); }\r
-.contextMenu LI.delete A { background-image: url(images/page_white_delete.png); }\r
-.contextMenu LI.quit A { background-image: url(images/door.png); }\r
diff --git a/redakcja/static/contextmenu2/jquery.contextMenu.js b/redakcja/static/contextmenu2/jquery.contextMenu.js
deleted file mode 100644 (file)
index 59c1737..0000000
+++ /dev/null
@@ -1,211 +0,0 @@
-// jQuery Context Menu Plugin\r
-//\r
-// Version 1.01\r
-//\r
-// Cory S.N. LaViska\r
-// A Beautiful Site (http://abeautifulsite.net/)\r
-//\r
-// More info: http://abeautifulsite.net/2008/09/jquery-context-menu-plugin/\r
-//\r
-// Terms of Use\r
-//\r
-// This plugin is dual-licensed under the GNU General Public License\r
-//   and the MIT License and is copyright A Beautiful Site, LLC.\r
-//\r
-if(jQuery)( function() {\r
-       $.extend($.fn, {\r
-               \r
-               contextMenu: function(o, callback) {\r
-                       // Defaults\r
-                       if( o.menu == undefined ) return false;\r
-                       if( o.inSpeed == undefined ) o.inSpeed = 150;\r
-                       if( o.outSpeed == undefined ) o.outSpeed = 75;\r
-                       // 0 needs to be -1 for expected results (no fade)\r
-                       if( o.inSpeed == 0 ) o.inSpeed = -1;\r
-                       if( o.outSpeed == 0 ) o.outSpeed = -1;\r
-                       // Loop each context menu\r
-                       $(this).each( function() {\r
-                               var el = $(this);\r
-                               var offset = $(el).offset();\r
-                               // Add contextMenu class\r
-                               $('#' + o.menu).addClass('contextMenu');\r
-                               // Simulate a true right click\r
-                               $(this).mousedown( function(e) {\r
-                                       var evt = e;\r
-                                       evt.stopPropagation();\r
-                                       $(this).mouseup( function(e) {\r
-                                               e.stopPropagation();\r
-                                               var srcElement = $(this);\r
-                                               $(this).unbind('mouseup');\r
-                                               if( evt.button == 2 ) {\r
-                                                       // Hide context menus that may be showing\r
-                                                       $(".contextMenu").hide();\r
-                                                       // Get this context menu\r
-                                                       var menu = $('#' + o.menu);\r
-                                                       \r
-                                                       if( $(el).hasClass('disabled') ) return false;\r
-                                                       \r
-                                                       // Detect mouse position\r
-                                                       var d = {}, x, y;\r
-                                                       if( self.innerHeight ) {\r
-                                                               d.pageYOffset = self.pageYOffset;\r
-                                                               d.pageXOffset = self.pageXOffset;\r
-                                                               d.innerHeight = self.innerHeight;\r
-                                                               d.innerWidth = self.innerWidth;\r
-                                                       } else if( document.documentElement &&\r
-                                                               document.documentElement.clientHeight ) {\r
-                                                               d.pageYOffset = document.documentElement.scrollTop;\r
-                                                               d.pageXOffset = document.documentElement.scrollLeft;\r
-                                                               d.innerHeight = document.documentElement.clientHeight;\r
-                                                               d.innerWidth = document.documentElement.clientWidth;\r
-                                                       } else if( document.body ) {\r
-                                                               d.pageYOffset = document.body.scrollTop;\r
-                                                               d.pageXOffset = document.body.scrollLeft;\r
-                                                               d.innerHeight = document.body.clientHeight;\r
-                                                               d.innerWidth = document.body.clientWidth;\r
-                                                       }\r
-                                                       (e.pageX) ? x = e.pageX : x = e.clientX + d.scrollLeft;\r
-                                                       (e.pageY) ? y = e.pageY : y = e.clientY + d.scrollTop;\r
-                                                       \r
-                                                       // Show the menu\r
-                                                       $(document).unbind('click');\r
-                                                       $(menu).css({ top: y, left: x }).fadeIn(o.inSpeed);\r
-                                                       // Hover events\r
-                                                       $(menu).find('A').mouseover( function() {\r
-                                                               $(menu).find('LI.hover').removeClass('hover');\r
-                                                               $(this).parent().addClass('hover');\r
-                                                       }).mouseout( function() {\r
-                                                               $(menu).find('LI.hover').removeClass('hover');\r
-                                                       });\r
-                                                       \r
-                                                       // Keyboard\r
-                                                       $(document).keypress( function(e) {\r
-                                                               switch( e.keyCode ) {\r
-                                                                       case 38: // up\r
-                                                                               if( $(menu).find('LI.hover').size() == 0 ) {\r
-                                                                                       $(menu).find('LI:last').addClass('hover');\r
-                                                                               } else {\r
-                                                                                       $(menu).find('LI.hover').removeClass('hover').prevAll('LI:not(.disabled)').eq(0).addClass('hover');\r
-                                                                                       if( $(menu).find('LI.hover').size() == 0 ) $(menu).find('LI:last').addClass('hover');\r
-                                                                               }\r
-                                                                       break;\r
-                                                                       case 40: // down\r
-                                                                               if( $(menu).find('LI.hover').size() == 0 ) {\r
-                                                                                       $(menu).find('LI:first').addClass('hover');\r
-                                                                               } else {\r
-                                                                                       $(menu).find('LI.hover').removeClass('hover').nextAll('LI:not(.disabled)').eq(0).addClass('hover');\r
-                                                                                       if( $(menu).find('LI.hover').size() == 0 ) $(menu).find('LI:first').addClass('hover');\r
-                                                                               }\r
-                                                                       break;\r
-                                                                       case 13: // enter\r
-                                                                               $(menu).find('LI.hover A').trigger('click');\r
-                                                                       break;\r
-                                                                       case 27: // esc\r
-                                                                               $(document).trigger('click');\r
-                                                                       break\r
-                                                               }\r
-                                                       });\r
-                                                       \r
-                                                       // When items are selected\r
-                                                       $('#' + o.menu).find('A').unbind('click');\r
-                                                       $('#' + o.menu).find('LI:not(.disabled) A').click( function() {\r
-                                                               $(document).unbind('click').unbind('keypress');\r
-                                                               $(".contextMenu").hide();\r
-                                                               // Callback\r
-                                                               if( callback ) callback( $(this).attr('href').substr(1), $(srcElement), {x: x - offset.left, y: y - offset.top, docX: x, docY: y} );\r
-                                                               return false;\r
-                                                       });\r
-                                                       \r
-                                                       // Hide bindings\r
-                                                       setTimeout( function() { // Delay for Mozilla\r
-                                                               $(document).click( function() {\r
-                                                                       $(document).unbind('click').unbind('keypress');\r
-                                                                       $(menu).fadeOut(o.outSpeed);\r
-                                                                       return false;\r
-                                                               });\r
-                                                       }, 0);\r
-                                               }\r
-                                       });\r
-                               });\r
-                               \r
-                               // Disable text selection\r
-                               if( $.browser.mozilla ) {\r
-                                       $('#' + o.menu).each( function() { $(this).css({ 'MozUserSelect' : 'none' }); });\r
-                               } else if( $.browser.msie ) {\r
-                                       $('#' + o.menu).each( function() { $(this).bind('selectstart.disableTextSelect', function() { return false; }); });\r
-                               } else {\r
-                                       $('#' + o.menu).each(function() { $(this).bind('mousedown.disableTextSelect', function() { return false; }); });\r
-                               }\r
-                               // Disable browser context menu (requires both selectors to work in IE/Safari + FF/Chrome)\r
-                               $(el).add($('UL.contextMenu')).bind('contextmenu', function() { return false; });\r
-                               \r
-                       });\r
-                       return $(this);\r
-               },\r
-               \r
-               // Disable context menu items on the fly\r
-               disableContextMenuItems: function(o) {\r
-                       if( o == undefined ) {\r
-                               // Disable all\r
-                               $(this).find('LI').addClass('disabled');\r
-                               return( $(this) );\r
-                       }\r
-                       $(this).each( function() {\r
-                               if( o != undefined ) {\r
-                                       var d = o.split(',');\r
-                                       for( var i = 0; i < d.length; i++ ) {\r
-                                               $(this).find('A[href="' + d[i] + '"]').parent().addClass('disabled');\r
-                                               \r
-                                       }\r
-                               }\r
-                       });\r
-                       return( $(this) );\r
-               },\r
-               \r
-               // Enable context menu items on the fly\r
-               enableContextMenuItems: function(o) {\r
-                       if( o == undefined ) {\r
-                               // Enable all\r
-                               $(this).find('LI.disabled').removeClass('disabled');\r
-                               return( $(this) );\r
-                       }\r
-                       $(this).each( function() {\r
-                               if( o != undefined ) {\r
-                                       var d = o.split(',');\r
-                                       for( var i = 0; i < d.length; i++ ) {\r
-                                               $(this).find('A[href="' + d[i] + '"]').parent().removeClass('disabled');\r
-                                               \r
-                                       }\r
-                               }\r
-                       });\r
-                       return( $(this) );\r
-               },\r
-               \r
-               // Disable context menu(s)\r
-               disableContextMenu: function() {\r
-                       $(this).each( function() {\r
-                               $(this).addClass('disabled');\r
-                       });\r
-                       return( $(this) );\r
-               },\r
-               \r
-               // Enable context menu(s)\r
-               enableContextMenu: function() {\r
-                       $(this).each( function() {\r
-                               $(this).removeClass('disabled');\r
-                       });\r
-                       return( $(this) );\r
-               },\r
-               \r
-               // Destroy context menu(s)\r
-               destroyContextMenu: function() {\r
-                       // Destroy specified context menus\r
-                       $(this).each( function() {\r
-                               // Disable action\r
-                               $(this).unbind('mousedown').unbind('mouseup');\r
-                       });\r
-                       return( $(this) );\r
-               }\r
-               \r
-       });\r
-})(jQuery);
\ No newline at end of file
diff --git a/redakcja/static/css/book.css b/redakcja/static/css/book.css
deleted file mode 100644 (file)
index f685e41..0000000
+++ /dev/null
@@ -1,432 +0,0 @@
-body {
-    font-size: 16px;
-    font-family: Georgia, "Times New Roman", serif;
-    line-height: 1.5em;
-    margin: 0;
-}
-
-a {
-    color: blue;
-    text-decoration: none;
-}
-
-#book-text {
-    margin: 3em;
-    max-width: 48em;
-}
-
-table {
-    border-collapse: collapse;
-    width: 100%;
-}
-
-table.border, table.border td, table.border th {
-    border: 1px solid black;
-}
-
-td, th {
-    vertical-align: top;
-}
-
-/* ================================== */
-/* = Header with logo and menu      = */
-/* ================================== */
-#header {
-    margin: 3.4em 0 0 1.4em;
-}
-
-img {
-    border: none;
-    max-width: 48em;
-    margin-top: 1em;
-}
-
-#logo {
-    font-size: 1.5em;
-}
-#logo a {
-    color: black;
-}
-
-#menu {
-    position: fixed;
-    left: 0;
-    top: 0;
-    width: 100%;
-    height: 1.5em;
-    background: #333;
-    color: #FFF;
-    opacity: 0.9;
-    z-index: 99;
-}
-
-#menu ul {
-    list-style: none;
-    padding: 0;
-    margin: 0;
-}
-
-#menu li a {
-    display: block;
-    float: left;
-    height: 1.5em;
-    margin-left: 0.5em;
-    text-align: center;
-    color: #FFF;
-    padding: 0 1em;
-}
-#menu li a.menu {
-    padding-right: 1.5em;
-}
-
-#menu li a.menu:hover, #menu li a.menu:active {
-    color: #000;
-    background: #FFF;
-}
-
-#menu li a.menu.selected {
-    color: #000;
-    background: #FFF;
-}
-#menu a.menu-link {
-    display: block;
-    float: left;
-    height: 1.5em;
-    margin-left: 0.5em;
-    text-align: center;
-    color: #FFF;
-}
-#menu span {
-    color: #888;
-    font-style: italic;
-    font-size: .75em;
-    margin-right: 0.5em;
-}
-
-
-#toc, #themes, #nota_red, #info {
-    position: fixed;
-    left: 0;
-    top: 1.5em;
-    width: 37em;
-    padding: 1.5em;
-    background: #FFF;
-    border-bottom: 0.25em solid #DDD;
-    border-right: 0.25em solid #DDD;
-    display: none;
-    height: 16em;
-    overflow-x: hidden;
-    overflow-y: auto;
-    opacity: 0.9;
-    z-index: 99;
-}
-#download {
-    position: fixed;
-    left: 0;
-    top: 1.5em;
-    width: 37em;
-    padding: 1.5em;
-    background: #FFF;
-    border-bottom: 0.25em solid #DDD;
-    border-right: 0.25em solid #DDD;
-    display: none;
-    height: 10em;
-    overflow-x: hidden;
-    overflow-y: auto;
-    opacity: 0.9;
-    z-index: 99;
-}
-
-#toc ol, #themes ol {
-    list-style: none;
-    padding: 0;
-    margin: 0;
-}
-
-#toc ol li {
-    font-weight: bold;
-}
-
-#toc ol ol {
-    padding: 0 0 1.5em 1.5em;
-    margin: 0;
-}
-
-#toc ol ol li {
-    font-weight: normal;
-}
-
-#toc h2 {
-    display: none;
-}
-
-#toc .anchor {
-    float: none;
-    margin: 0;
-    color: blue;
-    font-size: 16px;
-    position: inherit;
-}
-
-#info p {
-    text-align: justify;
-    margin: 1.5em 0 0;
-}
-
-/* =================================================== */
-/* = Common elements: headings, paragraphs and lines = */
-/* =================================================== */
-h1 {
-    font-size: 3em;
-    margin: 1.5em 0;
-    text-align: center;
-    line-height: 1.5em;
-    font-weight: bold;
-}
-
-h2 {
-    font-size: 2em;
-    margin: 1.5em 0 0;
-    font-weight: bold;
-    line-height: 1.5em;
-}
-
-h3 {
-    font-size: 1.5em;
-    margin: 1.5em 0 0;
-    font-weight: normal;
-    line-height: 1.5em;
-}
-
-h4 {
-    font-size: 1em;
-    margin: 1.5em 0 0;
-    line-height: 1.5em;
-}
-
-p {
-    margin: 0;
-}
-
-/* ======================== */
-/* = Footnotes and themes = */
-/* ======================== */
-.theme-begin {
-    border-left: 0.1em solid #DDDDDD;
-    color: #777;
-    padding: 0 0.5em;
-    width: 7.5em;
-
-    font-style: normal;
-    font-weight: normal;
-    font-variant: normal;
-    letter-spacing: 0;
-    text-transform: none;
-    text-decoration: none;
-
-    font-size: 16px;
-    float: right;
-    margin-right: -9.5em;
-    margin-bottom: 0.5em;
-    clear: both;
-    left: 40em;
-    line-height: 1.5em;
-    text-align: left;
-}
-
-.annotation {
-    font-style: normal;
-    font-weight: normal;
-    font-size: 12px;
-    padding-left: 2px;
-    position: relative;
-    top: -4px;
-}
-
-#footnotes {
-    margin-top: 3em;
-}
-
-#footnotes .annotation {
-    display: block;
-    float: left;
-    width: 2.5em;
-    clear: both;
-}
-
-#footnotes div {
-    margin: 1.5em 0 0 0;
-}
-
-#footnotes p, #footnotes ul {
-    margin-left: 2.5em;
-    font-size: 0.875em;
-}
-
-#footnotes .permalink {
-    font-size: .75em;
-}
-
-blockquote {
-    font-size: 0.875em;
-}
-
-/* ============= */
-/* = Numbering = */
-/* ============= */
-.verse, .paragraph {
-    position:relative;
-}
-.anchor {
-    position: absolute;
-    margin: -0.25em -0.5em;
-    left: -3em;
-    color: #777;
-    font-size: 12px;
-    width: 2em;
-    text-align: center;
-    padding: 0.25em 0.5em;
-    line-height: 1.5em;
-}
-
-.anchor:hover, #book-text .anchor:active {
-    color: #FFF;
-    background-color: #CCC;
-}
-
-/* =================== */
-/* = Custom elements = */
-/* =================== */
-span.author {
-    font-size: 0.5em;
-    display: block;
-    line-height: 1.5em;
-    margin-bottom: 0.25em;
-}
-
-span.collection {
-    font-size: 0.375em;
-    display: block;
-    line-height: 1.5em;
-    margin-bottom: -0.25em;
-}
-
-span.subtitle {
-    font-size: 0.5em;
-    display: block;
-    line-height: 1.5em;
-    margin-top: -0.25em;
-}
-
-span.translator {
-    font-size: 0.375em;
-    display: block;
-    line-height: 1.5em;
-    margin-top: 0.25em;
-}
-
-div.didaskalia {
-    font-style: italic;
-    margin: 0.5em 0 0 1.5em;
-}
-
-div.kwestia {
-    margin: 0.5em 0 0;
-}
-
-div.stanza {
-    margin: 1.5em 0 0;
-}
-
-div.kwestia div.stanza {
-    margin: 0;
-}
-
-p.paragraph {
-    text-align: justify;
-    margin: 1.5em 0 0;
-}
-
-p.motto {
-    text-align: justify;
-    font-style: italic;
-    margin: 1.5em 0 0;
-}
-
-p.motto_podpis {
-    font-size: 0.875em;
-    text-align: right;
-}
-
-div.fragment {
-    border-bottom: 0.1em solid #999;
-    padding-bottom: 1.5em;
-}
-
-div.note p, div.dedication p, div.note p.paragraph, div.dedication p.paragraph {
-    text-align: right;
-    font-style: italic;
-}
-
-div.frame {
-    border: 1px darkgray solid;
-    padding-bottom: 1.5em;
-}
-
-hr.spacer {
-    height: 3em;
-    visibility: hidden;
-}
-
-hr.spacer-line {
-    margin: 1.5em 0;
-    border: none;
-    border-bottom: 0.1em solid #000;
-}
-
-p.spacer-asterisk {
-    padding: 0;
-    margin: 1.5em 0;
-    text-align: center;
-}
-
-div.person-list ol {
-    list-style: none;
-    padding: 0 0 0 1.5em;
-}
-
-p.place-and-time {
-    font-style: italic;
-}
-
-em.math, em.foreign-word, em.book-title, em.didaskalia {
-    font-style: italic;
-}
-
-em.author-emphasis {
-    letter-spacing: 0.1em;
-}
-
-em.person {
-    font-style: normal;
-    font-variant: small-caps;
-}
-
-em.wieksze-odstepy
-{
-    font-style: normal;
-    word-spacing: 1em;
-}
-
-.verse:after {
-    content: "\feff";
-}
-
-
-/* =================================== */
-/* = Hide some elements for printing = */
-/* =================================== */
-
-@media print {
-    #menu {display: none;}
-}
diff --git a/redakcja/static/css/book_list.css b/redakcja/static/css/book_list.css
deleted file mode 100644 (file)
index 86a0de5..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-/* icons used in the contex menu of book list */
-
-.context-menu-item.icon-user { background-image: url(../icons/user.png); }
-.context-menu-item.icon-clock { background-image: url(../icons/clock.png); }
-.context-menu-item.icon-ok { background-image: url(../icons/ok.png); }
-.context-menu-item.icon-stop { background-image: url(../icons/stop.png); }
diff --git a/redakcja/static/css/dialogs.css b/redakcja/static/css/dialogs.css
deleted file mode 100644 (file)
index 1032b13..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-.dialog {
-       display: none;
-       padding: 5px;
-       text-align: left;
-       cursor: auto;
-       background-color: #E1E1E1; /* #e0ffb8; */
-}
-
-.dialog .help_text {
-       font-size: 11px;
-       color: #2e3536;
-}
-
-.dialog .action_area {
-       padding: 1em 0.5em 0.5em;
-       border-top: 1px solid black;
-       margin-top: 0.5em;
-}
-
-.dialog p {
-       margin: 0.5em;
-}
-
-*[data-ui-error-for] {
-       color: red;
-       font-weight: bold;
-}
-
-#save_dialog textarea, #revert_dialog textarea {
-       width: 90%;
-       margin: 0.2em 4%;
-}
\ No newline at end of file
diff --git a/redakcja/static/css/filelist.css b/redakcja/static/css/filelist.css
deleted file mode 100644 (file)
index b5e4fc4..0000000
+++ /dev/null
@@ -1,238 +0,0 @@
-/*
-    Document   : filelist
-    Created on : 2009-09-04, 20:44:44
-    Author     : lreqc
-    Description:
-        Dodatkowe style dla listy plików na stronie głównej.
-*/
-
-body {
-    margin: 0;
-    font-family: verdana, sans-serif;
-    font-size: 10px;
-}
-
-img {
-    border: 0;
-}
-
-th {
-    text-align: left;
-}
-
-td {
-    vertical-align: top;
-    padding: 0 3px;
-}
-.clr {
-    clear: both;
-}
-
-#tabs-nav {
-    padding: 5px 5px 0 10px;
-    background: #ffdfbf;
-    border-bottom: 1px solid #ff8000;
-    position: relative;
-}
-
-#tabs-nav-left {
-    margin-left: 60px;
-}
-
-#tabs-nav-left a {
-    display: block;
-    float: left;
-    padding: 5px 20px 5px 20px;
-    margin-bottom: -1px;
-    border-width: 1px;
-    border-style: solid;
-    border-color: transparent;
-}
-
-#tabs-nav-left .active {
-    background: white;
-    border-color: #ff8000 #ff8000 white #ff8000;
-}
-
-.section {
-    border-top: 1px solid #ffdfbf;
-    margin-top: 2em;
-    padding-top: 1em;
-}
-
-.editable td {
-    padding: 1px;
-}
-.editable input, .editable select, .editable textarea {
-    width: 400px;
-}
-.editable .number-input {
-    width: 100px;
-}
-
-
-#login-box {
-    float: right;
-}
-
-#logo {
-    position: absolute;
-    bottom: 0;
-}
-
-#content {
-    padding: 10px;
-}
-
-
-
-#catalogue_layout_left_column {
-       overflow: visible;
-       float: left;
-       max-width: 60%;
-       padding-right: 2%;
-       border-right: 1px dashed black;
-
-}
-
-#catalogue_layout_right_column {
-       float: left;
-       max-width: 30%;
-       margin-left: 5%;
-}
-
-#last-edited-list ul {
-       margin: 0px;
-}
-
-#last-edited-list li {
-       margin-bottom: 1em;
-}
-
-#last-edited-list .date {
-       font-size: 70%;
-       color: #808080;
-}
-
-a, a:visited, a:active {
-       color: #a05000;
-       text-decoration: none;
-}
-
-a:hover {
-       text-decoration: underline;
-}
-
-
-
-.error {
-    color: red;
-    font-weight: bold;
-}
-
-.success {
-    color: green;
-}
-
-#error-list {
-    color: red;
-}
-
-#skipped-list {
-    color: #666;
-}
-
-.chunkno {
-    font-size: .7em;
-    padding-left: 2em;
-}
-
-
-/* Big cheesy publish button */
-#publish-button {
-        color: black;
-        border: 2px solid black; 
-        border-radius: 20px;
-        box-shadow: 0px 0px 15px #88f;
-        /*moz-border-radius: 20px;
-        -moz-box-shadow: 10px 10px 5px #888;*/
-        font-size:1.5em; 
-        padding: 1em; 
-        background: -moz-linear-gradient(top,  #fff,  #44f);
-        -moz-transition: all 0.5s ease-in-out;        
-        margin: 20px;
-}
-
-#publish-button:hover {
-    -moz-transition: all 0.5s ease-in-out;        
-    -moz-transform: scale(1.1);
-    background: -moz-linear-gradient(top,  #fff,  #88f);
-    -moz-box-shadow: 0px 0px 30px #ff8;
-}
-
-#publish-button:active {
-    background: -moz-linear-gradient(top,  #88f,  #fff);
-}
-
-
-/* book list */
-
-.book-search-column input {
-    width: 96%;
-}
-
-.book-list-user .user-column {
-    display: none;
-}
-
-
-/* wall */
-.wall {
-    padding-left: 0;
-    list-style: none;
-}
-
-.wall li {
-    clear: left;
-    border-top: 1px dotted gray;
-    padding: 0 1em 2em 1em;
-    margin-bottom: 0;
-}
-
-.wall .gravatar {
-    float: left;
-    margin-right: 1em;
-    margin-left: -1em;
-}
-.wall h3 {
-    font-size: 1.25em;
-    margin-top: 0;
-}
-
-.wall .time {
-    /* float:right; */
-}
-
-.wall .anonymous {
-    background-color: #efa;
-}
-
-.wall .comment {
-    background-color: #dfc;
-}
-
-.wall .publish {
-    background-color: #fdc;
-}
-
-div.cover-preview {
-       width: 216px;
-       min-height: 300px;
-       float: left;
-       margin-right: 2em;
-}
-
-img.cover-preview {
-       width: 216px;
-       height: 300px;
-}
diff --git a/redakcja/static/css/gallery.css b/redakcja/static/css/gallery.css
deleted file mode 100644 (file)
index 08e9318..0000000
+++ /dev/null
@@ -1,135 +0,0 @@
-#sidebar {
-    position: absolute;
-    top: 0px;
-    right: 0px;
-    bottom: 0px;
-    width: 0px;
-    background-color: #FFF;
-}
-
-#side-search {
-    height: 100%;
-    display: none;
-    background-color: #C1C1C1;
-}
-
-#side-search p {
-    padding: .5em;
-}
-
-#side-annotations {
-    display: none;
-}
-
-.annotations-list {
-    position: absolute;
-    left: 0;
-    top: 28px;
-    right: 0;
-    bottom: 0;
-    overflow: auto;
-    padding: 1em 1em 0 1em;
-}
-
-#side-annotations .active {
-    font-weight: bold;
-}
-
-#side-annotations .spinner {
-    position: absolute;
-    left: 0;
-    top: 28px;
-    right: 0;
-    bottom: 0;
-    text-align:center;
-    padding-top: 50%;
-    background: white;
-}
-
-#side-annotations .spinner img {
-    margin-top: -15px;
-}
-
-#side-annotations .akap {
-    font-size: 16px;
-    font-family: "Georgia", "Times New Roman", serif;
-    line-height: 1.5em;
-    margin-bottom: 1em;
-    position: relative;
-}
-
-#side-annotations .src {
-    font-family: monospace;
-    font-size: 10px;
-    font-weight: 400;
-    line-height: 13px;
-    position: absolute;
-    top: 0;
-    bottom: -.5em;
-    display:none;
-    background: white;
-    border: 1px solid #888;
-    width: 100%;
-}
-
-#side-annotations .akap:hover .src {
-    display: block;
-}
-
-/* =========== */
-/* = Gallery = */
-/* =========== */
-
-#side-gallery .error_message
-{
-       background-color: white;
-       color: black;
-       padding: 0.5em 1em;
-}
-
-.change-gallery-panel {
-    width: 100%;
-    border-bottom: 1px solid #777;
-    background-color: #C1C1C1;
-    margin: 0;
-    padding: 2px;
-    z-index: 10;
-}
-
-.gallery-image {
-    position: absolute;
-    top: 30px;
-    right: 0;
-    bottom: 0;
-    left: 0;
-    margin: 0;
-    border: none;
-    background-color: transparent;
-    overflow: hidden;
-}
-
-.gallery-image img {
-    position: absolute;
-    -moz-drag: none;
-    user-select: none;
-    -webkit-user-select: none;
-    -khtml-user-select: none;
-    -moz-user-select: none;
-    cursor: pointer;
-
-       background-color: white;
-       min-height: 100px;
-       min-width: 100px;
-}
-
-.gallery-image img[src=''] {
-       background-color: red;
-}
-
-#motifs-editor .toolbar {
-    height: auto;
-}
-
-#objects-editor .toolbar {
-    height: auto;
-}
diff --git a/redakcja/static/css/history.css b/redakcja/static/css/history.css
deleted file mode 100644 (file)
index cbc5c52..0000000
+++ /dev/null
@@ -1,159 +0,0 @@
-#history-view {
-    overflow: auto;
-    position: absolute;
-    top: 30px; /* 0 for no toolbar, 30 for toolbar */
-    bottom: 0px;
-    left: 0px;
-    right: 0px;
-    z-index: 1;
-}
-
-/*
- * File History
- */
-#changes-list-container {
-    margin: 1em auto;
-    width: 70%;
-}
-
-table#changes-list-container {
-    border-spacing: 0px 15px;
-}
-
-#changes-list {
-    margin: 0.5em 0.2em;
-}
-
-#changes-list td {
-    padding: 0.5em 1em;
-}
-
-#changes-list .entry {
-    position: relative;
-    padding: 0.5em;
-    padding-left: 3em;
-    margin: 0.5em;
-}
-
-#changes-list .entry:hover {
-    background-color: #f0f0f0;
-}
-
-#changes-list .entry.selected {
-    background-color: #ffec63;
-}
-
-#changes-list .tag {
-    display: inline-block;
-    visibility: hidden;
-    width: 60px;
-    margin: 0 0.5em 0 0;
-    font-size: 11px;
-    padding: 3px 2px;
-    text-align: center;
-    color: black;
-    background: #add8e6;
-    cursor: pointer;
-    vertical-align: middle;
-    -moz-border-radius: 10px;
-    -webkit-border-radius: 10px;
-    border-radius: 10px;
-}
-
-#changes-list .entry:hover .tag {
-    visibility: visible;
-}
-
-#changes-list .tag:hover {
-    background: #bde8f6;
-}
-
-#changes-list *[data-version-tag] {
-    visibility: visible;
-    border: 1px solid black;
-    color: black;
-}
-
-#changes-list *[data-stub-value =
-'version'] {
-    font-weight: bold;
-}
-
-#changes-list td {
-    vertical-align: text-top;
-}
-
-#changes-list *[data-stub-value = 'description'] {
-    font-size: .8em;
-    color: gray;
-    white-space: pre-line;
-}
-
-#changes-list *[data-stub-value = 'published'] {
-    font-size: .7em;
-    color: gray;
-}
-
-/*
- * Graphical DIFF view
- *
- */
-#history-view .diff_table {
-    width: 90%;
-}
-
-.editor.DiffPerspective {
-    overflow-y: scroll;
-}
-
-.diff_table {
-    border-width: 1px;
-    border-style: solid;
-    border-color: black;
-    empty-cells: show;
-    border-spacing: 0px;
-}
-
-.diff_table td {
-    border-width: 0px 1px 1px 0px;
-    border-style: dotted;
-    border-color: grey;
-    font-size: 10px;
-    line-height: 20px;
-    font-family: monospace;
-    padding: 0px;
-    white-space: pre-line;
-    /*word-wrap:break-word;
-     word-break:break-all; */
-}
-
-.diff_table th {
-    border-width: 0px 1px 1px 0px;
-    border-style: solid;
-    border-color: black;
-    background: #e5ffe5;
-}
-
-/* .diff_table td.left, .diff_table td.right {
- width: 50%;
- }*/
-.diff_table tr.change {
-    background-color: #dcdcdc;
-}
-
-.diff_mark {
-    display: inline-block;
-    padding: 2px;
-}
-
-.diff_mark_removed {
-    background-color: #ff9c94;
-}
-
-.diff_mark_added {
-    background-color: #90ee90;
-}
-
-.diff_mark_changed {
-    background-color: yellow;
-}
diff --git a/redakcja/static/css/html.css b/redakcja/static/css/html.css
deleted file mode 100644 (file)
index 3b99681..0000000
+++ /dev/null
@@ -1,684 +0,0 @@
-/* Style widoku HTML. Nie należy tu ustawiać position ani marginesów */
-.htmlview {
-    counter-reset: main;
-    font-size: 16px;
-    font-family: "Georgia", "Times New Roman", serif;
-    line-height: 1.5em;
-    padding: 3em 3em 3em 45px;
-    overflow-y: scroll;
-    overflow-x: auto;
-}
-
-.htmlview[data-tag-names-visible] {
-    padding-left: 90px;
-}
-
-/*
- .htmlview[data-tag-names-visible] *[x-editable]:not(*[x-common])::before {
- display: block;
- float: left;
- clear: left;
- content: attr(x-node);
- font-weight: bold;
- font-style: normal;
- font-variant:normal;
- font-size: 8px;
- line-height: 8px;
- margin-bottom: 4px;
-
- padding: 3px 4px;
- vertical-align: super;
- background-color:#add8e6;
- margin-left: -80px;
- width: 70px;
- text-align: center;
-
- -webkit-border-radius: 4px;
- }
- */
-.htmlview *[x-node = 'RDF'][x-ns = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'] {
-    display: none;
-}
-
-.htmlview * {
-    position: relative;
-}
-
-.htmlview div {
-    max-width: 36em;
-}
-
-.htmlview #toc {
-    display: none;
-}
-
-.htmlview a {
-    color: blue;
-    text-decoration: none;
-}
-
-.htmlview a:hover {
-    text-decoration: none;
-}
-
-.htmlview h1 {
-    font-size: 3em;
-    margin: 1.5em 0;
-    text-align: center;
-    line-height: 1.5em;
-    font-weight: bold;
-}
-
-.htmlview h2 {
-    font-size: 2em;
-    margin: 1.5em 0 0;
-    font-weight: bold;
-    line-height: 1.5em;
-}
-
-.htmlview h3 {
-    font-size: 1.5em;
-    margin: 1.5em 0 0;
-    font-weight: normal;
-    line-height: 1.5em;
-}
-
-.htmlview h4 {
-    font-size: 1em;
-    margin: 1.5em 0 0;
-    line-height: 1.5em;
-}
-
-.htmlview p {
-    margin: 0;
-}
-
-.htmlview #footnotes div {
-    margin: 1.5em 0 0 0;
-}
-
-.htmlview #footnotes p {
-    margin-left: 2.5em;
-    font-size: 0.875em;
-}
-
-.htmlview blockquote {
-    font-size: 0.875em;
-}
-
-.htmlview pre {
-       overflow-x: auto; /* Use horizontal scroller if needed; for Firefox 2, not needed in Firefox 3 */
-       white-space: pre-wrap; /* css-3 */
-       white-space: -moz-pre-wrap !important; /* Mozilla, since 1999 */
-       white-space: -pre-wrap; /* Opera 4-6 */
-       white-space: -o-pre-wrap; /* Opera 7 */ /*
-       width: 99%; */
-       word-wrap: break-word; /* Internet Explorer 5.5+ */
-}
-
-/* ============= */
-/* = Numbering = */
-/* ============= */
-.htmlview .anchor {
-    position: relative;
-    margin: 0;
-    left: -2.2em;
-    color: #777;
-    font-size: 12px;
-    width: 2em;
-    text-align: center;
-    padding: 0.25em 0.7em;
-    line-height: 1.5em;
-}
-
-.htmlview .anchor:hover, .htmlview .anchor:active {
-    color: #FFF;
-    background-color: #CCC;
-}
-
-/* =================== */
-/* = Custom elements = */
-/* =================== */
-.htmlview .autor_utwor {
-    font-size: 0.5em;
-    display: block;
-    line-height: 1.5em;
-    margin-bottom: 0.25em;
-}
-
-.htmlview .dzielo_nadrzedne {
-    /* */
-}
-
-.htmlview .podtytul {
-    /* */
-}
-
-.htmlview .didaskalia {
-    font-style: italic;
-    margin: 0.5em 0 0 1.5em;
-}
-
-.htmlview .kwestia {
-    margin: 0.5em 0 0;
-}
-
-.htmlview .strofa {
-    margin: 1.5em 0 0.5em auto;
-}
-
-/* wersy */
-.htmlview *[x-verse]:after {
-    content: "\feff";
-}
-
-.htmlview .strofa .wers_wciety, .htmlview .strofa .wers_wciety[data-wlf-typ='1'] {
-    margin-left: 1em;
-}
-
-.htmlview .strofa .wers_wciety[data-wlf-typ='2'], .htmlview .strofa .wers_cd {
-    margin-left: 2em;
-}
-
-.htmlview .strofa .wers_wciety[data-wlf-typ='3'] {
-    margin-left: 3em;
-}
-
-.htmlview .strofa .wers_wciety[data-wlf-typ='4'] {
-    margin-left: 4em;
-}
-
-.htmlview .strofa .wers_wciety[data-wlf-typ='5'] {
-    margin-left: 5em;
-}
-
-.htmlview .strofa .wers_wciety[data-wlf-typ='6'] {
-    margin-left: 6em;
-}
-
-.htmlview .strofa .wers_do_prawej {
-    text-align: right;
-}
-
-/* błędne wersy */
-.htmlview *:not(.strofa) > *[x-verse]::after {
-    content: "Ten wers znajduje się poza strofą.";
-    display: inline;
-    background: red;
-    font-size: 8pt;
-    border: 1px solid black;
-    -moz-border-radius: 10px;
-    -webkit-border-radius: 10px;
-    padding: 1px 1em;
-    margin-left: 1em;
-    vertical-align: super;
-}
-
-.htmlview .kwestia .strofa {
-    margin: 0;
-}
-
-.htmlview .akap, .htmlview .akap_cd, .htmlview .akap_dialog {
-    text-align: justify;
-    margin: 1.5em 0 0;
-}
-
-.htmlview .motto_container {
-       display: inline-block;
-       margin: 1.5em 0 0;
-       clear: right;
-}
-
-.htmlview .motto {
-    text-align: justify;
-    font-style: italic;
-}
-
-.htmlview p.motto_podpis {
-       position: relative;
-       right: -3em;
-       text-align: right;
-}
-
-.htmlview div.fragment {
-    border-bottom: 0.1em solid #999;
-    padding-bottom: 1.5em;
-}
-
-.htmlview div.nota p, .htmlview div.dedykacja p {
-    text-align: right;
-    font-style: italic;
-}
-
-.htmlview div.ramka {
-    border: 1px darkgray solid;
-}
-
-.htmlview .nota_red {
-    background-color: #eee;
-    border: 1px solid #888;
-    -moz-border-radius: 10px;
-    -webkit-border-radius: 10px;
-    display: block;
-    padding: 0 1em 1em 1em;
-}
-
-.htmlview hr.sekcja_swiatlo {
-    margin: 2em 0;
-    visibility: hidden;
-}
-
-.htmlview hr.separator_linia {
-    margin: 1.5em 0;
-    border: none;
-    border-bottom: 0.1em solid #000;
-}
-
-.htmlview p.sekcja_asterysk {
-    padding: 0;
-    margin: 1.5em 0;
-    text-align: center;
-}
-
-.htmlview p.sekcja_asterysk:after {
-    content: "*";
-}
-
-.htmlview div.lista_osob ol {
-    list-style: none;
-    padding: 0 0 0 1.5em;
-}
-
-.htmlview p.miejsce_czas {
-    font-style: italic;
-}
-
-.htmlview .mat, .htmlview .slowo_obce, .htmlview .tytul_dziela, .htmlview .didaskalia {
-    font-style: italic;
-}
-
-.htmlview .wyroznienie {
-    letter-spacing: 0.1em;
-}
-
-.htmlview .osoba {
-    font-style: normal;
-    font-variant: small-caps;
-}
-
-.htmlview em.wieksze_odstepy {
-    font-style: normal;
-    word-spacing: 1em;
-}
-
-.htmlview .parse-warning {
-    display: block;
-    font-size: 10pt;
-    background: #C0C0C0;
-    margin: 1em;
-}
-
-.parse-warning .message {
-    color: purple;
-    font-weight: bold;
-}
-
-/* Uwaga/Extra */
-.htmlview .uwaga {
-    background-color: #96e0e4;
-    border: 1px solid black;
-    -moz-border-radius: 10px;
-    -webkit-border-radius: 10px;
-    display: block;
-    font-size: 10pt;
-    line-height: 12pt;
-    padding: 2px 1em;
-    float: right;
-    max-width: 20%;
-    max-height: 24pt;
-    margin-left: 0.5em;
-    overflow: hidden;
-       z-index: 500;
-}
-
-div[x-node] > .uwaga {
-    float: none;
-    padding: 0.5em 1em;
-    margin: 1em;
-    max-width: 100%;
-    max-height: 100%;
-}
-
-.htmlview .uwaga:hover {
-    max-height: 100%;
-}
-
-.htmlview .annotation-inline-box .uwaga {
-    max-height: 100%;
-}
-
-/* Motywy */
-/* ======================== */
-/* = Footnotes and themes = */
-/* ======================== */
-.htmlview .begin, .htmlview .end {
-    background: green;
-}
-
-.htmlview .motyw {
-    /* position: absolute; */
-    float: right;
-    left: auto;
-    clear: right;
-    width: 10em;
-
-    font-style: normal;
-    font-weight: normal;
-    font-variant: normal;
-    letter-spacing: 0;
-    text-transform: none;
-    text-decoration: none;
-
-    font-size: 13px;
-    line-height: 18px;
-    background-color: #fff;
-    /*    border: 1px solid gray;
-
-     border-right: none;
-     */
-    z-index: 1;
-    -moz-user-select: -moz-none;
-    -webkit-user-select: none;
-    user-select: none;
-    margin-right: -12em;
-    text-align: left;
-    color: #999;
-}
-
-.htmlview .canon {
-    color: black;
-}
-
-.htmlview .noncanon {
-    color: #d00;
-}
-
-.htmlview .motyw[x-editable] {
-    border-left: 4px solid #DDD;
-    padding: 0.2em 0.2em 0.2em 0.5em;
-    margin-top: 0.2em;
-}
-
-/*
- * Przypisy
- */
-/* Znaczniki w tekście */
-.annotation {
-    font-style: normal;
-}
-
-.htmlview .annotation:before {
-    content: "[" counter(main) "]";
-    counter-increment: main;
-       vertical-align: super;
-    text-decoration: none;
-       font-size: 66%;
-}
-
-.htmlview .annotation:hover {
-    background-color: #ffcccc;
-}
-.htmlview .pe .annotation:hover {
-    background-color: #96e0e4;
-}
-*.htmlview *.annotation-inline-box {
-    position: static;
-}
-
-/*
- * Przypisy w tekście
- */
-.htmlview .annotation-inline-box:hover > span[x-annotation-box]{
-    display: block;
-}
-
-.htmlview .annotation-inline-box > span[x-annotation-box] {
-    display: none;
-    width: 300px;
-    font-size: 10pt;
-    line-height: 12pt;
-    font-weight: normal;
-    font-style: normal;
-    background: #fffe93;
-    border: 1px solid black;
-    border-radius: 10px;
-    -moz-border-radius: 10px;
-    -webkit-border-radius: 10px;
-    padding: 3px 5px;
-    text-decoration: none;
-    z-index: 600;
-}
-
-/*
- * Przypisy na końcu utworu (aktualnie nieuzywane)
- */
-.htmlview .annotations-block {
-    counter-reset: secondary;
-}
-
-.htmlview .annotations-block .annotation-body {
-    position: relative;
-    padding-left: 2.5em;
-    padding-top: 0.2em;
-}
-
-.htmlview .annotations-block .annotation-backref {
-    position: absolute;
-    top: 0.4em;
-    left: -0.4em;
-    width: 2.5em;
-    text-align: right;
-}
-
-.htmlview .annotations-block .annotation-backref:before {
-    content: "[" counter(secondary) "]";
-    counter-increment: secondary;
-}
-
-/*
- * EDITABLE ELEMENTS
- */
-.htmlview *[x-editable] {
-    position: relative;
-    padding: 2px;
-    margin-left: 0;
-    border: 1px solid transparent;
-}
-
-/* focused editable element */
-.htmlview *[x-editable]:hover {
-    z-index: 900;
-}
-
-.htmlview *[x-editable][x-open] {
-    visibility: hidden;
-}
-
-.edit-button, .delete-button, .accept-button, .tytul-button, .wyroznienie-button, .slowo-button, .znak-button {
-    position: absolute;
-    top: -21px;
-    left: -1px;
-    width: 72px;
-    height: 28px;
-    display: block;
-    /*    margin: 4px 0 2px 0;*/
-    padding: 5px 5px 2px 5px;
-    background-color: #FAFAFA;
-    /*    z-index: 3000;*/
-    /*    color: #FFF;
-     z-index: 1500;
-     */
-    border: 1px solid #DDD;
-    border-bottom: none;
-}
-
-.delete-button {
-    left: 70px;
-}
-
-.tytul-button {
-    left:150px;
-    width:100px;
-}
-
-.wyroznienie-button {
-    left:250px;
-    width:100px;
-}
-.slowo-button {
-    left:350px;
-    width:100px;
-}
-.znak-button {
-    left:450px;
-    width:100px;
-}
-
-.edit-button:hover, .edit-button:active,
-.delete-button:hover, .delete-button:active,
-.accept-button:hover, .accept-button:active,
-.tytul-button:hover, .tytul-button:active,
-.wyroznienie-button:hover, .wyroznienie-button:active,
-.slowo-button:hover, .slowo-button:active,
-.znak-button:hover, .znak-button:active {
-    /*    color: #FFF;*/
-    background-color: #999;
-    color: #FFF;
-}
-
-/*
- * VISIBILITY RULES
- */
-.default-menu {
-    visibility: inherit;
-    opacity: 0.2;
-}
-
-.default-menu:hover {
-    opacity: 1;
-    z-index: 5000;
-}
-
-.htmlview *[x-annotation-box] > .default-menu {
-    opacity: 1;
-}
-
-.htmlview *[x-editable][x-open] > .default-menu {
-    visibility: hidden;
-}
-
-.htmlview *[x-editable][x-open] *[x-annotation-box] > .default-menu {
-    visibility: hidden;
-}
-
-.htmlview *[x-editable] > .edit-menu {
-    visibility: hidden;
-}
-
-.htmlview *[x-editable] *[x-annotation-box] > .edit-menu {
-    visibility: hidden;
-}
-
-.htmlview *[x-editable][x-open] > .edit-menu {
-    visibility: visible;
-}
-
-.htmlview *[x-editable][x-open] *[x-annotation-box] > .edit-menu {
-    visibility: visible;
-}
-
-.html-editarea {
-    border: 0;
-    background-color: gray;
-    padding: 1px;
-    z-index: 2000;
-    position: absolute;
-}
-
-.html-editarea textarea {
-    position: absolute;
-    top: 0;
-    margin: 0;
-    padding: 0;
-    width: 100%;
-    height: 100%;
-    z-index: 0;
-    font-size: 10pt;
-    /*    background-color: ivory;*/
-}
-
-.htmlview .out-of-flow-text {
-    display: block;
-    font-family: monospace;
-    border: 2px solid red !important;
-    white-space: pre-line;
-}
-
-.htmlview .out-of-flow-text::before {
-    content: "Tekst w tej ramce nie jest otagowany!";
-    background-color: #ff6c6c;
-    color: black;
-    font-size: 10pt;
-    line-height: 12pt;
-    border: 1px solid black;
-    -moz-border-radius: 10px;
-    -webkit-border-radius: 10px;
-    padding: 5px 1em;
-    margin: 0 0 0 1em;
-    text-align: justify;
-    display: inline;
-    float: right;
-    max-width: 25%;
-}
-
-.unknown-tag {
-    background-color: yellow;
-    margin: -0.25em;
-    padding: 0.25em;
-    border: 1px solid orange;
-}
-
-.alien {
-    color: red;
-}
-
-/* specialChars */
-#specialCharsContainer {
-    text-align: center; 
-    width: 600px; 
-    height: 400px;
-    padding:20px; 
-    background-color: gray; 
-    position: absolute; 
-    top: 20px; 
-    left: 20px; 
-    z-index:1000;
-    overflow:auto;
-}
-#specialCharsContainer a {
-    color: white;
-    font-weight: bold;
-} 
-
-#tableSpecialChars td input {
-    background-color: transparent;
-    border:0;
-    color: white;
-} 
-
-#tableSpecialChars td input.recentSymbol {
-    background-color: white;
-    border:0;
-    color: black;
-} 
diff --git a/redakcja/static/css/html_print.css b/redakcja/static/css/html_print.css
deleted file mode 100644 (file)
index 808f095..0000000
+++ /dev/null
@@ -1,246 +0,0 @@
-/* Style widoku HTML. Nie należy tu ustawiać position ani marginesów */
-.htmlview {
-    font-size: 16px;
-    font-family: Georgia, "Times New Roman", serif;
-    line-height: 1.5em;
-    padding: 3em;
-}
-
-.htmlview div {
-    max-width: 36em;
-}
-
-.htmlview #toc {
-    display: none;
-}
-
-.htmlview a {
-    color: blue;
-    text-decoration: none;
-}
-
-.htmlview h1 {
-    font-size: 3em;
-    margin: 1.5em 0;
-    text-align: center;
-    line-height: 1.5em;
-    font-weight: bold;
-}
-
-.htmlview h2 {
-    font-size: 2em;
-    margin: 1.5em 0 0;
-    font-weight: bold;
-    line-height: 1.5em;
-}
-
-.htmlview h3 {
-    font-size: 1.5em;
-    margin: 1.5em 0 0;
-    font-weight: normal;
-    line-height: 1.5em;
-}
-
-.htmlview h4 {
-    font-size: 1em;
-    margin: 1.5em 0 0;
-    line-height: 1.5em;
-}
-
-.htmlview p {
-    margin: 0;
-}
-
-/* ======================== */
-/* = Footnotes and themes = */
-/* ======================== */
-.htmlview .theme-begin {
-    border-left: 0.1em solid #DDDDDD;
-    color: #777;
-    padding: 0 0.5em;
-    width: 7.5em;
-
-
-    font-style: normal;
-    font-weight: normal;
-    font-variant: normal;
-    letter-spacing: 0;
-    text-transform: none;
-    text-decoration: none;
-
-    font-size: 16px;
-    float: right;
-    margin-right: -9.5em;
-    clear: both;
-    left: 40em;
-    line-height: 1.5em;
-    text-align: left;
-}
-
-.htmlview .annotation {
-    font-style: normal;
-    font-weight: normal;
-    font-size: 12px;
-}
-
-.htmlview #footnotes .annotation {
-    display: block;
-    float: left;
-    width: 2.5em;
-    clear: both;
-}
-
-.htmlview #footnotes div {
-    margin: 1.5em 0 0 0;
-}
-
-.htmlview #footnotes p {
-    margin-left: 2.5em;
-    font-size: 0.875em;
-}
-
-.htmlview blockquote {
-    font-size: 0.875em;
-}
-
-/* ============= */
-/* = Numbering = */
-/* ============= */
-.htmlview p {
-    position: relative;
-}
-
-.htmlview .anchor {
-    position: absolute;
-    margin: 0em;
-    left: -3em;
-    color: #777;
-    font-size: 12px;
-    width: 2em;
-    text-align: center;
-    padding: 0.25em 0.5em;
-    line-height: 1.5em;
-}
-
-.htmlview .anchor:hover, .htmlview .anchor:active {
-    color: #FFF;
-    background-color: #CCC;
-}
-
-/* =================== */
-/* = Custom elements = */
-/* =================== */
-.htmlview span.author {
-    font-size: 0.5em;
-    display: block;
-    line-height: 1.5em;
-    margin-bottom: 0.25em;
-}
-
-.htmlview span.collection {
-    font-size: 0.375em;
-    display: block;
-    line-height: 1.5em;
-    margin-bottom: -0.25em;
-}
-
-.htmlview span.subtitle {
-    font-size: 0.5em;
-    display: block;
-    line-height: 1.5em;
-    margin-top: -0.25em;
-}
-
-.htmlview div.didaskalia {
-    font-style: italic;
-    margin: 0.5em 0 0 1.5em;
-}
-
-.htmlview div.kwestia {
-    margin: 0.5em 0 0;
-}
-
-.htmlview div.stanza {
-    margin: 1.5em 0 0;
-}
-
-.htmlview div.kwestia div.stanza {
-    margin: 0;
-}
-
-.htmlview p.paragraph {
-    text-align: justify;
-    margin: 1.5em 0 0;
-}
-
-.htmlview p.motto {
-    text-align: justify;
-    font-style: italic;
-    margin: 1.5em 0 0;
-}
-
-.htmlview p.motto_podpis {
-    font-size: 0.875em;
-    text-align: right;
-}
-
-.htmlview div.fragment {
-    border-bottom: 0.1em solid #999;
-    padding-bottom: 1.5em;
-}
-
-.htmlview div.note p, .htmlview div.dedication p,
-.htmlview div.note p.paragraph, .htmlview div.dedication p.paragraph {
-    text-align: right;
-    font-style: italic;
-}
-
-.htmlview div.frame {
-    border: 1px darkgray solid;
-}
-
-.htmlview hr.spacer {
-    height: 3em;
-    visibility: hidden;
-}
-
-.htmlview hr.spacer-line {
-    margin: 1.5em 0;
-    border: none;
-    border-bottom: 0.1em solid #000;
-}
-
-.htmlview p.spacer-asterisk {
-    padding: 0;
-    margin: 1.5em 0;
-    text-align: center;
-}
-
-.htmlview div.person-list ol {
-    list-style: none;
-    padding: 0 0 0 1.5em;
-}
-
-.htmlview p.place-and-time {
-    font-style: italic;
-}
-
-.htmlview em.math, .htmlview em.foreign-word,
-.htmlview em.book-title, .htmlview em.didaskalia {
-    font-style: italic;
-}
-
-.htmlview em.author-emphasis {
-    letter-spacing: 0.1em;
-}
-
-.htmlview em.person {
-    font-style: normal;
-    font-variant: small-caps;
-}
-
-.htmlview em.wieksze-odstepy
-{
-    font-style: normal;
-    word-spacing: 1em;
-}
diff --git a/redakcja/static/css/imgareaselect-default.css b/redakcja/static/css/imgareaselect-default.css
deleted file mode 100644 (file)
index 78c13dc..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * imgAreaSelect default style
- */
-
-.imgareaselect-border1 {
-       background: url(../img/jquery.imgareaselect/border-v.gif) repeat-y left top;
-}
-
-.imgareaselect-border2 {
-    background: url(../img/jquery.imgareaselect/border-h.gif) repeat-x left top;
-}
-
-.imgareaselect-border3 {
-    background: url(../img/jquery.imgareaselect/border-v.gif) repeat-y right top;
-}
-
-.imgareaselect-border4 {
-    background: url(../img/jquery.imgareaselect/border-h.gif) repeat-x left bottom;
-}
-
-.imgareaselect-border1, .imgareaselect-border2,
-.imgareaselect-border3, .imgareaselect-border4 {
-    opacity: 0.5;
-    filter: alpha(opacity=50);
-}
-
-.imgareaselect-handle {
-    background-color: #fff;
-    border: solid 1px #000;
-    opacity: 0.5;
-    filter: alpha(opacity=50);
-}
-
-.imgareaselect-outer {
-    background-color: #000;
-    opacity: 0.5;
-    filter: alpha(opacity=50);
-}
-
-.imgareaselect-selection {  
-}
diff --git a/redakcja/static/css/jquery.autocomplete.css b/redakcja/static/css/jquery.autocomplete.css
deleted file mode 100644 (file)
index f349b9e..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-.ac_results {
-       padding: 0px;
-       border: 1px solid black;
-       background-color: white;
-       overflow: hidden;
-       z-index: 99999;
-}
-
-.ac_results ul {
-       width: 100%;
-       list-style-position: outside;
-       list-style: none;
-       padding: 0;
-       margin: 0;
-}
-
-.ac_results li {
-       margin: 0px;
-       padding: 2px 5px;
-       cursor: default;
-       display: block;
-       /*
-       if width will be 100% horizontal scrollbar will apear
-       when scroll mode will be used
-       */
-       /*width: 100%;*/
-       font: menu;
-       font-size: 12px;
-       /*
-       it is very important, if line-height not setted or setted
-       in relative units scroll will be broken in firefox
-       */
-       line-height: 16px;
-       overflow: hidden;
-}
-
-.ac_odd {
-       background-color: #eee;
-}
-
-.ac_over {
-       background-color: #0A246A;
-       color: white;
-}
diff --git a/redakcja/static/css/master.css b/redakcja/static/css/master.css
deleted file mode 100644 (file)
index 0394598..0000000
+++ /dev/null
@@ -1,407 +0,0 @@
-a, a:visited, a:active {
-       color: blue;
-       text-decoration: none;
-}
-
-a:hover {
-       text-decoration: underline;
-}
-
-
-body {
-    margin: 0;
-    overflow: hidden;
-    padding: 0;
-    min-width: 960px;
-    width: 100%;
-    height: 100%;
-}
-
-.vsplitbar {
-    position: absolute;
-    top: 0px;
-    bottom: 0;
-    right: 0;
-    width: 26px;
-    /*background: #C1C1C1 url(../img/gallery.png) no-repeat scroll center center;*/
-    border-left: 2px solid #999;
-    border-right: 2px solid #999;
-    cursor: pointer;  
-    background: #C1C1C1; 
-    z-index:100;
-    cursor: col-resize;
-}
-
-.vsplitbar:hover {
-       background-color: #E6E6E6;
-}
-
-.vsplitbar p {
-    font: 12px Helvetica,Verdana,sans-serif;
-
-  -moz-transform: rotate(270deg);
-  -moz-transform-origin: 50% 50%;
-  -webkit-transform: rotate(270deg);
-  -webkit-transform-origin: 50% 50%; 
-  -o-transform: rotate(-270deg);
-  -o-transform-origin:  bottom left;
-  filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1);
-  margin: 250px auto;
-}
-
-#drag-layer {
-    position:absolute;
-    top:0;
-    bottom:0;
-    left:0;
-    right:0;
-    z-index:1000;
-    display: none;
-    cursor: col-resize;
-}
-
-.editor {
-    position: absolute;
-    top: 0px;
-    bottom: 0;
-    left: 0;
-    right: 30px;
-    overflow: hidden;
-}
-
-.sideless .editor {
-    right: 0;
-}
-.image-object {
-    padding-left: 1em;
-    font: 12px Sans, Helvetica, Verdana, sans-serif;
-}
-.image-object:hover {
-    cursor: pointer;
-}
-#objects-list .delete {
-    padding-left: 3px;
-    font: 10px Sans, Helvetica, Verdana, sans-serif;
-}
-#objects-list .delete:hover {
-    cursor: pointer;
-}
-
-#objects-list .active {
-    color: #800;
-}
-
-
-#editor.readonly .editor {
-       right: 0px;
-}
-
-#html-view {
-    overflow: auto;
-    position: absolute;
-    top: 33px;
-    bottom: 0;
-    left: 0;
-    right: 0;
-       z-index: 1;
-}
-
-#splitter {
-       position: fixed;
-       top: 31px;
-       left: 0px;
-       right: 0px;
-       bottom: 0px;
-       overflow: hidden;
-}
-
-
-#header {
-       position: fixed;
-       top: 0px;
-       right: 0px;
-       left: 0px;
-       height: 30px;
-       border-bottom: 1px solid #999;
-
-    margin: 0;
-    padding: 0;
-    background-color: #C1C1C1;
-    background-image: -webkit-gradient(linear, left top, left bottom, from(#C1C1C1), color-stop(0.9, #A2A2A2));
-
-       /* Firefox 3.6 */
-       background-image: -moz-linear-gradient(top left, #C1C1C1, #A2A2A2, 90%);
-
-    font: 11px Helvetica, Verdana, sans-serif;
-    font-weight: bold;
-
-    z-index: 100;
-}
-
-
-#header.saving {
-       background-color: #E1C1C1;
-}
-#header.saving #save-button {
-    display: none;
-}
-#save-attempt-info {
-    color: #801000;
-    display: none;
-}
-.saving #save-attempt-info {
-    display: inline;
-    font-weight: normal;
-}
-
-
-#header.out-of-date {
-    background-color: #E1C1C1;
-}
-#header.out-of-date #save-button {
-    display: none;
-}
-#out-of-date-info {
-    color: #801000;
-    display: none;
-}
-.out-of-date #out-of-date-info {
-    display: inline;
-}
-
-
-
-#header h1, #header h1 a {
-    margin: 0;
-    padding: 0;
-    font: 9px Helvetica, Verdana, sans-serif;
-    font-weight: bold;
-    float: left;
-    padding: 3px 3px 2px 3px;
-    color: #222;
-    #line-height: 20px;
-}
-
-#header h1 a {
-    text-decoration: none;
-    color: #222;
-}
-
-#header img {
-    border: 0;
-}
-
-.tabs {
-       overflow: hidden;
-    margin: 0;
-       padding: 0;
-       height: 31px;
-       border: 0px;
-       padding-left: 1em;
-    float: left;
-}
-
-.tabs a {
-    color: black;
-}
-
-#tabs-right {
-    float: right;
-    padding-right: 1em;
-}
-
-.tabs li {
-       margin-top: 6px;
-       margin-bottom: 0px;
-
-    -webkit-user-select: none;
-    cursor: pointer;
-    display: block;
-    float: left;
-
-    font-weight: bold;
-    color: #222;
-    margin-left: 4px;
-
-    background-color: #A2A2A2;
-
-       -moz-box-shadow: 1px -1px 2px rgba(127, 127, 127, 0.25);
-    -webkit-box-shadow: 1px -1px 2px rgba(127, 127, 127, 0.25);
-
-    border: 1px solid #999;
-       border-bottom-width: 0px;
-       -moz-border-radius: 4px 4px 0px 0px;
-       -webkit-border-radius: 4px;
-    -webkit-border-bottom-left-radius: 0px;
-    -webkit-border-bottom-right-radius: 0px;
-}
-
-#tabs li {
-    height: 18px;
-    padding-left: 12px;
-    padding-right: 12px;
-    padding-top: 5px;
-}
-
-.tabs li.active {
-    background-color: #C1C1C1;
-}
-
-
-#tabs-right li {
-    height: 20px;
-    padding-left: 12px;
-    padding-right: 12px;
-    padding-top: 3px;
-}
-
-
-
-#tools {
-       float: right;
-       clear: right;
-       overflow: hidden;
-    margin: 0;
-       padding: 0;
-       height: 30px;
-       margin-right: 5px;
-       line-height: 30px;
-       font-size: 10px;
-       vertical-align: middle;
-}
-
-/* Remove extra padding in Firefox */
-button::-moz-focus-inner {
-    border: 0;
-    padding: 0;
-}
-
-p { margin: 0;}
-
-#body-wrap {
-    height: 100%;
-    width: 100%;
-}
-
-#content {
-    height: 100%;
-    width: 100%;
-}
-
-#loading-overlay {
-    background-color: #FFF;
-    position: absolute;
-    z-index: 1000;
-    padding: 0;
-    top: 0;
-    left: 0;
-    width: 100%;
-    height: 100%;
-}
-
-#loading-message {
-    position: absolute;
-    height: 20px;
-    width: 120px;
-    left: 50%;
-    margin-left: -60px;
-    top: 50%;
-    margin-top: -10px;
-/*    text-align: center;*/
-}
-
-#loading-message img {
-    float: left;
-    margin-right: 10px;
-    margin-top: -6px;
-}
-
-/*
- * CodeMirror
- */
-
-.CodeMirror-line-numbers {
-       padding: 0px;
-       padding-top: 5px;
-       text-align: right;
-       overflow: hidden;
-       width: 40px;
-       border-right: 1px solid black;
-       background-color: #e6e6fa;
-}
-
-.CodeMirror-line-numbers div {
-       display: block;
-       font-family:"Lucida Console", monospace;
-    font-size: 13px;
-    line-height: 18px;
-       padding-right: 5px;
-}
-
-img.tabclose {
-       padding-left: 8px;
-       width: 16px;
-       height: 16px;
-       vertical-align: middle;
-       vertical-align: text-bottom;
-}
-
-/*
- * HTML Editor view
- */
-
-.htmlview {
-       z-index: 1;
-       overflow: hidden;
-}
-
-.htmlview .active[x-editable] {
-    background-color: #FAFAFA;
-    border: 1px solid #DDD;
-/*    -webkit-transition: all 1s linear;*/
-}
-
-.blockOverlay {
-       background-color: black;
-       opacity: 0.4;
-}
-
-.poezja_cyt {
-    margin:1.5em 2em 0;
-    font-size:0.875em
-}
-
-.wers_akap {
-       padding-left: 1em;
-}
-
-.saveNotify {
-    position:absolute; 
-    bottom:22px; 
-    right:7px; 
-    z-index:800;
-    background-color: #FFFF69; 
-    padding:10px; 
-    border: 1px solid black;
-    border-radius: 5px;
-    -moz-border-radius: 15px;
-}
-
-.notifyTip {
-    font-size:12px; float:right;
-}
-
-.saveNotify span {
-    font-weight: bold;
-}
-
-
-
-.scrolled {
-    position: absolute;
-    top: 0;
-    left: 0;
-    right: 0;
-    bottom: 0;
-    overflow: auto;
-}
diff --git a/redakcja/static/css/summary.css b/redakcja/static/css/summary.css
deleted file mode 100644 (file)
index 5111ad5..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-#summary-view-editor {
-       background: white;
-}
-
-#summary-view {
-       padding: 1em;
-}
-
-#summary-view .summary-cover-area {
-       float: left;
-       margin: 1em;
-       margin-right: 2em;
-}
-
-#summary-view .book-cover {
-       height: 300px;
-       width: 216px;
-}
-
-#summary-view p {
-       margin: 0.5em;
-}
-
-#summary-view label {
-       font-weight: bold;
-}
-
-#summary-view .book-cover {
-
-}
diff --git a/redakcja/static/css/toolbar.css b/redakcja/static/css/toolbar.css
deleted file mode 100644 (file)
index 7b96820..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Toolbars
- */
-
-.toolbar {
-    border-bottom: 1px solid #777;
-    background-color: #C1C1C1;
-    margin: 0px;
-    padding: 0px;
-    z-index: 100;
-    height: 28px;
-    position: relative;
-}
-
-.toolbar > .group_selector {
-       position: absolute;
-
-    margin: 1px 5px 1px 0;
-    border: 1px solid #999;
-    padding: 1px;
-
-    top: 0px;
-    left: 0px;
-    bottom: 0px;
-    width: 180px;
-}
-
-.toolbar > .group_selector + button {
-       position: absolute;
-       width: 20px;
-       top: 0px;
-    left: 180px;
-    bottom: 0px;
-}
-
-.toolbar > .button_group_container {
-       position: absolute;
-       top: 0px;
-       left: 200px;
-       right: 20px;
-       bottom: 0px;
-
-       overflow: hidden;
-}
-
-.toolbar > .button_group_container + button {
-       position: absolute;
-       width: 20px;
-       top: 0px;
-    right: 0px;
-    bottom: 0px;
-}
-
-.toolbar ul.button_group {
-       margin: 0;
-       padding: 0;
-       width: 10000%;
-}
-
-.toolbar ul.button_group li {
-       display: inline-block;
-       margin: 0;
-       padding: 0;
-}
-
-
-.toolbar .button_group button {
-    display: block;
-    margin: 4px 0 2px 0;
-    padding: 2px 5px;
-    border: none;
-    background: none;
-    color: #424242;
-}
-
-.toolbar .button_group button img {
-       margin: 0;
-       padding: 0;
-       margin-bottom: -3px;
-}
-
-.toolbar .button_group button:hover,
-.toolbar .button_group button:active {
-    background: #777;
-    color: #FFF;
-    -webkit-border-radius: 10px;
-    -moz-border-radius: 10px;
-}
\ No newline at end of file
diff --git a/redakcja/static/css/xmlcolors_20100906.css b/redakcja/static/css/xmlcolors_20100906.css
deleted file mode 100644 (file)
index 7446450..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-html {
-  cursor: text;
-}
-
-.editbox {
-  margin: .4em;
-  padding: 0;
-  /* fnp changes */
-  margin-top: 5px;
-  margin-left: 5px;
-  font-family: "Lucida Console", monospace;
-  font-size: 13px;
-  line-height: 18px;
-  /* end fnp changes */
-  color: black;
-}
-
-.editbox p {
-  margin: 0;
-}
-
-span.xml-tagname {
-  color: #A0B;
-}
-
-span.xml-attribute {
-  color: #281;
-}
-
-span.xml-punctuation {
-  color: black;
-}
-
-span.xml-attname {
-  color: #00F;
-}
-
-span.xml-comment {
-  color: #A70;
-}
-
-span.xml-cdata {
-  color: #48A;
-}
-
-span.xml-processing {
-  color: #999;
-}
-
-span.xml-entity {
-  color: #A22;
-}
-
-span.xml-error {
- color: #F00 !important;
-  /* fnp changes */
-  font-weight: bolder;
-  /* end fnp changes */
-}
-
-span.xml-text {
-  color: black;
-}
diff --git a/redakcja/static/email_mangler/email_mangler.js b/redakcja/static/email_mangler/email_mangler.js
deleted file mode 100755 (executable)
index 03c1a91..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-var rot13 = function(s){
-    return s.replace(/[a-zA-Z]/g, function(c){
-        return String.fromCharCode((c <= "Z" ? 90 : 122) >= (c = c.charCodeAt(0) + 13) ? c : c - 26);
-    });
-};
-
-(function($) {
-    $(function() {
-
-        $(".mangled").each(function() {
-            $this = $(this);
-            var email = rot13($this.attr('data-addr1')) + '@' +
-                rot13($this.attr('data-addr2'));
-            $this.attr('href', "mailto:" + email);
-            $this.html(email);
-        });
-
-
-    });
-})(jQuery);
-
diff --git a/redakcja/static/icons/clock.png b/redakcja/static/icons/clock.png
deleted file mode 100644 (file)
index 57e496f..0000000
Binary files a/redakcja/static/icons/clock.png and /dev/null differ
diff --git a/redakcja/static/icons/close.png b/redakcja/static/icons/close.png
deleted file mode 100644 (file)
index 5c08b05..0000000
Binary files a/redakcja/static/icons/close.png and /dev/null differ
diff --git a/redakcja/static/icons/go-home.png b/redakcja/static/icons/go-home.png
deleted file mode 100644 (file)
index 9d62109..0000000
Binary files a/redakcja/static/icons/go-home.png and /dev/null differ
diff --git a/redakcja/static/icons/go-next.png b/redakcja/static/icons/go-next.png
deleted file mode 100644 (file)
index 6ef8de7..0000000
Binary files a/redakcja/static/icons/go-next.png and /dev/null differ
diff --git a/redakcja/static/icons/go-previous.png b/redakcja/static/icons/go-previous.png
deleted file mode 100644 (file)
index 659cd90..0000000
Binary files a/redakcja/static/icons/go-previous.png and /dev/null differ
diff --git a/redakcja/static/icons/image-x-generic.png b/redakcja/static/icons/image-x-generic.png
deleted file mode 100644 (file)
index 68da502..0000000
Binary files a/redakcja/static/icons/image-x-generic.png and /dev/null differ
diff --git a/redakcja/static/icons/ok.png b/redakcja/static/icons/ok.png
deleted file mode 100644 (file)
index ea094f4..0000000
Binary files a/redakcja/static/icons/ok.png and /dev/null differ
diff --git a/redakcja/static/icons/revert.png b/redakcja/static/icons/revert.png
deleted file mode 100644 (file)
index de96265..0000000
Binary files a/redakcja/static/icons/revert.png and /dev/null differ
diff --git a/redakcja/static/icons/revert_.png b/redakcja/static/icons/revert_.png
deleted file mode 100755 (executable)
index afdf20d..0000000
Binary files a/redakcja/static/icons/revert_.png and /dev/null differ
diff --git a/redakcja/static/icons/stop.png b/redakcja/static/icons/stop.png
deleted file mode 100644 (file)
index adf1b73..0000000
Binary files a/redakcja/static/icons/stop.png and /dev/null differ
diff --git a/redakcja/static/icons/system-search.png b/redakcja/static/icons/system-search.png
deleted file mode 100644 (file)
index fd7f0b0..0000000
Binary files a/redakcja/static/icons/system-search.png and /dev/null differ
diff --git a/redakcja/static/icons/user.png b/redakcja/static/icons/user.png
deleted file mode 100644 (file)
index 45390a6..0000000
Binary files a/redakcja/static/icons/user.png and /dev/null differ
diff --git a/redakcja/static/icons/zoom.png b/redakcja/static/icons/zoom.png
deleted file mode 100644 (file)
index 6033b4d..0000000
Binary files a/redakcja/static/icons/zoom.png and /dev/null differ
diff --git a/redakcja/static/icons/zoom_in.png b/redakcja/static/icons/zoom_in.png
deleted file mode 100644 (file)
index c7feedc..0000000
Binary files a/redakcja/static/icons/zoom_in.png and /dev/null differ
diff --git a/redakcja/static/icons/zoom_out.png b/redakcja/static/icons/zoom_out.png
deleted file mode 100644 (file)
index fdd7124..0000000
Binary files a/redakcja/static/icons/zoom_out.png and /dev/null differ
diff --git a/redakcja/static/img/angel-left.png b/redakcja/static/img/angel-left.png
deleted file mode 100644 (file)
index 7744103..0000000
Binary files a/redakcja/static/img/angel-left.png and /dev/null differ
diff --git a/redakcja/static/img/angel-right.png b/redakcja/static/img/angel-right.png
deleted file mode 100644 (file)
index df85d33..0000000
Binary files a/redakcja/static/img/angel-right.png and /dev/null differ
diff --git a/redakcja/static/img/arrow-down.png b/redakcja/static/img/arrow-down.png
deleted file mode 100644 (file)
index 0e32315..0000000
Binary files a/redakcja/static/img/arrow-down.png and /dev/null differ
diff --git a/redakcja/static/img/arrow-up.png b/redakcja/static/img/arrow-up.png
deleted file mode 100644 (file)
index cdf9cf6..0000000
Binary files a/redakcja/static/img/arrow-up.png and /dev/null differ
diff --git a/redakcja/static/img/gallery.png b/redakcja/static/img/gallery.png
deleted file mode 100644 (file)
index 851a678..0000000
Binary files a/redakcja/static/img/gallery.png and /dev/null differ
diff --git a/redakcja/static/img/jquery.imgareaselect/border-anim-h.gif b/redakcja/static/img/jquery.imgareaselect/border-anim-h.gif
deleted file mode 100644 (file)
index ec9f5da..0000000
Binary files a/redakcja/static/img/jquery.imgareaselect/border-anim-h.gif and /dev/null differ
diff --git a/redakcja/static/img/jquery.imgareaselect/border-anim-v.gif b/redakcja/static/img/jquery.imgareaselect/border-anim-v.gif
deleted file mode 100644 (file)
index 331cc90..0000000
Binary files a/redakcja/static/img/jquery.imgareaselect/border-anim-v.gif and /dev/null differ
diff --git a/redakcja/static/img/jquery.imgareaselect/border-h.gif b/redakcja/static/img/jquery.imgareaselect/border-h.gif
deleted file mode 100644 (file)
index a2aa5b0..0000000
Binary files a/redakcja/static/img/jquery.imgareaselect/border-h.gif and /dev/null differ
diff --git a/redakcja/static/img/jquery.imgareaselect/border-v.gif b/redakcja/static/img/jquery.imgareaselect/border-v.gif
deleted file mode 100644 (file)
index 4bfd555..0000000
Binary files a/redakcja/static/img/jquery.imgareaselect/border-v.gif and /dev/null differ
diff --git a/redakcja/static/img/logo-220.png b/redakcja/static/img/logo-220.png
deleted file mode 100644 (file)
index 9b15e88..0000000
Binary files a/redakcja/static/img/logo-220.png and /dev/null differ
diff --git a/redakcja/static/img/logo.png b/redakcja/static/img/logo.png
deleted file mode 100644 (file)
index 398f45d..0000000
Binary files a/redakcja/static/img/logo.png and /dev/null differ
diff --git a/redakcja/static/img/pr-icon.png b/redakcja/static/img/pr-icon.png
deleted file mode 100644 (file)
index fc0f9fe..0000000
Binary files a/redakcja/static/img/pr-icon.png and /dev/null differ
diff --git a/redakcja/static/img/sample_cover.png b/redakcja/static/img/sample_cover.png
deleted file mode 100644 (file)
index f7a678e..0000000
Binary files a/redakcja/static/img/sample_cover.png and /dev/null differ
diff --git a/redakcja/static/img/spinner.gif b/redakcja/static/img/spinner.gif
deleted file mode 100644 (file)
index c69e937..0000000
Binary files a/redakcja/static/img/spinner.gif and /dev/null differ
diff --git a/redakcja/static/img/wl-orange.png b/redakcja/static/img/wl-orange.png
deleted file mode 100644 (file)
index d5c56d0..0000000
Binary files a/redakcja/static/img/wl-orange.png and /dev/null differ
diff --git a/redakcja/static/img/wl-orange.svg b/redakcja/static/img/wl-orange.svg
deleted file mode 100644 (file)
index a7ef60b..0000000
+++ /dev/null
@@ -1,84 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
-   xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://creativecommons.org/ns#"
-   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-   xmlns:svg="http://www.w3.org/2000/svg"
-   xmlns="http://www.w3.org/2000/svg"
-   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
-   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
-   id="svg2"
-   version="1.1"
-   inkscape:version="0.91 r13725"
-   width="171.35579"
-   height="172.75761"
-   viewBox="0 0 171.35578 172.75761"
-   sodipodi:docname="wl-orange.svg"
-   inkscape:export-filename="/home/janek/Desktop/pr-icon.png"
-   inkscape:export-xdpi="8.4035673"
-   inkscape:export-ydpi="8.4035673">
-  <metadata
-     id="metadata8">
-    <rdf:RDF>
-      <cc:Work
-         rdf:about="">
-        <dc:format>image/svg+xml</dc:format>
-        <dc:type
-           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
-        <dc:title></dc:title>
-      </cc:Work>
-    </rdf:RDF>
-  </metadata>
-  <defs
-     id="defs6" />
-  <sodipodi:namedview
-     pagecolor="#ffffff"
-     bordercolor="#666666"
-     borderopacity="1"
-     objecttolerance="10"
-     gridtolerance="10"
-     guidetolerance="10"
-     inkscape:pageopacity="0"
-     inkscape:pageshadow="2"
-     inkscape:window-width="1012"
-     inkscape:window-height="652"
-     id="namedview4"
-     showgrid="false"
-     fit-margin-top="0"
-     fit-margin-left="0"
-     fit-margin-right="0"
-     fit-margin-bottom="0"
-     inkscape:zoom="1.5132586"
-     inkscape:cx="81.786073"
-     inkscape:cy="104.54976"
-     inkscape:window-x="444"
-     inkscape:window-y="29"
-     inkscape:window-maximized="0"
-     inkscape:current-layer="svg2" />
-  <path
-     style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#ff8000;stroke-width:6;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.89411765;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
-     d="M 69.761219,87.45616 35.01474,92.310571 2.9997126,63.566463 38.619672,51.969714 73.499413,79.979772 c 2.693747,2.268439 7.087505,-2.82549 5.220188,-5.333568 l -28.116626,-29.712"
-     id="path3338"
-     inkscape:connector-curvature="0"
-     sodipodi:nodetypes="ccccccc" />
-  <path
-     sodipodi:nodetypes="ccccccc"
-     inkscape:connector-curvature="0"
-     id="path4140"
-     d="M 83.899635,69.761218 79.045224,35.014739 107.78933,2.9997126 119.38608,38.619671 91.376023,73.499408 c -2.929265,3.189374 2.329871,9.069983 4.672742,6.54184 L 126.42159,50.602974"
-     style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#ff8000;stroke-width:6;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.89411765;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
-  <path
-     style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#ff8000;stroke-width:6;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.89411765;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
-     d="m 101.59457,85.301455 34.74649,-4.854411 32.01502,28.744106 -35.61996,11.59675 -34.879733,-28.010057 c -2.693755,-2.433646 -8.739571,2.164664 -6.54184,4.672742 l 29.438273,30.372825"
-     id="path4142"
-     inkscape:connector-curvature="0"
-     sodipodi:nodetypes="ccccccc" />
-  <path
-     sodipodi:nodetypes="ccccccc"
-     inkscape:connector-curvature="0"
-     id="path4144"
-     d="m 86.054345,102.99639 4.85441,34.74649 -28.74411,32.01502 -11.59675,-35.61996 28.01006,-34.879733 c 3.230746,-3.230746 -2.164666,-8.904777 -4.67274,-6.54184 l -30.37283,29.438273"
-     style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#ff8000;stroke-width:6;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.89411765;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
-</svg>
diff --git a/redakcja/static/js/book_text/book.js b/redakcja/static/js/book_text/book.js
deleted file mode 100644 (file)
index 335fe39..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-$(function() {
-    function scrollToAnchor(anchor) {
-        if (anchor) {
-            var anchor_name = anchor.slice(1);
-            var element = $('a[name="' + anchor_name + '"]');
-            if (element.length > 0) {
-                $.scrollTo(element, 500, {offset: {top: -50, left: 0}});
-                foot_elem = $('#footnotes a[name="' + anchor_name + '"]');
-                if (foot_elem.length > 0) {
-                    $(element).parent().highlightFade('yellow');
-                }
-                window.location.hash = anchor;
-            }
-        }
-    }
-
-    $.highlightFade.defaults.speed = 3000;
-    $('#toc').hide();
-    if ($('#toc li').length == 0) {
-        $('#menu li a[href="#toc"]').remove();
-    }
-    if ($('#nota_red').length == 0) {
-        $('#menu li a[href="#nota_red"]').remove();
-    }
-
-    // On page load, scroll to anchor
-    scrollToAnchor(window.location.hash)
-
-    $('#toc, #themes, #book-text').delegate('click', 'a', function(event) {
-        event.preventDefault();
-        $('#menu li a.selected').click();
-        scrollToAnchor($(this).attr('href'));
-    });
-
-    $('#menu li a.menu').toggle(function() {
-        $('#menu li a.selected').click();
-        $(this).addClass('selected');
-        $($(this).attr('href')).slideDown('fast');
-    }, function() {
-        $(this).removeClass('selected');
-        $($(this).attr('href')).slideUp('fast');
-    });
-    
-
-    if (window.getSelection) {
-        $('.theme-begin').click(function() {
-            var selection = window.getSelection();
-            selection.removeAllRanges();
-            var range = document.createRange();
-
-            var e = $(".theme-end[fid='" + $(this).attr('fid') + "']")[0];
-
-            if (e) {
-                range.setStartAfter(this);
-                range.setEndBefore(e);
-                selection.addRange(range);
-            }
-        });
-    }
-
-});
diff --git a/redakcja/static/js/book_text/jquery.eventdelegation.js b/redakcja/static/js/book_text/jquery.eventdelegation.js
deleted file mode 100644 (file)
index 52fce07..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * jQuery Event Delegation Plugin - jquery.eventdelegation.js
- * Fast flexible event handling
- *
- * January 2008 - Randy Morey (http://dev.distilldesign.com/)
- */
-
-(function ($) {
-       /* setup list of allowed events for event delegation
-        * only events that bubble are appropriate
-        */
-       var allowed = {};
-       $.each([
-               'click',
-               'dblclick',
-               'mousedown',
-               'mouseup',
-               'mousemove',
-               'mouseover',
-               'mouseout',
-               'keydown',
-               'keypress',
-               'keyup'
-               ], function(i, eventName) {
-                       allowed[eventName] = true;
-       });
-
-       $.fn.extend({
-               delegate: function (event, selector, f) {
-                       return $(this).each(function () {
-                               if (allowed[event])
-                                       $(this).bind(event, function (e) {
-                                               var el = $(e.target),
-                                                       result = false;
-
-                                               while (!$(el).is('body')) {
-                                                       if ($(el).is(selector)) {
-                                                               result = f.apply($(el)[0], [e]);
-                                                               if (result === false)
-                                                                       e.preventDefault();
-                                                               return;
-                                                       }
-
-                                                       el = $(el).parent();
-                                               }
-                                       });
-                       });
-               },
-               undelegate: function (event) {
-                       return $(this).each(function () {
-                               $(this).unbind(event);
-                       });
-               }
-       });
-})(jQuery);
\ No newline at end of file
diff --git a/redakcja/static/js/book_text/jquery.highlightfade.js b/redakcja/static/js/book_text/jquery.highlightfade.js
deleted file mode 100644 (file)
index bbe39f0..0000000
+++ /dev/null
@@ -1,150 +0,0 @@
-/**
- *  jQuery Plugin highlightFade (jquery.offput.ca/highlightFade)
- *  (c) 2006 Blair Mitchelmore (offput.ca) blair@offput.ca
- */
-/**
- * This is version 0.7 of my highlightFade plugin. It follows the yellow fade technique of Web 2.0 fame
- * but expands it to allow any starting colour and allows you to specify the end colour as well.
- *
- * For the moment, I'm done with this plug-in. Unless I come upon a really cool feature it should have
- * this plug-in will only receive updates to ensure future compatibility with jQuery.
- *
- * As of now (Aug. 16, 2006) the plugin has been written with the 1.0.1 release of jQuery (rev 249) which
- * is available from http://jquery.com/src/jquery-1.0.1.js
- *
- * A note regarding rgb() syntax: I noticed that most browsers implement rgb syntax as either an integer
- * (0-255) or percentage (0-100%) value for each field, that is, rgb(i/p,i/p,i/p); however, the W3C
- * standard clearly defines it as "either three integer values or three percentage values" [http://www.w3.org/TR/CSS21/syndata.html]
- * which I choose to follow despite the error redundancy of the typical behaviour browsers employ.
- *
- * Changelog:
- *
- *    0.7:
- *        - Added the awesome custom attribute support written by George Adamson (slightly modified)
- *        - Removed bgColor plugin dependency seeing as attr is customizable now...
- *    0.6:
- *        - Abstracted getBGColor into its own plugin with optional test and data retrieval functions
- *        - Converted all $ references to jQuery references as John's code seems to be shifting away
- *          from that and I don't want to have to update this for a long time.
- *    0.5:
- *        - Added simple argument syntax for only specifying start colour of event
- *        - Removed old style argument syntax
- *        - Added 'interval', 'final, and 'end' properties
- *        - Renamed 'color' property to 'start'
- *        - Added second argument to $.highlightFade.getBGColor to bypass the e.highlighting check
- *    0.4:
- *        - Added rgb(%,%,%) color syntax
- *    0.3:
- *        - Fixed bug when event was called while parent was also running event corrupting the
- *          the background colour of the child
- *    0.2:
- *        - Fixed bug where an unspecified onComplete function made the page throw continuous errors
- *        - Fixed bug where multiple events on the same element would speed each subsequent event
- *    0.1:
- *        - Initial Release
- *
- * @author          Blair Mitchelmore (blair@offput.ca)
- * @version         0.5
- */
-jQuery.fn.highlightFade = function(settings) {
-       var o = (settings && settings.constructor == String) ? {start: settings} : settings || {};
-       var d = jQuery.highlightFade.defaults;
-       var i = o['interval'] || d['interval'];
-       var a = o['attr'] || d['attr'];
-       var ts = {
-               'linear': function(s,e,t,c) { return parseInt(s+(c/t)*(e-s)); },
-               'sinusoidal': function(s,e,t,c) { return parseInt(s+Math.sin(((c/t)*90)*(Math.PI/180))*(e-s)); },
-               'exponential': function(s,e,t,c) { return parseInt(s+(Math.pow(c/t,2))*(e-s)); }
-       };
-       var t = (o['iterator'] && o['iterator'].constructor == Function) ? o['iterator'] : ts[o['iterator']] || ts[d['iterator']] || ts['linear'];
-       if (d['iterator'] && d['iterator'].constructor == Function) t = d['iterator'];
-       return this.each(function() {
-               if (!this.highlighting) this.highlighting = {};
-               var e = (this.highlighting[a]) ? this.highlighting[a].end : jQuery.highlightFade.getBaseValue(this,a) || [255,255,255];
-               var c = jQuery.highlightFade.getRGB(o['start'] || o['colour'] || o['color'] || d['start'] || [255,255,128]);
-               var s = jQuery.speed(o['speed'] || d['speed']);
-               var r = o['final'] || (this.highlighting[a] && this.highlighting[a].orig) ? this.highlighting[a].orig : jQuery.curCSS(this,a);
-               if (o['end'] || d['end']) r = jQuery.highlightFade.asRGBString(e = jQuery.highlightFade.getRGB(o['end'] || d['end']));
-               if (typeof o['final'] != 'undefined') r = o['final'];
-               if (this.highlighting[a] && this.highlighting[a].timer) window.clearInterval(this.highlighting[a].timer);
-               this.highlighting[a] = { steps: ((s.duration) / i), interval: i, currentStep: 0, start: c, end: e, orig: r, attr: a };
-               jQuery.highlightFade(this,a,o['complete'],t);
-       });
-};
-
-jQuery.highlightFade = function(e,a,o,t) {
-       e.highlighting[a].timer = window.setInterval(function() {
-               var newR = t(e.highlighting[a].start[0],e.highlighting[a].end[0],e.highlighting[a].steps,e.highlighting[a].currentStep);
-               var newG = t(e.highlighting[a].start[1],e.highlighting[a].end[1],e.highlighting[a].steps,e.highlighting[a].currentStep);
-               var newB = t(e.highlighting[a].start[2],e.highlighting[a].end[2],e.highlighting[a].steps,e.highlighting[a].currentStep);
-               jQuery(e).css(a,jQuery.highlightFade.asRGBString([newR,newG,newB]));
-               if (e.highlighting[a].currentStep++ >= e.highlighting[a].steps) {
-                       jQuery(e).css(a,e.highlighting[a].orig || '');
-                       window.clearInterval(e.highlighting[a].timer);
-                       e.highlighting[a] = null;
-                       if (o && o.constructor == Function) o.call(e);
-               }
-       },e.highlighting[a].interval);
-};
-
-jQuery.highlightFade.defaults = {
-       start: [255,255,128],
-       interval: 50,
-       speed: 400,
-       attr: 'backgroundColor'
-};
-
-jQuery.highlightFade.getRGB = function(c,d) {
-       var result;
-       if (c && c.constructor == Array && c.length == 3) return c;
-       if (result = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(c))
-               return [parseInt(result[1]),parseInt(result[2]),parseInt(result[3])];
-       else if (result = /rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(c))
-               return [parseFloat(result[1])*2.55,parseFloat(result[2])*2.55,parseFloat(result[3])*2.55];
-       else if (result = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(c))
-               return [parseInt("0x" + result[1]),parseInt("0x" + result[2]),parseInt("0x" + result[3])];
-       else if (result = /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(c))
-               return [parseInt("0x"+ result[1] + result[1]),parseInt("0x" + result[2] + result[2]),parseInt("0x" + result[3] + result[3])];
-       else
-               return jQuery.highlightFade.checkColorName(c) || d || null;
-};
-
-jQuery.highlightFade.asRGBString = function(a) {
-       return "rgb(" + a.join(",") + ")";
-};
-
-jQuery.highlightFade.getBaseValue = function(e,a,b) {
-       var s, t;
-       b = b || false;
-       t = a = a || jQuery.highlightFade.defaults['attr'];
-       do {
-               s = jQuery(e).css(t || 'backgroundColor');
-               if ((s  != '' && s != 'transparent') || (e.tagName.toLowerCase() == "body") || (!b && e.highlighting && e.highlighting[a] && e.highlighting[a].end)) break;
-               t = false;
-       } while (e = e.parentNode);
-       if (!b && e.highlighting && e.highlighting[a] && e.highlighting[a].end) s = e.highlighting[a].end;
-       if (s == undefined || s == '' || s == 'transparent') s = [255,255,255];
-       return jQuery.highlightFade.getRGB(s);
-};
-
-jQuery.highlightFade.checkColorName = function(c) {
-       if (!c) return null;
-       switch(c.replace(/^\s*|\s*$/g,'').toLowerCase()) {
-               case 'aqua': return [0,255,255];
-               case 'black': return [0,0,0];
-               case 'blue': return [0,0,255];
-               case 'fuchsia': return [255,0,255];
-               case 'gray': return [128,128,128];
-               case 'green': return [0,128,0];
-               case 'lime': return [0,255,0];
-               case 'maroon': return [128,0,0];
-               case 'navy': return [0,0,128];
-               case 'olive': return [128,128,0];
-               case 'purple': return [128,0,128];
-               case 'red': return [255,0,0];
-               case 'silver': return [192,192,192];
-               case 'teal': return [0,128,128];
-               case 'white': return [255,255,255];
-               case 'yellow': return [255,255,0];
-       }
-};
diff --git a/redakcja/static/js/book_text/jquery.scrollto.js b/redakcja/static/js/book_text/jquery.scrollto.js
deleted file mode 100644 (file)
index c403ab9..0000000
+++ /dev/null
@@ -1,194 +0,0 @@
-/**
- * jQuery.ScrollTo
- * Copyright (c) 2007-2008 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com
- * Dual licensed under MIT and GPL.
- * Date: 9/11/2008
- *
- * @projectDescription Easy element scrolling using jQuery.
- * http://flesler.blogspot.com/2007/10/jqueryscrollto.html
- * Tested with jQuery 1.2.6. On FF 2/3, IE 6/7, Opera 9.2/5 and Safari 3. on Windows.
- *
- * @author Ariel Flesler
- * @version 1.4
- *
- * @id jQuery.scrollTo
- * @id jQuery.fn.scrollTo
- * @param {String, Number, DOMElement, jQuery, Object} target Where to scroll the matched elements.
- *       The different options for target are:
- *             - A number position (will be applied to all axes).
- *             - A string position ('44', '100px', '+=90', etc ) will be applied to all axes
- *             - A jQuery/DOM element ( logically, child of the element to scroll )
- *             - A string selector, that will be relative to the element to scroll ( 'li:eq(2)', etc )
- *             - A hash { top:x, left:y }, x and y can be any kind of number/string like above.
- * @param {Number} duration The OVERALL length of the animation, this argument can be the settings object instead.
- * @param {Object,Function} settings Optional set of settings or the onAfter callback.
- *      @option {String} axis Which axis must be scrolled, use 'x', 'y', 'xy' or 'yx'.
- *      @option {Number} duration The OVERALL length of the animation.
- *      @option {String} easing The easing method for the animation.
- *      @option {Boolean} margin If true, the margin of the target element will be deducted from the final position.
- *      @option {Object, Number} offset Add/deduct from the end position. One number for both axes or { top:x, left:y }.
- *      @option {Object, Number} over Add/deduct the height/width multiplied by 'over', can be { top:x, left:y } when using both axes.
- *      @option {Boolean} queue If true, and both axis are given, the 2nd axis will only be animated after the first one ends.
- *      @option {Function} onAfter Function to be called after the scrolling ends.
- *      @option {Function} onAfterFirst If queuing is activated, this function will be called after the first scrolling ends.
- * @return {jQuery} Returns the same jQuery object, for chaining.
- *
- * @desc Scroll to a fixed position
- * @example $('div').scrollTo( 340 );
- *
- * @desc Scroll relatively to the actual position
- * @example $('div').scrollTo( '+=340px', { axis:'y' } );
- *
- * @dec Scroll using a selector (relative to the scrolled element)
- * @example $('div').scrollTo( 'p.paragraph:eq(2)', 500, { easing:'swing', queue:true, axis:'xy' } );
- *
- * @ Scroll to a DOM element (same for jQuery object)
- * @example var second_child = document.getElementById('container').firstChild.nextSibling;
- *                     $('#container').scrollTo( second_child, { duration:500, axis:'x', onAfter:function(){
- *                             alert('scrolled!!');
- *                     }});
- *
- * @desc Scroll on both axes, to different values
- * @example $('div').scrollTo( { top: 300, left:'+=200' }, { axis:'xy', offset:-20 } );
- */
-;(function( $ ){
-
-       var $scrollTo = $.scrollTo = function( target, duration, settings ){
-               $(window).scrollTo( target, duration, settings );
-       };
-
-       $scrollTo.defaults = {
-               axis:'y',
-               duration:1
-       };
-
-       // Returns the element that needs to be animated to scroll the window.
-       // Kept for backwards compatibility (specially for localScroll & serialScroll)
-       $scrollTo.window = function( scope ){
-               return $(window).scrollable();
-       };
-
-       // Hack, hack, hack... stay away!
-       // Returns the real elements to scroll (supports window/iframes, documents and regular nodes)
-       $.fn.scrollable = function(){
-               return this.map(function(){
-                       // Just store it, we might need it
-                       var win = this.parentWindow || this.defaultView,
-                               // If it's a document, get its iframe or the window if it's THE document
-                               elem = this.nodeName == '#document' ? win.frameElement || win : this,
-                               // Get the corresponding document
-                               doc = elem.contentDocument || (elem.contentWindow || elem).document,
-                               isWin = elem.setInterval;
-
-                       return elem.nodeName == 'IFRAME' || isWin && $.browser.safari ? doc.body
-                               : isWin ? doc.documentElement
-                               : this;
-               });
-       };
-
-       $.fn.scrollTo = function( target, duration, settings ){
-               if( typeof duration == 'object' ){
-                       settings = duration;
-                       duration = 0;
-               }
-               if( typeof settings == 'function' )
-                       settings = { onAfter:settings };
-
-               settings = $.extend( {}, $scrollTo.defaults, settings );
-               // Speed is still recognized for backwards compatibility
-               duration = duration || settings.speed || settings.duration;
-               // Make sure the settings are given right
-               settings.queue = settings.queue && settings.axis.length > 1;
-
-               if( settings.queue )
-                       // Let's keep the overall duration
-                       duration /= 2;
-               settings.offset = both( settings.offset );
-               settings.over = both( settings.over );
-
-               return this.scrollable().each(function(){
-                       var elem = this,
-                               $elem = $(elem),
-                               targ = target, toff, attr = {},
-                               win = $elem.is('html,body');
-
-                       switch( typeof targ ){
-                               // A number will pass the regex
-                               case 'number':
-                               case 'string':
-                                       if( /^([+-]=)?\d+(px)?$/.test(targ) ){
-                                               targ = both( targ );
-                                               // We are done
-                                               break;
-                                       }
-                                       // Relative selector, no break!
-                                       targ = $(targ,this);
-                               case 'object':
-                                       // DOMElement / jQuery
-                                       if( targ.is || targ.style )
-                                               // Get the real position of the target
-                                               toff = (targ = $(targ)).offset();
-                       }
-                       $.each( settings.axis.split(''), function( i, axis ){
-                               var Pos = axis == 'x' ? 'Left' : 'Top',
-                                       pos = Pos.toLowerCase(),
-                                       key = 'scroll' + Pos,
-                                       old = elem[key],
-                                       Dim = axis == 'x' ? 'Width' : 'Height',
-                                       dim = Dim.toLowerCase();
-
-                               if( toff ){// jQuery / DOMElement
-                                       attr[key] = toff[pos] + ( win ? 0 : old - $elem.offset()[pos] );
-
-                                       // If it's a dom element, reduce the margin
-                                       if( settings.margin ){
-                                               attr[key] -= parseInt(targ.css('margin'+Pos)) || 0;
-                                               attr[key] -= parseInt(targ.css('border'+Pos+'Width')) || 0;
-                                       }
-
-                                       attr[key] += settings.offset[pos] || 0;
-
-                                       if( settings.over[pos] )
-                                               // Scroll to a fraction of its width/height
-                                               attr[key] += targ[dim]() * settings.over[pos];
-                               }else
-                                       attr[key] = targ[pos];
-
-                               // Number or 'number'
-                               if( /^\d+$/.test(attr[key]) )
-                                       // Check the limits
-                                       attr[key] = attr[key] <= 0 ? 0 : Math.min( attr[key], max(Dim) );
-
-                               // Queueing axes
-                               if( !i && settings.queue ){
-                                       // Don't waste time animating, if there's no need.
-                                       if( old != attr[key] )
-                                               // Intermediate animation
-                                               animate( settings.onAfterFirst );
-                                       // Don't animate this axis again in the next iteration.
-                                       delete attr[key];
-                               }
-                       });
-                       animate( settings.onAfter );
-
-                       function animate( callback ){
-                               $elem.animate( attr, duration, settings.easing, callback && function(){
-                                       callback.call(this, target, settings);
-                               });
-                       };
-                       function max( Dim ){
-                               var attr ='scroll'+Dim,
-                                       doc = elem.ownerDocument;
-
-                               return win
-                                               ? Math.max( doc.documentElement[attr], doc.body[attr]  )
-                                               : elem[attr];
-                       };
-               }).end();
-       };
-
-       function both( val ){
-               return typeof val == 'object' ? val : { top:val, left:val };
-       };
-
-})( jQuery );
\ No newline at end of file
diff --git a/redakcja/static/js/button_scripts.js b/redakcja/static/js/button_scripts.js
deleted file mode 100644 (file)
index 37f37a7..0000000
+++ /dev/null
@@ -1,367 +0,0 @@
-(function() {
-  var slice = Array.prototype.slice;
-
-  function update(array, args) {
-    var arrayLength = array.length, length = args.length;
-    while (length--) array[arrayLength + length] = args[length];
-    return array;
-  };
-
-  function merge(array, args) {
-    array = slice.call(array, 0);
-    return update(array, args);
-  };
-
-  Function.prototype.bind = function(context) {
-    if (arguments.length < 2 && typeof arguments[0] === 'undefined') {
-      return this;
-    }
-    var __method = this;
-    var args = slice.call(arguments, 1);
-    return function() {
-      var a = merge(args, arguments);
-      return __method.apply(context, a);
-    }
-  }
-
-})();
-
-function nblck_each(array, body, after) {
-       $.each(array, function(i) {
-               body(this, i);
-       });
-
-       after();
-};
-
-function nblck_map(array, func, after) {
-       var acc = [];
-
-       nblck_each(array, function(elem, index) {
-               acc.push(func(elem, index));
-       }, function(){
-               after(acc);
-       });
-};
-
-function ScriptletCenter()
-{
-    this.scriptlets = {};
-
-    this.scriptlets['insert_text'] = function(context, params, text, move_forward, move_up, done)
-    {
-        done(params.text, move_forward, move_up);
-    }.bind(this);
-
-    this.scriptlets['insert_tag'] = function(context, params, text, move_forward, move_up, done)
-    {
-        var padding_top = '';
-        for (var i=params.padding_top; i; i--)
-            padding_top += '\n';
-
-        var start_tag = '<'+params.tag;
-        var cursor_inside = false;
-
-        for (var attr in params.attrs) {
-            start_tag += ' '+attr+'="' + params.attrs[attr] + '"';
-        }
-
-        start_tag += '>';
-        var end_tag = '</'+params.tag+'>';
-
-        var padding_bottom = '';
-        for (var i=params.padding_bottom; i; i--)
-            padding_bottom += '\n';
-
-        if(text.length > 0) {
-            // tokenize
-            var output = '';
-            var token = '';
-            for(var index=0; index < text.length; index++)
-            {
-                if (text[index].match(/\s/)) { // whitespace
-                    token += text[index];
-                }
-                else { // character
-                    output += token;
-                    if(output == token) output += padding_top + start_tag;
-                    token = '';
-                    output += text[index];
-                }
-            }
-
-            if( output[output.length-1] == '\\' ) {
-                output = output.substr(0, output.length-1) + end_tag + padding_bottom + '\\';
-            } else {
-                output += end_tag + padding_bottom;
-            }
-            output += token;
-
-            // keep cursor inside tag if some previous scriptlet has already moved it
-            cursor_inside = move_forward != 0 || move_up != 0;
-        }
-        else {
-            if(params.nocontent) {
-                output = padding_top + "<"+params.tag +" />" + padding_bottom;
-            }
-            else {
-                output = padding_top + start_tag + end_tag + padding_bottom;
-                cursor_inside = true;
-            }
-        }
-
-        if (cursor_inside) {
-            move_forward -= params.tag.length + 3;
-            move_up += params.padding_bottom || 0;
-        }
-
-        done(output, move_forward, move_up);
-    }.bind(this);
-
-    this.scriptlets['lineregexp'] = function(context, params, text, move_forward, move_up, done) {
-               var self = this;
-
-        var exprs = $.map(params.exprs, function(expr) {
-            var opts = "g";
-            if(expr.length > 2) {
-                opts = expr[2];
-            } return {
-                rx: new RegExp(expr[0], opts),
-                repl: expr[1]
-                };
-        });
-
-        if(!text) done(text, move_forward, move_up);
-
-        var changed = 0;
-        var lines = text.split('\n');
-
-               nblck_map(lines, function(line, index) {
-            var old_line = line;
-            $(exprs).each(function() {
-                var expr = this;
-                line = line.replace(expr.rx, expr.repl);
-            });
-
-                       $progress.html(index);
-
-            if(old_line != line) changed += 1;
-            return line;
-        }, function(newlines) {
-            if(changed > 0) {
-                text = newlines.join('\n');
-            };
-
-            done(text, move_forward, move_up);
-               });
-    }.bind(this);
-
-    this.scriptlets['fulltextregexp'] = function(context, params, text, move_forward, move_up, done) {
-               var self = this;
-
-        var exprs = $.map(params.exprs, function(expr) {
-            var opts = "mg";
-            if(expr.length > 2) {
-                opts = expr[2];
-            }
-            return {
-                rx: new RegExp(expr[0], opts),
-                repl: expr[1]
-                };
-        });
-
-        if(!text) done(text, move_forward, move_up);
-
-               nblck_each(exprs, function(expr, index) {
-                       $progress.html(600 + index);
-            text = text.replace(expr.rx, expr.repl);
-        }, function() {
-                       done(text, move_forward, move_up);
-               });
-    }.bind(this);
-
-    this.scriptlets['macro'] = function(context, params, text, move_forward, move_up, done) {
-        var self = this;
-               var i = 0;
-
-               function next(text, move_forward, move_up) {
-               if (i < params.length) {
-                               var e = params[i];
-                               i = i + 1;
-                               self.scriptlets[e[0]](context, e[1], text, move_forward, move_up, next);
-                       }
-                       else {
-                               done(text, move_forward, move_up);
-                       }
-        };
-
-               next(text, move_forward, move_up);
-    }.bind(this);
-
-    this.scriptlets['lowercase'] = function(context, params, text, move_forward, move_up, done)
-    {
-        if(!text) done(text, move_forward, move_up);
-        done(text.toLowerCase(), move_forward, move_up);
-    }.bind(this);
-
-
-    this.scriptlets["insert_stanza"] = function(context, params, text, move_forward, move_up, done) {
-        var padding_top = '';
-        for (var i=params.padding_top; i; i--)
-            padding_top += '\n';
-
-        var padding_bottom = '';
-        for (var i=params.padding_bottom; i; i--)
-            padding_bottom += '\n';
-
-        if(text) {
-            var verses = text.split('\n');
-            text = ''; var buf = ''; var ebuf = '';
-            var first = true;
-
-            for(var i=0;  i < verses.length; i++) {
-                var verse = verses[i].replace(/^\s+/, "").replace(/\s+$/, "");
-                if(verse) {
-                    text += (buf ? buf + '/\n' : '') + ebuf;
-                    buf = (first ? '<strofa>\n' : '') + verses[i];
-                    ebuf = '';
-                    first = false;
-                } else {
-                    ebuf += '\n' + verses[i];
-                }
-            }
-            text = padding_top + text + buf + '\n</strofa>' + padding_bottom + ebuf;
-        }
-        else {
-            text = padding_top + "<strofa></strofa>" + padding_bottom;
-            move_forward -= "</strofa>".length;
-            move_up += params.padding_bottom || 0;
-        }
-
-        done(text, move_forward, move_up);
-    }.bind(this);
-
-
-    this.scriptlets['autotag'] = function(context, params, text, move_forward, move_up, done)
-    {
-        if(!text.match(/^\n+$/)) done(text, move_forward, move_up);
-
-        var output = '';
-
-        function insert_done(text, mf, mu) {
-            output += text;
-        }
-
-        if (!params.split) params.split = 2;
-        if (!params.padding) params.padding = 3;
-
-        if (params.tag == 'strofa')
-            tagger = this.scriptlets['insert_stanza'];
-        else
-            tagger = this.scriptlets['insert_tag'];
-
-        var padding_top = text.match(/^\n+/)
-        output = padding_top ? padding_top[0] : '';
-
-        padding = '';
-        for(var i=params.padding; i; --i) {
-            padding += "\n";
-        }
-
-        text = text.substr(output.length);
-        var chunk_reg = new RegExp("^([\\s\\S]+?)(\\n{"+params.split+",}|$)");
-        while (match = text.match(chunk_reg)) {
-            if (params.tag == 'akap' && match[1].match(/^---/))
-                tag = 'akap_dialog';
-            else tag = params.tag;
-            tagger(context, {tag: tag}, match[1], 0, 0, insert_done);
-            if (match[2].length > params.padding)
-                output += match[2];
-            else
-                output += padding;
-            text = text.substr(match[0].length)
-        }
-
-        output += text;
-
-        done(output, move_forward, move_up);
-    }.bind(this);
-
-
-    this.scriptlets['slugify'] = function(context, params, text, move_forward, move_up, done)
-    {
-        done(slugify(text.replace(/_/g, '-')), move_forward, move_up);
-    }.bind(this);
-
-}
-
-ScriptletCenter.prototype.callInteractive = function(opts) {
-       $progress = $('<span>Executing script</span>');
-       var self = this;
-
-       /* This won't work, 'cause the JS below might be synchronous :( */
-       /* var timer = setTimeout(function() {
-           $.blockUI({message: $progress});
-           timer = null;
-       }, 1000); */
-
-       $.blockUI({message: $progress, showOverlay: false});
-
-    $('#save-button').attr('disabled', true);
-    var input = self.XMLEditorSelectedText(opts.context);
-    window.setTimeout(function() {
-        self.scriptlets[opts.action](opts.context, opts.extra, input, 0, 0, function(output, move_forward, move_up){
-            /*if(timer)
-                clearTimeout(timer);
-            else */
-            if (input != output) {
-                self.XMLEditorReplaceSelectedText(opts.context, output)
-            }
-            if (move_forward || move_up) {
-                try {
-                    self.XMLEditorMoveCursorForward(opts.context, move_forward, move_up)
-                }
-                catch(e) {}
-            }
-            $.unblockUI({onUnblock: function() { $('#save-button').attr('disabled', null)}}); // done
-        });
-    }, 0);
-
-}
-
-ScriptletCenter.prototype.XMLEditorSelectedText = function(editor) {
-
-    return editor.selection();
-};
-
-ScriptletCenter.prototype.XMLEditorReplaceSelectedText = function(editor, replacement)
-{
-       $progress.html("Replacing text");
-       editor.replaceSelection(replacement);
-};
-
-ScriptletCenter.prototype.XMLEditorMoveCursorForward = function(panel, right, up) {
-    var pos = panel.cursorPosition();
-    if (up) {
-        line = pos.line;
-        while (up < 0) {
-            line = panel.nextLine(line);
-            ++up;
-        }
-        while (up > 0) {
-            line = panel.prevLine(line);
-            --up;
-        }
-        len = panel.lineContent(line).length;
-        panel.selectLines(line, len + right);
-    }
-    else {
-        panel.selectLines(pos.line, pos.character + right);
-    }
-};
-
-var scriptletCenter;
-
-$(function() {
-    scriptletCenter = new ScriptletCenter();
-});
diff --git a/redakcja/static/js/catalogue/book_list.js b/redakcja/static/js/catalogue/book_list.js
deleted file mode 100644 (file)
index 9d2511d..0000000
+++ /dev/null
@@ -1,96 +0,0 @@
-(function($) {
-    $(function() {
-
-    // clicking on book checks chunks, too
-    $("input[name=select_book]").change(function(ev) {
-        $book = $(this);
-        $book.closest("table").find("input[name=select_chunk][data-book-id=" + $book.val() + "]").attr("checked", $book.is(':checked'));
-    });
-
-    // initialize context menu
-
-   var get_ids = function() {
-       return $.map($("input[name=select_chunk]:checked"), function(ele, idx) {
-           return ele.value;
-           }).concat(
-               $.map($("input[name=select_book][data-chunk-id!=]:checked"), function(ele, idx) {
-                   return $(ele).attr("data-chunk-id");
-                   })).join();
-   };
-
-    var get_callback = function(form_field_name) {
-        var $form = $("#chunk_mass_edit");
-        var $field = $("[name=" + form_field_name + "]", $form);
-        var $ids_field = $("[name=ids]").val(get_ids());
-        var usable_callback = function(value) {
-            $field.val(value);
-            $ids_field.val(get_ids());
-            $.post($form.attr("action"),
-               $form.serialize(),
-               function(data, status) {
-                   location.reload(true);
-               }
-            );
-            return true;
-        };
-        return usable_callback;
-    };
-
-    var get_items = function(field, callback) {
-        var d = {};
-        $.each($("select[name="+field+"] option[value!=]"),
-            function(idx, ele) {
-                d[field + "_" + idx] = {
-                    name: $(ele).text(), 
-                    callback: function() {callback($(ele).attr('value'));}
-                };
-            });
-        return d;
-    };
-
-    var user_callback = get_callback('user');
-    var users = [
-        get_items("user", user_callback),
-        {sep: '----'},
-        get_items("other-user", user_callback)
-    ];
-    var current_user_items = user_items = {};
-    var i = 0;
-    var more_label = $("label[for=mass_edit_more_users]").text();
-    for (user_table in users) {
-        for (user in users[user_table]) {
-            if (i && i % 20 == 0) {
-                var more_items = {};
-                current_user_items['more'] = {
-                    name: more_label,
-                    items: more_items
-                };
-                current_user_items = more_items;
-            }
-            current_user_items[user] = users[user_table][user];
-            i += 1;
-        }
-    }
-    $.contextMenu({
-        selector: '#file-list',
-        items: {
-            stage: { 
-                name: $("label[for=mass_edit_stage]").text(),
-                items: get_items("stage", get_callback('stage')),
-                icon: "clock",
-            },
-            user: { 
-                name: $("label[for=mass_edit_user]").text(),
-                items: user_items,
-                icon: "user",
-            },
-            project: {
-                name: $("label[for=mass_edit_project]").text(),
-                items: get_items("project", get_callback('project')),
-            },
-        },
-    });
-
-
-    });
-})(jQuery);
diff --git a/redakcja/static/js/catalogue/catalogue.js b/redakcja/static/js/catalogue/catalogue.js
deleted file mode 100755 (executable)
index 9d2bd95..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-(function($) {
-    $(function() {
-
-
-        $('.filter').change(function() {
-            document.filter[this.name].value = this.value;
-            document.filter.submit();
-        });
-
-        $('.check-filter').change(function() {
-            document.filter[this.name].value = this.checked ? '1' : '';
-            document.filter.submit();
-        });
-
-        $('.text-filter').each(function() {
-            var inp = this;
-            $(inp).parent().submit(function() {
-                document.filter[inp.name].value = inp.value;
-                document.filter.submit();
-                return false;
-            });
-        });
-
-
-        $('.autoslug-source').change(function() {
-            $('.autoslug').attr('value', slugify(this.value));
-        });
-
-
-    });
-})(jQuery);
-
diff --git a/redakcja/static/js/lib/codemirror-0.8/codemirror.js b/redakcja/static/js/lib/codemirror-0.8/codemirror.js
deleted file mode 100644 (file)
index 57e44be..0000000
+++ /dev/null
@@ -1,538 +0,0 @@
-/* CodeMirror main module
- *
- * Implements the CodeMirror constructor and prototype, which take care
- * of initializing the editor frame, and providing the outside interface.
- */
-
-// The CodeMirrorConfig object is used to specify a default
-// configuration. If you specify such an object before loading this
-// file, the values you put into it will override the defaults given
-// below. You can also assign to it after loading.
-var CodeMirrorConfig = window.CodeMirrorConfig || {};
-
-var CodeMirror = (function(){
-  function setDefaults(object, defaults) {
-    for (var option in defaults) {
-      if (!object.hasOwnProperty(option))
-        object[option] = defaults[option];
-    }
-  }
-  function forEach(array, action) {
-    for (var i = 0; i < array.length; i++)
-      action(array[i]);
-  }
-
-  // These default options can be overridden by passing a set of
-  // options to a specific CodeMirror constructor. See manual.html for
-  // their meaning.
-  setDefaults(CodeMirrorConfig, {
-    stylesheet: [],
-    path: "",
-    parserfile: [],
-    basefiles: ["util.js", "stringstream.js", "select.js", "undo.js", "editor.js", "tokenize.js"],
-    iframeClass: null,
-    passDelay: 200,
-    passTime: 50,
-    lineNumberDelay: 200,
-    lineNumberTime: 50,
-    continuousScanning: false,
-    saveFunction: null,
-    onChange: null,
-    undoDepth: 50,
-    undoDelay: 800,
-    disableSpellcheck: true,
-    textWrapping: true,
-    readOnly: false,
-    width: "",
-    height: "300px",
-    minHeight: 100,
-    autoMatchParens: false,
-    parserConfig: null,
-    tabMode: "indent", // or "spaces", "default", "shift"
-    reindentOnLoad: false,
-    activeTokens: null,
-    cursorActivity: null,
-    lineNumbers: false,
-    indentUnit: 2,
-    domain: null
-  });
-
-  function addLineNumberDiv(container) {
-    var nums = document.createElement("DIV"),
-        scroller = document.createElement("DIV");
-    nums.style.position = "absolute";
-    nums.style.height = "100%";
-    if (nums.style.setExpression) {
-      try {nums.style.setExpression("height", "this.previousSibling.offsetHeight + 'px'");}
-      catch(e) {} // Seems to throw 'Not Implemented' on some IE8 versions
-    }
-    nums.style.top = "0px";
-    nums.style.left = "0px";
-    nums.style.overflow = "hidden";
-    container.appendChild(nums);
-    scroller.className = "CodeMirror-line-numbers";
-    nums.appendChild(scroller);
-    scroller.innerHTML = "<div>1</div>";
-    return nums;
-  }
-
-  function frameHTML(options) {
-    if (typeof options.parserfile == "string")
-      options.parserfile = [options.parserfile];
-    if (typeof options.stylesheet == "string")
-      options.stylesheet = [options.stylesheet];
-
-    var html = ["<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\"><html><head>"];
-    // Hack to work around a bunch of IE8-specific problems.
-    html.push("<meta http-equiv=\"X-UA-Compatible\" content=\"IE=EmulateIE7\"/>");
-    forEach(options.stylesheet, function(file) {
-      html.push("<link rel=\"stylesheet\" type=\"text/css\" href=\"" + file + "\"/>");
-    });
-    forEach(options.basefiles.concat(options.parserfile), function(file) {
-      if (!/^https?:/.test(file)) file = options.path + file;
-      html.push("<script type=\"text/javascript\" src=\"" + file + "\"><" + "/script>");
-    });
-    html.push("</head><body style=\"border-width: 0;\" class=\"editbox\" spellcheck=\"" +
-              (options.disableSpellcheck ? "false" : "true") + "\"></body></html>");
-    return html.join("");
-  }
-
-  var internetExplorer = document.selection && window.ActiveXObject && /MSIE/.test(navigator.userAgent);
-
-  function CodeMirror(place, options) {
-    // Use passed options, if any, to override defaults.
-    this.options = options = options || {};
-    setDefaults(options, CodeMirrorConfig);
-
-    // Backward compatibility for deprecated options.
-    if (options.dumbTabs) options.tabMode = "spaces";
-    else if (options.normalTab) options.tabMode = "default";
-
-    var frame = this.frame = document.createElement("IFRAME");
-    if (options.iframeClass) frame.className = options.iframeClass;
-    frame.frameBorder = 0;
-    frame.style.border = "0";
-    frame.style.width = '100%';
-    frame.style.height = '100%';
-    // display: block occasionally suppresses some Firefox bugs, so we
-    // always add it, redundant as it sounds.
-    frame.style.display = "block";
-
-    var div = this.wrapping = document.createElement("DIV");
-    div.style.position = "relative";
-    div.className = "CodeMirror-wrapping";
-    div.style.width = options.width;
-    div.style.height = (options.height == "dynamic") ? options.minHeight + "px" : options.height;
-    // This is used by Editor.reroutePasteEvent
-    var teHack = this.textareaHack = document.createElement("TEXTAREA");
-    div.appendChild(teHack);
-    teHack.style.position = "absolute";
-    teHack.style.left = "-10000px";
-    teHack.style.width = "10px";
-
-    // Link back to this object, so that the editor can fetch options
-    // and add a reference to itself.
-    frame.CodeMirror = this;
-    if (options.domain && internetExplorer) {
-      this.html = frameHTML(options);
-      frame.src = "javascript:(function(){document.open();" +
-        (options.domain ? "document.domain=\"" + options.domain + "\";" : "") +
-        "document.write(window.frameElement.CodeMirror.html);document.close();})()";
-    }
-    else {
-      frame.src = "javascript:false";
-    }
-
-    if (place.appendChild) place.appendChild(div);
-    else place(div);
-    div.appendChild(frame);
-    if (options.lineNumbers) this.lineNumbers = addLineNumberDiv(div);
-
-    this.win = frame.contentWindow;
-    if (!options.domain || !internetExplorer) {
-      this.win.document.open();
-      this.win.document.write(frameHTML(options));
-      this.win.document.close();
-    }
-  }
-
-  CodeMirror.prototype = {
-    init: function() {
-      if (this.options.initCallback) this.options.initCallback(this);
-      if (this.options.lineNumbers) this.activateLineNumbers();
-      if (this.options.reindentOnLoad) this.reindent();
-      if (this.options.height == "dynamic") this.setDynamicHeight();
-    },
-
-    getCode: function() {return this.editor.getCode();},
-    setCode: function(code) {this.editor.importCode(code);},
-    selection: function() {this.focusIfIE(); return this.editor.selectedText();},
-    reindent: function() {this.editor.reindent();},
-    reindentSelection: function() {this.focusIfIE(); this.editor.reindentSelection(null);},
-
-    focusIfIE: function() {
-      // in IE, a lot of selection-related functionality only works when the frame is focused
-      if (this.win.select.ie_selection) this.focus();
-    },
-    focus: function() {
-      this.win.focus();
-      if (this.editor.selectionSnapshot) // IE hack
-        this.win.select.setBookmark(this.win.document.body, this.editor.selectionSnapshot);
-    },
-    replaceSelection: function(text) {
-      this.focus();
-      this.editor.replaceSelection(text);
-      return true;
-    },
-    replaceChars: function(text, start, end) {
-      this.editor.replaceChars(text, start, end);
-    },
-    getSearchCursor: function(string, fromCursor, caseFold) {
-      return this.editor.getSearchCursor(string, fromCursor, caseFold);
-    },
-
-    undo: function() {this.editor.history.undo();},
-    redo: function() {this.editor.history.redo();},
-    historySize: function() {return this.editor.history.historySize();},
-    clearHistory: function() {this.editor.history.clear();},
-
-    grabKeys: function(callback, filter) {this.editor.grabKeys(callback, filter);},
-    ungrabKeys: function() {this.editor.ungrabKeys();},
-
-    setParser: function(name, parserConfig) {this.editor.setParser(name, parserConfig);},
-    setSpellcheck: function(on) {this.win.document.body.spellcheck = on;},
-    setStylesheet: function(names) {
-      if (typeof names === "string") names = [names];
-      var activeStylesheets = {};
-      var matchedNames = {};
-      var links = this.win.document.getElementsByTagName("link");
-      // Create hashes of active stylesheets and matched names.
-      // This is O(n^2) but n is expected to be very small.
-      for (var x = 0, link; link = links[x]; x++) {
-        if (link.rel.indexOf("stylesheet") !== -1) {
-          for (var y = 0; y < names.length; y++) {
-            var name = names[y];
-            if (link.href.substring(link.href.length - name.length) === name) {
-              activeStylesheets[link.href] = true;
-              matchedNames[name] = true;
-            }
-          }
-        }
-      }
-      // Activate the selected stylesheets and disable the rest.
-      for (var x = 0, link; link = links[x]; x++) {
-        if (link.rel.indexOf("stylesheet") !== -1) {
-          link.disabled = !(link.href in activeStylesheets);
-        }
-      }
-      // Create any new stylesheets.
-      for (var y = 0; y < names.length; y++) {
-        var name = names[y];
-        if (!(name in matchedNames)) {
-          var link = this.win.document.createElement("link");
-          link.rel = "stylesheet";
-          link.type = "text/css";
-          link.href = name;
-          this.win.document.getElementsByTagName('head')[0].appendChild(link);
-        }
-      }
-    },
-    setTextWrapping: function(on) {
-      if (on == this.options.textWrapping) return;
-      this.win.document.body.style.whiteSpace = on ? "" : "nowrap";
-      this.options.textWrapping = on;
-      if (this.lineNumbers) {
-        this.setLineNumbers(false);
-        this.setLineNumbers(true);
-      }
-    },
-    setIndentUnit: function(unit) {this.win.indentUnit = unit;},
-    setUndoDepth: function(depth) {this.editor.history.maxDepth = depth;},
-    setTabMode: function(mode) {this.options.tabMode = mode;},
-    setLineNumbers: function(on) {
-      if (on && !this.lineNumbers) {
-        this.lineNumbers = addLineNumberDiv(this.wrapping);
-        this.activateLineNumbers();
-      }
-      else if (!on && this.lineNumbers) {
-        this.wrapping.removeChild(this.lineNumbers);
-        this.wrapping.style.marginLeft = "";
-        this.lineNumbers = null;
-      }
-    },
-
-    cursorPosition: function(start) {this.focusIfIE(); return this.editor.cursorPosition(start);},
-    firstLine: function() {return this.editor.firstLine();},
-    lastLine: function() {return this.editor.lastLine();},
-    nextLine: function(line) {return this.editor.nextLine(line);},
-    prevLine: function(line) {return this.editor.prevLine(line);},
-    lineContent: function(line) {return this.editor.lineContent(line);},
-    setLineContent: function(line, content) {this.editor.setLineContent(line, content);},
-    removeLine: function(line){this.editor.removeLine(line);},
-    insertIntoLine: function(line, position, content) {this.editor.insertIntoLine(line, position, content);},
-    selectLines: function(startLine, startOffset, endLine, endOffset) {
-      this.win.focus();
-      this.editor.selectLines(startLine, startOffset, endLine, endOffset);
-    },
-    nthLine: function(n) {
-      var line = this.firstLine();
-      for (; n > 1 && line !== false; n--)
-        line = this.nextLine(line);
-      return line;
-    },
-    lineNumber: function(line) {
-      var num = 0;
-      while (line !== false) {
-        num++;
-        line = this.prevLine(line);
-      }
-      return num;
-    },
-    jumpToLine: function(line) {
-      if (typeof line == "number") line = this.nthLine(line);
-      this.selectLines(line, 0);
-      this.win.focus();
-    },
-    currentLine: function() { // Deprecated, but still there for backward compatibility
-      return this.lineNumber(this.cursorLine());
-    },
-    cursorLine: function() {
-      return this.cursorPosition().line;
-    },
-    cursorCoords: function(start) {return this.editor.cursorCoords(start);},
-
-    activateLineNumbers: function() {
-      var frame = this.frame, win = frame.contentWindow, doc = win.document, body = doc.body,
-          nums = this.lineNumbers, scroller = nums.firstChild, self = this;
-      var barWidth = null;
-
-      function sizeBar() {
-        if (frame.offsetWidth == 0) return;
-        for (var root = frame; root.parentNode; root = root.parentNode);
-        if (!nums.parentNode || root != document || !win.Editor) {
-          // Clear event handlers (their nodes might already be collected, so try/catch)
-          try{clear();}catch(e){}
-          clearInterval(sizeInterval);
-          return;
-        }
-
-        /*if (nums.offsetWidth != barWidth) {
-          barWidth = nums.offsetWidth;
-          frame.parentNode.style.paddingLeft = barWidth + "px";
-        }*/
-      }
-      function doScroll() {
-        nums.scrollTop = body.scrollTop || doc.documentElement.scrollTop || 0;
-      }
-      // Cleanup function, registered by nonWrapping and wrapping.
-      var clear = function(){};
-      sizeBar();
-      var sizeInterval = setInterval(sizeBar, 500);
-
-      function ensureEnoughLineNumbers(fill) {
-        var lineHeight = scroller.firstChild.offsetHeight;
-        if (lineHeight == 0) return;
-        var targetHeight = 50 + Math.max(body.offsetHeight, Math.max(frame.offsetHeight, body.scrollHeight || 0)),
-            lastNumber = Math.ceil(targetHeight / lineHeight);
-        for (var i = scroller.childNodes.length; i <= lastNumber; i++) {
-          var div = document.createElement("DIV");
-          div.appendChild(document.createTextNode(fill ? String(i + 1) : "\u00a0"));
-          scroller.appendChild(div);
-        }
-      }
-
-      function nonWrapping() {
-        function update() {
-          ensureEnoughLineNumbers(true);
-          doScroll();
-        }
-        self.updateNumbers = update;
-        var onScroll = win.addEventHandler(win, "scroll", doScroll, true),
-            onResize = win.addEventHandler(win, "resize", update, true);
-        clear = function(){
-          onScroll(); onResize();
-          if (self.updateNumbers == update) self.updateNumbers = null;
-        };
-        update();
-      }
-
-      function wrapping() {
-        var node, lineNum, next, pos, changes = [], styleNums = self.options.styleNumbers;
-
-        function setNum(n, node) {
-          // Does not typically happen (but can, if you mess with the
-          // document during the numbering)
-          if (!lineNum) lineNum = scroller.appendChild(document.createElement("DIV"));
-          if (styleNums) styleNums(lineNum, node, n);
-          // Changes are accumulated, so that the document layout
-          // doesn't have to be recomputed during the pass
-          changes.push(lineNum); changes.push(n);
-          pos = lineNum.offsetHeight + lineNum.offsetTop;
-          lineNum = lineNum.nextSibling;
-        }
-        function commitChanges() {
-          for (var i = 0; i < changes.length; i += 2)
-            changes[i].innerHTML = changes[i + 1];
-          changes = [];
-        }
-        function work() {
-          if (!scroller.parentNode || scroller.parentNode != self.lineNumbers) return;
-
-          var endTime = new Date().getTime() + self.options.lineNumberTime;
-          while (node) {
-            setNum(next++, node.previousSibling);
-            for (; node && !win.isBR(node); node = node.nextSibling) {
-              var bott = node.offsetTop + node.offsetHeight;
-              while (scroller.offsetHeight && bott - 3 > pos) setNum("&nbsp;");
-            }
-            if (node) node = node.nextSibling;
-            if (new Date().getTime() > endTime) {
-              commitChanges();
-              pending = setTimeout(work, self.options.lineNumberDelay);
-              return;
-            }
-          }
-          while (lineNum) setNum(next++);
-          commitChanges();
-          doScroll();
-        }
-        function start(firstTime) {
-          doScroll();
-          ensureEnoughLineNumbers(firstTime);
-          node = body.firstChild;
-          lineNum = scroller.firstChild;
-          pos = 0;
-          next = 1;
-          work();
-        }
-
-        start(true);
-        var pending = null;
-        function update() {
-          if (pending) clearTimeout(pending);
-          if (self.editor.allClean()) start();
-          else pending = setTimeout(update, 200);
-        }
-        self.updateNumbers = update;
-        var onScroll = win.addEventHandler(win, "scroll", doScroll, true),
-            onResize = win.addEventHandler(win, "resize", update, true);
-        clear = function(){
-          if (pending) clearTimeout(pending);
-          if (self.updateNumbers == update) self.updateNumbers = null;
-          onScroll();
-          onResize();
-        };
-      }
-      (this.options.textWrapping || this.options.styleNumbers ? wrapping : nonWrapping)();
-    },
-
-    setDynamicHeight: function() {
-      var self = this, activity = self.options.cursorActivity, win = self.win, body = win.document.body,
-          lineHeight = null, timeout = null, vmargin = 2 * self.frame.offsetTop;
-      body.style.overflowY = "hidden";
-      win.document.documentElement.style.overflowY = "hidden";
-      this.frame.scrolling = "no";
-
-      function updateHeight() {
-        for (var span = body.firstChild, sawBR = false; span; span = span.nextSibling)
-          if (win.isSpan(span) && span.offsetHeight) {
-            lineHeight = span.offsetHeight;
-            if (!sawBR) vmargin = 2 * (self.frame.offsetTop + span.offsetTop + body.offsetTop + (internetExplorer ? 10 : 0));
-            break;
-          }
-        if (lineHeight)
-          self.wrapping.style.height = Math.max(vmargin + lineHeight * (body.getElementsByTagName("BR").length + 1),
-                                                self.options.minHeight) + "px";
-      }
-      setTimeout(updateHeight, 100);
-      self.options.cursorActivity = function(x) {
-        if (activity) activity(x);
-        clearTimeout(timeout);
-        timeout = setTimeout(updateHeight, 200);
-      };
-    }
-  };
-
-  CodeMirror.InvalidLineHandle = {toString: function(){return "CodeMirror.InvalidLineHandle";}};
-
-  CodeMirror.replace = function(element) {
-    if (typeof element == "string")
-      element = document.getElementById(element);
-    return function(newElement) {
-      element.parentNode.replaceChild(newElement, element);
-    };
-  };
-
-  CodeMirror.fromTextArea = function(area, options) {
-    if (typeof area == "string")
-      area = document.getElementById(area);
-
-    options = options || {};
-    if (area.style.width && options.width == null)
-      options.width = area.style.width;
-    if (area.style.height && options.height == null)
-      options.height = area.style.height;
-    if (options.content == null) options.content = area.value;
-
-    if (area.form) {
-      function updateField() {
-        area.value = mirror.getCode();
-      }
-      if (typeof area.form.addEventListener == "function")
-        area.form.addEventListener("submit", updateField, false);
-      else
-        area.form.attachEvent("onsubmit", updateField);
-      var realSubmit = area.form.submit;
-      function wrapSubmit() {
-        updateField();
-        // Can't use realSubmit.apply because IE6 is too stupid
-        area.form.submit = realSubmit;
-        area.form.submit();
-        area.form.submit = wrapSubmit;
-      }
-      area.form.submit = wrapSubmit;
-    }
-
-    function insert(frame) {
-      if (area.nextSibling)
-        area.parentNode.insertBefore(frame, area.nextSibling);
-      else
-        area.parentNode.appendChild(frame);
-    }
-
-    area.style.display = "none";
-    var mirror = new CodeMirror(insert, options);
-    mirror.toTextArea = function() {
-      area.parentNode.removeChild(mirror.wrapping);
-      area.style.display = "";
-      if (area.form) {
-        area.form.submit = realSubmit;
-        if (typeof area.form.removeEventListener == "function")
-          area.form.removeEventListener("submit", updateField, false);
-        else
-          area.form.detachEvent("onsubmit", updateField);
-      }
-    };
-
-    return mirror;
-  };
-
-  CodeMirror.isProbablySupported = function() {
-    // This is rather awful, but can be useful.
-    var match;
-    if (window.opera)
-      return Number(window.opera.version()) >= 9.52;
-    else if (/Apple Computers, Inc/.test(navigator.vendor) && (match = navigator.userAgent.match(/Version\/(\d+(?:\.\d+)?)\./)))
-      return Number(match[1]) >= 3;
-    else if (document.selection && window.ActiveXObject && (match = navigator.userAgent.match(/MSIE (\d+(?:\.\d*)?)\b/)))
-      return Number(match[1]) >= 6;
-    else if (match = navigator.userAgent.match(/gecko\/(\d{8})/i))
-      return Number(match[1]) >= 20050901;
-    else if (match = navigator.userAgent.match(/AppleWebKit\/(\d+)/))
-      return Number(match[1]) >= 525;
-    else
-      return null;
-  };
-
-  return CodeMirror;
-})();
diff --git a/redakcja/static/js/lib/codemirror-0.8/editor.js b/redakcja/static/js/lib/codemirror-0.8/editor.js
deleted file mode 100644 (file)
index 07410d2..0000000
+++ /dev/null
@@ -1,1514 +0,0 @@
-/* The Editor object manages the content of the editable frame. It
- * catches events, colours nodes, and indents lines. This file also
- * holds some functions for transforming arbitrary DOM structures into
- * plain sequences of <span> and <br> elements
- */
-
-var internetExplorer = document.selection && window.ActiveXObject && /MSIE/.test(navigator.userAgent);
-var webkit = /AppleWebKit/.test(navigator.userAgent);
-var safari = /Apple Computers, Inc/.test(navigator.vendor);
-var gecko = /gecko\/(\d{8})/i.test(navigator.userAgent);
-// TODO this is related to the backspace-at-end-of-line bug. Remove
-// this if Opera gets their act together, make the version check more
-// broad if they don't.
-var brokenOpera = window.opera && /Version\/10.[56]/.test(navigator.userAgent);
-
-// Make sure a string does not contain two consecutive 'collapseable'
-// whitespace characters.
-function makeWhiteSpace(n) {
-  var buffer = [], nb = true;
-  for (; n > 0; n--) {
-    buffer.push((nb || n == 1) ? nbsp : " ");
-    nb ^= true;
-  }
-  return buffer.join("");
-}
-
-// Create a set of white-space characters that will not be collapsed
-// by the browser, but will not break text-wrapping either.
-function fixSpaces(string) {
-  if (string.charAt(0) == " ") string = nbsp + string.slice(1);
-  return string.replace(/\t/g, function() {return makeWhiteSpace(indentUnit);})
-    .replace(/[ \u00a0]{2,}/g, function(s) {return makeWhiteSpace(s.length);});
-}
-
-function cleanText(text) {
-  return text.replace(/\u00a0/g, " ");
-}
-
-// Create a SPAN node with the expected properties for document part
-// spans.
-function makePartSpan(value, doc) {
-  var text = value;
-  if (value.nodeType == 3) text = value.nodeValue;
-  else value = doc.createTextNode(text);
-
-  var span = doc.createElement("SPAN");
-  span.isPart = true;
-  span.appendChild(value);
-  span.currentText = text;
-  return span;
-}
-
-var Editor = (function(){
-  // The HTML elements whose content should be suffixed by a newline
-  // when converting them to flat text.
-  var newlineElements = {"P": true, "DIV": true, "LI": true};
-
-  function asEditorLines(string) {
-    var tab = makeWhiteSpace(indentUnit);
-    return map(string.replace(/\t/g, tab).replace(/\u00a0/g, " ").replace(/\r\n?/g, "\n").split("\n"), fixSpaces);
-  }
-
-  // Helper function for traverseDOM. Flattens an arbitrary DOM node
-  // into an array of textnodes and <br> tags.
-  function simplifyDOM(root, atEnd) {
-    var doc = root.ownerDocument;
-    var result = [];
-    var leaving = true;
-
-    function simplifyNode(node, top) {
-      if (node.nodeType == 3) {
-        var text = node.nodeValue = fixSpaces(node.nodeValue.replace(/\r/g, "").replace(/\n/g, " "));
-        if (text.length) leaving = false;
-        result.push(node);
-      }
-      else if (isBR(node) && node.childNodes.length == 0) {
-        leaving = true;
-        result.push(node);
-      }
-      else {
-        for (var n = node.firstChild; n; n = n.nextSibling) simplifyNode(n);
-        if (!leaving && newlineElements.hasOwnProperty(node.nodeName.toUpperCase())) {
-          leaving = true;
-          if (!atEnd || !top)
-            result.push(doc.createElement("BR"));
-        }
-      }
-    }
-
-    simplifyNode(root, true);
-    return result;
-  }
-
-  // Creates a MochiKit-style iterator that goes over a series of DOM
-  // nodes. The values it yields are strings, the textual content of
-  // the nodes. It makes sure that all nodes up to and including the
-  // one whose text is being yielded have been 'normalized' to be just
-  // <span> and <br> elements.
-  function traverseDOM(start){
-    var owner = start.ownerDocument;
-    var nodeQueue = [];
-
-    // Create a function that can be used to insert nodes after the
-    // one given as argument.
-    function pointAt(node){
-      var parent = node.parentNode;
-      var next = node.nextSibling;
-      return function(newnode) {
-        parent.insertBefore(newnode, next);
-      };
-    }
-    var point = null;
-
-    // This an Opera-specific hack -- always insert an empty span
-    // between two BRs, because Opera's cursor code gets terribly
-    // confused when the cursor is between two BRs.
-    var afterBR = true;
-
-    // Insert a normalized node at the current point. If it is a text
-    // node, wrap it in a <span>, and give that span a currentText
-    // property -- this is used to cache the nodeValue, because
-    // directly accessing nodeValue is horribly slow on some browsers.
-    // The dirty property is used by the highlighter to determine
-    // which parts of the document have to be re-highlighted.
-    function insertPart(part){
-      var text = "\n";
-      if (part.nodeType == 3) {
-        select.snapshotChanged();
-        part = makePartSpan(part, owner);
-        text = part.currentText;
-        afterBR = false;
-      }
-      else {
-        if (afterBR && window.opera)
-          point(makePartSpan("", owner));
-        afterBR = true;
-      }
-      part.dirty = true;
-      nodeQueue.push(part);
-      point(part);
-      return text;
-    }
-
-    // Extract the text and newlines from a DOM node, insert them into
-    // the document, and return the textual content. Used to replace
-    // non-normalized nodes.
-    function writeNode(node, end) {
-      var simplified = simplifyDOM(node, end);
-      for (var i = 0; i < simplified.length; i++)
-        simplified[i] = insertPart(simplified[i]);
-      return simplified.join("");
-    }
-
-    // Check whether a node is a normalized <span> element.
-    function partNode(node){
-      if (node.isPart && node.childNodes.length == 1 && node.firstChild.nodeType == 3) {
-        node.currentText = node.firstChild.nodeValue;
-        return !/[\n\t\r]/.test(node.currentText);
-      }
-      return false;
-    }
-
-    // Advance to next node, return string for current node.
-    function next() {
-      if (!start) throw StopIteration;
-      var node = start;
-      start = node.nextSibling;
-
-      if (partNode(node)){
-        nodeQueue.push(node);
-        afterBR = false;
-        return node.currentText;
-      }
-      else if (isBR(node)) {
-        if (afterBR && window.opera)
-          node.parentNode.insertBefore(makePartSpan("", owner), node);
-        nodeQueue.push(node);
-        afterBR = true;
-        return "\n";
-      }
-      else {
-        var end = !node.nextSibling;
-        point = pointAt(node);
-        removeElement(node);
-        return writeNode(node, end);
-      }
-    }
-
-    // MochiKit iterators are objects with a next function that
-    // returns the next value or throws StopIteration when there are
-    // no more values.
-    return {next: next, nodes: nodeQueue};
-  }
-
-  // Determine the text size of a processed node.
-  function nodeSize(node) {
-    return isBR(node) ? 1 : node.currentText.length;
-  }
-
-  // Search backwards through the top-level nodes until the next BR or
-  // the start of the frame.
-  function startOfLine(node) {
-    while (node && !isBR(node)) node = node.previousSibling;
-    return node;
-  }
-  function endOfLine(node, container) {
-    if (!node) node = container.firstChild;
-    else if (isBR(node)) node = node.nextSibling;
-
-    while (node && !isBR(node)) node = node.nextSibling;
-    return node;
-  }
-
-  function time() {return new Date().getTime();}
-
-  // Client interface for searching the content of the editor. Create
-  // these by calling CodeMirror.getSearchCursor. To use, call
-  // findNext on the resulting object -- this returns a boolean
-  // indicating whether anything was found, and can be called again to
-  // skip to the next find. Use the select and replace methods to
-  // actually do something with the found locations.
-  function SearchCursor(editor, string, fromCursor, caseFold) {
-    this.editor = editor;
-    if (caseFold == undefined) {
-      caseFold = (string == string.toLowerCase());
-    }
-    this.caseFold = caseFold;
-    if (caseFold) string = string.toLowerCase();
-    this.history = editor.history;
-    this.history.commit();
-
-    // Are we currently at an occurrence of the search string?
-    this.atOccurrence = false;
-    // The object stores a set of nodes coming after its current
-    // position, so that when the current point is taken out of the
-    // DOM tree, we can still try to continue.
-    this.fallbackSize = 15;
-    var cursor;
-    // Start from the cursor when specified and a cursor can be found.
-    if (fromCursor && (cursor = select.cursorPos(this.editor.container))) {
-      this.line = cursor.node;
-      this.offset = cursor.offset;
-    }
-    else {
-      this.line = null;
-      this.offset = 0;
-    }
-    this.valid = !!string;
-
-    // Create a matcher function based on the kind of string we have.
-    var target = string.split("\n"), self = this;
-    this.matches = (target.length == 1) ?
-      // For one-line strings, searching can be done simply by calling
-      // indexOf on the current line.
-      function() {
-        var line = cleanText(self.history.textAfter(self.line).slice(self.offset));
-        var match = (self.caseFold ? line.toLowerCase() : line).indexOf(string);
-        if (match > -1)
-          return {from: {node: self.line, offset: self.offset + match},
-                  to: {node: self.line, offset: self.offset + match + string.length}};
-      } :
-      // Multi-line strings require internal iteration over lines, and
-      // some clunky checks to make sure the first match ends at the
-      // end of the line and the last match starts at the start.
-      function() {
-        var firstLine = cleanText(self.history.textAfter(self.line).slice(self.offset));
-        var match = (self.caseFold ? firstLine.toLowerCase() : firstLine).lastIndexOf(target[0]);
-        if (match == -1 || match != firstLine.length - target[0].length)
-          return false;
-        var startOffset = self.offset + match;
-
-        var line = self.history.nodeAfter(self.line);
-        for (var i = 1; i < target.length - 1; i++) {
-          var lineText = cleanText(self.history.textAfter(line));
-          if ((self.caseFold ? lineText.toLowerCase() : lineText) != target[i])
-            return false;
-          line = self.history.nodeAfter(line);
-        }
-
-        var lastLine = cleanText(self.history.textAfter(line));
-        if ((self.caseFold ? lastLine.toLowerCase() : lastLine).indexOf(target[target.length - 1]) != 0)
-          return false;
-
-        return {from: {node: self.line, offset: startOffset},
-                to: {node: line, offset: target[target.length - 1].length}};
-      };
-  }
-
-  SearchCursor.prototype = {
-    findNext: function() {
-      if (!this.valid) return false;
-      this.atOccurrence = false;
-      var self = this;
-
-      // Go back to the start of the document if the current line is
-      // no longer in the DOM tree.
-      if (this.line && !this.line.parentNode) {
-        this.line = null;
-        this.offset = 0;
-      }
-
-      // Set the cursor's position one character after the given
-      // position.
-      function saveAfter(pos) {
-        if (self.history.textAfter(pos.node).length > pos.offset) {
-          self.line = pos.node;
-          self.offset = pos.offset + 1;
-        }
-        else {
-          self.line = self.history.nodeAfter(pos.node);
-          self.offset = 0;
-        }
-      }
-
-      while (true) {
-        var match = this.matches();
-        // Found the search string.
-        if (match) {
-          this.atOccurrence = match;
-          saveAfter(match.from);
-          return true;
-        }
-        this.line = this.history.nodeAfter(this.line);
-        this.offset = 0;
-        // End of document.
-        if (!this.line) {
-          this.valid = false;
-          return false;
-        }
-      }
-    },
-
-    select: function() {
-      if (this.atOccurrence) {
-        select.setCursorPos(this.editor.container, this.atOccurrence.from, this.atOccurrence.to);
-        select.scrollToCursor(this.editor.container);
-      }
-    },
-
-    replace: function(string) {
-      if (this.atOccurrence) {
-        var end = this.editor.replaceRange(this.atOccurrence.from, this.atOccurrence.to, string);
-        this.line = end.node;
-        this.offset = end.offset;
-        this.atOccurrence = false;
-      }
-    }
-  };
-
-  // The Editor object is the main inside-the-iframe interface.
-  function Editor(options) {
-    this.options = options;
-    window.indentUnit = options.indentUnit;
-    this.parent = parent;
-    this.doc = document;
-    var container = this.container = this.doc.body;
-    this.win = window;
-    this.history = new UndoHistory(container, options.undoDepth, options.undoDelay, this);
-    var self = this;
-
-    if (!Editor.Parser)
-      throw "No parser loaded.";
-    if (options.parserConfig && Editor.Parser.configure)
-      Editor.Parser.configure(options.parserConfig);
-
-    if (!options.readOnly)
-      select.setCursorPos(container, {node: null, offset: 0});
-
-    this.dirty = [];
-    this.importCode(options.content || "");
-    this.history.onChange = options.onChange;
-
-    if (!options.readOnly) {
-      if (options.continuousScanning !== false) {
-        this.scanner = this.documentScanner(options.passTime);
-        this.delayScanning();
-      }
-
-      function setEditable() {
-        // Use contentEditable instead of designMode on IE, since designMode frames
-        // can not run any scripts. It would be nice if we could use contentEditable
-        // everywhere, but it is significantly flakier than designMode on every
-        // single non-IE browser.
-        if (document.body.contentEditable != undefined && internetExplorer)
-          document.body.contentEditable = "true";
-        else
-          document.designMode = "on";
-
-        document.documentElement.style.borderWidth = "0";
-        if (!options.textWrapping)
-          container.style.whiteSpace = "nowrap";
-      }
-
-      // If setting the frame editable fails, try again when the user
-      // focus it (happens when the frame is not visible on
-      // initialisation, in Firefox).
-      try {
-        setEditable();
-      }
-      catch(e) {
-        var focusEvent = addEventHandler(document, "focus", function() {
-          focusEvent();
-          setEditable();
-        }, true);
-      }
-
-      addEventHandler(document, "keydown", method(this, "keyDown"));
-      addEventHandler(document, "keypress", method(this, "keyPress"));
-      addEventHandler(document, "keyup", method(this, "keyUp"));
-
-      function cursorActivity() {self.cursorActivity(false);}
-      addEventHandler(document.body, "mouseup", cursorActivity);
-      addEventHandler(document.body, "cut", cursorActivity);
-
-      // workaround for a gecko bug [?] where going forward and then
-      // back again breaks designmode (no more cursor)
-      if (gecko)
-        addEventHandler(this.win, "pagehide", function(){self.unloaded = true;});
-
-      addEventHandler(document.body, "paste", function(event) {
-        cursorActivity();
-        var text = null;
-        try {
-          var clipboardData = event.clipboardData || window.clipboardData;
-          if (clipboardData) text = clipboardData.getData('Text');
-        }
-        catch(e) {}
-        if (text !== null) {
-          event.stop();
-          self.replaceSelection(text);
-          select.scrollToCursor(self.container);
-        }
-      });
-
-      if (this.options.autoMatchParens)
-        addEventHandler(document.body, "click", method(this, "scheduleParenHighlight"));
-    }
-    else if (!options.textWrapping) {
-      container.style.whiteSpace = "nowrap";
-    }
-  }
-
-  function isSafeKey(code) {
-    return (code >= 16 && code <= 18) || // shift, control, alt
-           (code >= 33 && code <= 40); // arrows, home, end
-  }
-
-  Editor.prototype = {
-    // Import a piece of code into the editor.
-    importCode: function(code) {
-      this.history.push(null, null, asEditorLines(code));
-      this.history.reset();
-    },
-
-    // Extract the code from the editor.
-    getCode: function() {
-      if (!this.container.firstChild)
-        return "";
-
-      var accum = [];
-      select.markSelection(this.win);
-      forEach(traverseDOM(this.container.firstChild), method(accum, "push"));
-      select.selectMarked();
-      return cleanText(accum.join(""));
-    },
-
-    checkLine: function(node) {
-      if (node === false || !(node == null || node.parentNode == this.container))
-        throw parent.CodeMirror.InvalidLineHandle;
-    },
-
-    cursorPosition: function(start) {
-      if (start == null) start = true;
-      var pos = select.cursorPos(this.container, start);
-      if (pos) return {line: pos.node, character: pos.offset};
-      else return {line: null, character: 0};
-    },
-
-    firstLine: function() {
-      return null;
-    },
-
-    lastLine: function() {
-      if (this.container.lastChild) return startOfLine(this.container.lastChild);
-      else return null;
-    },
-
-    nextLine: function(line) {
-      this.checkLine(line);
-      var end = endOfLine(line, this.container);
-      return end || false;
-    },
-
-    prevLine: function(line) {
-      this.checkLine(line);
-      if (line == null) return false;
-      return startOfLine(line.previousSibling);
-    },
-
-    visibleLineCount: function() {
-      var line = this.container.firstChild;
-      while (line && isBR(line)) line = line.nextSibling; // BR heights are unreliable
-      if (!line) return false;
-      var innerHeight = (window.innerHeight
-                         || document.documentElement.clientHeight
-                         || document.body.clientHeight);
-      return Math.floor(innerHeight / line.offsetHeight);
-    },
-
-    selectLines: function(startLine, startOffset, endLine, endOffset) {
-      this.checkLine(startLine);
-      var start = {node: startLine, offset: startOffset}, end = null;
-      if (endOffset !== undefined) {
-        this.checkLine(endLine);
-        end = {node: endLine, offset: endOffset};
-      }
-      select.setCursorPos(this.container, start, end);
-      select.scrollToCursor(this.container);
-    },
-
-    lineContent: function(line) {
-      var accum = [];
-      for (line = line ? line.nextSibling : this.container.firstChild;
-           line && !isBR(line); line = line.nextSibling)
-        accum.push(nodeText(line));
-      return cleanText(accum.join(""));
-    },
-
-    setLineContent: function(line, content) {
-      this.history.commit();
-      this.replaceRange({node: line, offset: 0},
-                        {node: line, offset: this.history.textAfter(line).length},
-                        content);
-      this.addDirtyNode(line);
-      this.scheduleHighlight();
-    },
-
-    removeLine: function(line) {
-      var node = line ? line.nextSibling : this.container.firstChild;
-      while (node) {
-        var next = node.nextSibling;
-        removeElement(node);
-        if (isBR(node)) break;
-        node = next;
-      }
-      this.addDirtyNode(line);
-      this.scheduleHighlight();
-    },
-
-    insertIntoLine: function(line, position, content) {
-      var before = null;
-      if (position == "end") {
-        before = endOfLine(line, this.container);
-      }
-      else {
-        for (var cur = line ? line.nextSibling : this.container.firstChild; cur; cur = cur.nextSibling) {
-          if (position == 0) {
-            before = cur;
-            break;
-          }
-          var text = nodeText(cur);
-          if (text.length > position) {
-            before = cur.nextSibling;
-            content = text.slice(0, position) + content + text.slice(position);
-            removeElement(cur);
-            break;
-          }
-          position -= text.length;
-        }
-      }
-
-      var lines = asEditorLines(content), doc = this.container.ownerDocument;
-      for (var i = 0; i < lines.length; i++) {
-        if (i > 0) this.container.insertBefore(doc.createElement("BR"), before);
-        this.container.insertBefore(makePartSpan(lines[i], doc), before);
-      }
-      this.addDirtyNode(line);
-      this.scheduleHighlight();
-    },
-
-    // Retrieve the selected text.
-    selectedText: function() {
-      var h = this.history;
-      h.commit();
-
-      var start = select.cursorPos(this.container, true),
-          end = select.cursorPos(this.container, false);
-      if (!start || !end) return "";
-
-      if (start.node == end.node)
-        return h.textAfter(start.node).slice(start.offset, end.offset);
-
-      var text = [h.textAfter(start.node).slice(start.offset)];
-      for (var pos = h.nodeAfter(start.node); pos != end.node; pos = h.nodeAfter(pos))
-        text.push(h.textAfter(pos));
-      text.push(h.textAfter(end.node).slice(0, end.offset));
-      return cleanText(text.join("\n"));
-    },
-
-    // Replace the selection with another piece of text.
-    replaceSelection: function(text) {
-      this.history.commit();
-
-      var start = select.cursorPos(this.container, true),
-          end = select.cursorPos(this.container, false);
-      if (!start || !end) return;
-
-      end = this.replaceRange(start, end, text);
-      select.setCursorPos(this.container, end);
-    },
-
-    cursorCoords: function(start) {
-      var sel = select.cursorPos(this.container, start);
-      if (!sel) return null;
-      var off = sel.offset, node = sel.node, doc = this.win.document, self = this;
-      function measureFromNode(node, xOffset) {
-        var y = -(self.win.document.body.scrollTop || self.win.document.documentElement.scrollTop || 0),
-            x = -(self.win.document.body.scrollLeft || self.win.document.documentElement.scrollLeft || 0) + xOffset;
-        forEach([node, self.win.frameElement], function(n) {
-          while (n) {x += n.offsetLeft; y += n.offsetTop;n = n.offsetParent;}
-        });
-        return {x: x, y: y, yBot: y + node.offsetHeight};
-      }
-      function withTempNode(text, f) {
-        var node = doc.createElement("SPAN");
-        node.appendChild(doc.createTextNode(text));
-        try {return f(node);}
-        finally {if (node.parentNode) node.parentNode.removeChild(node);}
-      }
-
-      while (off) {
-        node = node ? node.nextSibling : this.container.firstChild;
-        var txt = nodeText(node);
-        if (off < txt.length)
-          return withTempNode(txt.substr(0, off), function(tmp) {
-            tmp.style.position = "absolute"; tmp.style.visibility = "hidden";
-            tmp.className = node.className;
-            self.container.appendChild(tmp);
-            return measureFromNode(node, tmp.offsetWidth);
-          });
-        off -= txt.length;
-      }
-      if (node && isSpan(node))
-        return measureFromNode(node, node.offsetWidth);
-      else if (node && node.nextSibling && isSpan(node.nextSibling))
-        return measureFromNode(node.nextSibling, 0);
-      else
-        return withTempNode("\u200b", function(tmp) {
-          if (node) node.parentNode.insertBefore(tmp, node.nextSibling);
-          else self.container.insertBefore(tmp, self.container.firstChild);
-          return measureFromNode(tmp, 0);
-        });
-    },
-
-    reroutePasteEvent: function() {
-      if (this.capturingPaste || window.opera) return;
-      this.capturingPaste = true;
-      var te = window.frameElement.CodeMirror.textareaHack;
-      parent.focus();
-      te.value = "";
-      te.focus();
-
-      var self = this;
-      this.parent.setTimeout(function() {
-        self.capturingPaste = false;
-        self.win.focus();
-        if (self.selectionSnapshot) // IE hack
-          self.win.select.setBookmark(self.container, self.selectionSnapshot);
-        var text = te.value;
-        if (text) {
-          self.replaceSelection(text);
-          select.scrollToCursor(self.container);
-        }
-      }, 10);
-    },
-
-    replaceRange: function(from, to, text) {
-      var lines = asEditorLines(text);
-      lines[0] = this.history.textAfter(from.node).slice(0, from.offset) + lines[0];
-      var lastLine = lines[lines.length - 1];
-      lines[lines.length - 1] = lastLine + this.history.textAfter(to.node).slice(to.offset);
-      var end = this.history.nodeAfter(to.node);
-      this.history.push(from.node, end, lines);
-      return {node: this.history.nodeBefore(end),
-              offset: lastLine.length};
-    },
-
-    getSearchCursor: function(string, fromCursor, caseFold) {
-      return new SearchCursor(this, string, fromCursor, caseFold);
-    },
-
-    // Re-indent the whole buffer
-    reindent: function() {
-      if (this.container.firstChild)
-        this.indentRegion(null, this.container.lastChild);
-    },
-
-    reindentSelection: function(direction) {
-      if (!select.somethingSelected(this.win)) {
-        this.indentAtCursor(direction);
-      }
-      else {
-        var start = select.selectionTopNode(this.container, true),
-            end = select.selectionTopNode(this.container, false);
-        if (start === false || end === false) return;
-        this.indentRegion(start, end, direction);
-      }
-    },
-
-    grabKeys: function(eventHandler, filter) {
-      this.frozen = eventHandler;
-      this.keyFilter = filter;
-    },
-    ungrabKeys: function() {
-      this.frozen = "leave";
-    },
-
-    setParser: function(name, parserConfig) {
-      Editor.Parser = window[name];
-      parserConfig = parserConfig || this.options.parserConfig;
-      if (parserConfig && Editor.Parser.configure)
-        Editor.Parser.configure(parserConfig);
-
-      if (this.container.firstChild) {
-        forEach(this.container.childNodes, function(n) {
-          if (n.nodeType != 3) n.dirty = true;
-        });
-        this.addDirtyNode(this.firstChild);
-        this.scheduleHighlight();
-      }
-    },
-
-    // Intercept enter and tab, and assign their new functions.
-    keyDown: function(event) {
-      if (this.frozen == "leave") {this.frozen = null; this.keyFilter = null;}
-      if (this.frozen && (!this.keyFilter || this.keyFilter(event.keyCode, event))) {
-        event.stop();
-        this.frozen(event);
-        return;
-      }
-
-      var code = this.lastKeyDownCode = event.keyCode;
-      // Don't scan when the user is typing.
-      this.delayScanning();
-      // Schedule a paren-highlight event, if configured.
-      if (this.options.autoMatchParens)
-        this.scheduleParenHighlight();
-
-      // The various checks for !altKey are there because AltGr sets both
-      // ctrlKey and altKey to true, and should not be recognised as
-      // Control.
-      if (code == 13) { // enter
-        if (event.ctrlKey && !event.altKey) {
-          this.reparseBuffer();
-        }
-        else {
-          select.insertNewlineAtCursor(this.win);
-          this.indentAtCursor();
-          select.scrollToCursor(this.container);
-        }
-        event.stop();
-      }
-      else if (code == 9 && this.options.tabMode != "default" && !event.ctrlKey) { // tab
-        this.handleTab(!event.shiftKey);
-        event.stop();
-      }
-      else if (code == 32 && event.shiftKey && this.options.tabMode == "default") { // space
-        this.handleTab(true);
-        event.stop();
-      }
-      else if (code == 36 && !event.shiftKey && !event.ctrlKey) { // home
-        if (this.home()) event.stop();
-      }
-      else if (code == 35 && !event.shiftKey && !event.ctrlKey) { // end
-        if (this.end()) event.stop();
-      }
-      // Only in Firefox is the default behavior for PgUp/PgDn correct.
-      else if (code == 33 && !event.shiftKey && !event.ctrlKey && !gecko) { // PgUp
-        if (this.pageUp()) event.stop();
-      }
-      else if (code == 34 && !event.shiftKey && !event.ctrlKey && !gecko) {  // PgDn
-        if (this.pageDown()) event.stop();
-      }
-      else if ((code == 219 || code == 221) && event.ctrlKey && !event.altKey) { // [, ]
-        this.highlightParens(event.shiftKey, true);
-        event.stop();
-      }
-      else if (event.metaKey && !event.shiftKey && (code == 37 || code == 39)) { // Meta-left/right
-        var cursor = select.selectionTopNode(this.container);
-        if (cursor !== false && this.container.firstChild) {
-          if (code == 37) select.focusAfterNode(startOfLine(cursor), this.container);
-          else {
-            var end = endOfLine(cursor, this.container);
-            select.focusAfterNode(end ? end.previousSibling : this.container.lastChild, this.container);
-          }
-          event.stop();
-        }
-      }
-      else if ((event.ctrlKey || event.metaKey) && !event.altKey) {
-        if ((event.shiftKey && code == 90) || code == 89) { // shift-Z, Y
-          select.scrollToNode(this.history.redo());
-          event.stop();
-        }
-        else if (code == 90 || (safari && code == 8)) { // Z, backspace
-          select.scrollToNode(this.history.undo());
-          event.stop();
-        }
-        else if (code == 83 && this.options.saveFunction) { // S
-          this.options.saveFunction();
-          event.stop();
-        }
-        else if (internetExplorer && code == 86) {
-          this.reroutePasteEvent();
-        }
-      }
-      this.keyUpOrPressAfterLastKeyDown = false;
-    },
-
-    // Check for characters that should re-indent the current line,
-    // and prevent Opera from handling enter and tab anyway.
-    keyPress: function(event) {
-      this.keyUpOrPressAfterLastKeyDown = true;
-      var electric = Editor.Parser.electricChars, self = this;
-      // Hack for Opera, and Firefox on OS X, in which stopping a
-      // keydown event does not prevent the associated keypress event
-      // from happening, so we have to cancel enter and tab again
-      // here.
-      if ((this.frozen && (!this.keyFilter || this.keyFilter(event.keyCode || event.code, event))) ||
-          event.code == 13 || (event.code == 9 && this.options.tabMode != "default") ||
-          (event.code == 32 && event.shiftKey && this.options.tabMode == "default"))
-        event.stop();
-      else if (electric && electric.indexOf(event.character) != -1)
-        this.parent.setTimeout(function(){self.indentAtCursor(null);}, 0);
-      else if ((event.character == "v" || event.character == "V")
-               && (event.ctrlKey || event.metaKey) && !event.altKey) // ctrl-V
-        this.reroutePasteEvent();
-      // Work around a bug where pressing backspace at the end of a
-      // line often causes the cursor to jump to the start of the line
-      // in Opera 10.60.
-      else if (brokenOpera && event.code == 8) {
-        var sel = select.selectionTopNode(this.container), self = this,
-            next = sel ? sel.nextSibling : this.container.firstChild;
-        if (sel !== false && next && isBR(next))
-          this.parent.setTimeout(function(){
-            if (select.selectionTopNode(self.container) == next)
-              select.focusAfterNode(next.previousSibling, self.container);
-          }, 20);
-      }
-    },
-
-    // Mark the node at the cursor dirty when a non-safe key is
-    // released.
-    keyUp: function(event) {
-      this.keyUpOrPressAfterLastKeyDown = true;
-      this.cursorActivity(isSafeKey(event.keyCode));
-    },
-
-    // Indent the line following a given <br>, or null for the first
-    // line. If given a <br> element, this must have been highlighted
-    // so that it has an indentation method. Returns the whitespace
-    // element that has been modified or created (if any).
-    indentLineAfter: function(start, direction) {
-      // whiteSpace is the whitespace span at the start of the line,
-      // or null if there is no such node.
-      var whiteSpace = start ? start.nextSibling : this.container.firstChild;
-      if (whiteSpace && !hasClass(whiteSpace, "whitespace"))
-        whiteSpace = null;
-
-      // Sometimes the start of the line can influence the correct
-      // indentation, so we retrieve it.
-      var firstText = whiteSpace ? whiteSpace.nextSibling : (start ? start.nextSibling : this.container.firstChild);
-      var nextChars = (start && firstText && firstText.currentText) ? firstText.currentText : "";
-
-      // Ask the lexical context for the correct indentation, and
-      // compute how much this differs from the current indentation.
-      var newIndent = 0, curIndent = whiteSpace ? whiteSpace.currentText.length : 0;
-      if (direction != null && this.options.tabMode == "shift")
-        newIndent = direction ? curIndent + indentUnit : Math.max(0, curIndent - indentUnit)
-      else if (start)
-        newIndent = start.indentation(nextChars, curIndent, direction);
-      else if (Editor.Parser.firstIndentation)
-        newIndent = Editor.Parser.firstIndentation(nextChars, curIndent, direction);
-      var indentDiff = newIndent - curIndent;
-
-      // If there is too much, this is just a matter of shrinking a span.
-      if (indentDiff < 0) {
-        if (newIndent == 0) {
-          if (firstText) select.snapshotMove(whiteSpace.firstChild, firstText.firstChild, 0);
-          removeElement(whiteSpace);
-          whiteSpace = null;
-        }
-        else {
-          select.snapshotMove(whiteSpace.firstChild, whiteSpace.firstChild, indentDiff, true);
-          whiteSpace.currentText = makeWhiteSpace(newIndent);
-          whiteSpace.firstChild.nodeValue = whiteSpace.currentText;
-        }
-      }
-      // Not enough...
-      else if (indentDiff > 0) {
-        // If there is whitespace, we grow it.
-        if (whiteSpace) {
-          whiteSpace.currentText = makeWhiteSpace(newIndent);
-          whiteSpace.firstChild.nodeValue = whiteSpace.currentText;
-          select.snapshotMove(whiteSpace.firstChild, whiteSpace.firstChild, indentDiff, true);
-        }
-        // Otherwise, we have to add a new whitespace node.
-        else {
-          whiteSpace = makePartSpan(makeWhiteSpace(newIndent), this.doc);
-          whiteSpace.className = "whitespace";
-          if (start) insertAfter(whiteSpace, start);
-          else this.container.insertBefore(whiteSpace, this.container.firstChild);
-          select.snapshotMove(firstText && (firstText.firstChild || firstText),
-                              whiteSpace.firstChild, newIndent, false, true);
-        }
-      }
-      if (indentDiff != 0) this.addDirtyNode(start);
-    },
-
-    // Re-highlight the selected part of the document.
-    highlightAtCursor: function() {
-      var pos = select.selectionTopNode(this.container, true);
-      var to = select.selectionTopNode(this.container, false);
-      if (pos === false || to === false) return false;
-
-      select.markSelection(this.win);
-      if (this.highlight(pos, endOfLine(to, this.container), true, 20) === false)
-        return false;
-      select.selectMarked();
-      return true;
-    },
-
-    // When tab is pressed with text selected, the whole selection is
-    // re-indented, when nothing is selected, the line with the cursor
-    // is re-indented.
-    handleTab: function(direction) {
-      if (this.options.tabMode == "spaces")
-        select.insertTabAtCursor(this.win);
-      else
-        this.reindentSelection(direction);
-    },
-
-    // Custom home behaviour that doesn't land the cursor in front of
-    // leading whitespace unless pressed twice.
-    home: function() {
-      var cur = select.selectionTopNode(this.container, true), start = cur;
-      if (cur === false || !(!cur || cur.isPart || isBR(cur)) || !this.container.firstChild)
-        return false;
-
-      while (cur && !isBR(cur)) cur = cur.previousSibling;
-      var next = cur ? cur.nextSibling : this.container.firstChild;
-      if (next && next != start && next.isPart && hasClass(next, "whitespace"))
-        select.focusAfterNode(next, this.container);
-      else
-        select.focusAfterNode(cur, this.container);
-
-      select.scrollToCursor(this.container);
-      return true;
-    },
-
-    // Some browsers (Opera) don't manage to handle the end key
-    // properly in the face of vertical scrolling.
-    end: function() {
-      var cur = select.selectionTopNode(this.container, true);
-      if (cur === false) return false;
-      cur = endOfLine(cur, this.container);
-      if (!cur) return false;
-      select.focusAfterNode(cur.previousSibling, this.container);
-      select.scrollToCursor(this.container);
-      return true;
-    },
-
-    pageUp: function() {
-      var line = this.cursorPosition().line, scrollAmount = this.visibleLineCount();
-      if (line === false || scrollAmount === false) return false;
-      // Try to keep one line on the screen.
-      scrollAmount -= 2;
-      for (var i = 0; i < scrollAmount; i++) {
-        line = this.prevLine(line);
-        if (line === false) break;
-      }
-      if (i == 0) return false; // Already at first line
-      select.setCursorPos(this.container, {node: line, offset: 0});
-      select.scrollToCursor(this.container);
-      return true;
-    },
-
-    pageDown: function() {
-      var line = this.cursorPosition().line, scrollAmount = this.visibleLineCount();
-      if (line === false || scrollAmount === false) return false;
-      // Try to move to the last line of the current page.
-      scrollAmount -= 2;
-      for (var i = 0; i < scrollAmount; i++) {
-        var nextLine = this.nextLine(line);
-        if (nextLine === false) break;
-        line = nextLine;
-      }
-      if (i == 0) return false; // Already at last line
-      select.setCursorPos(this.container, {node: line, offset: 0});
-      select.scrollToCursor(this.container);
-      return true;
-    },
-
-    // Delay (or initiate) the next paren highlight event.
-    scheduleParenHighlight: function() {
-      if (this.parenEvent) this.parent.clearTimeout(this.parenEvent);
-      var self = this;
-      this.parenEvent = this.parent.setTimeout(function(){self.highlightParens();}, 300);
-    },
-
-    // Take the token before the cursor. If it contains a character in
-    // '()[]{}', search for the matching paren/brace/bracket, and
-    // highlight them in green for a moment, or red if no proper match
-    // was found.
-    highlightParens: function(jump, fromKey) {
-      var self = this;
-      // give the relevant nodes a colour.
-      function highlight(node, ok) {
-        if (!node) return;
-        if (self.options.markParen) {
-          self.options.markParen(node, ok);
-        }
-        else {
-          node.style.fontWeight = "bold";
-          node.style.color = ok ? "#8F8" : "#F88";
-        }
-      }
-      function unhighlight(node) {
-        if (!node) return;
-        if (self.options.unmarkParen) {
-          self.options.unmarkParen(node);
-        }
-        else {
-          node.style.fontWeight = "";
-          node.style.color = "";
-        }
-      }
-      if (!fromKey && self.highlighted) {
-        unhighlight(self.highlighted[0]);
-        unhighlight(self.highlighted[1]);
-      }
-
-      if (!window.select) return;
-      // Clear the event property.
-      if (this.parenEvent) this.parent.clearTimeout(this.parenEvent);
-      this.parenEvent = null;
-
-      // Extract a 'paren' from a piece of text.
-      function paren(node) {
-        if (node.currentText) {
-          var match = node.currentText.match(/^[\s\u00a0]*([\(\)\[\]{}])[\s\u00a0]*$/);
-          return match && match[1];
-        }
-      }
-      // Determine the direction a paren is facing.
-      function forward(ch) {
-        return /[\(\[\{]/.test(ch);
-      }
-
-      var ch, cursor = select.selectionTopNode(this.container, true);
-      if (!cursor || !this.highlightAtCursor()) return;
-      cursor = select.selectionTopNode(this.container, true);
-      if (!(cursor && ((ch = paren(cursor)) || (cursor = cursor.nextSibling) && (ch = paren(cursor)))))
-        return;
-      // We only look for tokens with the same className.
-      var className = cursor.className, dir = forward(ch), match = matching[ch];
-
-      // Since parts of the document might not have been properly
-      // highlighted, and it is hard to know in advance which part we
-      // have to scan, we just try, and when we find dirty nodes we
-      // abort, parse them, and re-try.
-      function tryFindMatch() {
-        var stack = [], ch, ok = true;
-        for (var runner = cursor; runner; runner = dir ? runner.nextSibling : runner.previousSibling) {
-          if (runner.className == className && isSpan(runner) && (ch = paren(runner))) {
-            if (forward(ch) == dir)
-              stack.push(ch);
-            else if (!stack.length)
-              ok = false;
-            else if (stack.pop() != matching[ch])
-              ok = false;
-            if (!stack.length) break;
-          }
-          else if (runner.dirty || !isSpan(runner) && !isBR(runner)) {
-            return {node: runner, status: "dirty"};
-          }
-        }
-        return {node: runner, status: runner && ok};
-      }
-
-      while (true) {
-        var found = tryFindMatch();
-        if (found.status == "dirty") {
-          this.highlight(found.node, endOfLine(found.node));
-          // Needed because in some corner cases a highlight does not
-          // reach a node.
-          found.node.dirty = false;
-          continue;
-        }
-        else {
-          highlight(cursor, found.status);
-          highlight(found.node, found.status);
-          if (fromKey)
-            self.parent.setTimeout(function() {unhighlight(cursor); unhighlight(found.node);}, 500);
-          else
-            self.highlighted = [cursor, found.node];
-          if (jump && found.node)
-            select.focusAfterNode(found.node.previousSibling, this.container);
-          break;
-        }
-      }
-    },
-
-    // Adjust the amount of whitespace at the start of the line that
-    // the cursor is on so that it is indented properly.
-    indentAtCursor: function(direction) {
-      if (!this.container.firstChild) return;
-      // The line has to have up-to-date lexical information, so we
-      // highlight it first.
-      if (!this.highlightAtCursor()) return;
-      var cursor = select.selectionTopNode(this.container, false);
-      // If we couldn't determine the place of the cursor,
-      // there's nothing to indent.
-      if (cursor === false)
-        return;
-      select.markSelection(this.win);
-      this.indentLineAfter(startOfLine(cursor), direction);
-      select.selectMarked();
-    },
-
-    // Indent all lines whose start falls inside of the current
-    // selection.
-    indentRegion: function(start, end, direction) {
-      var current = (start = startOfLine(start)), before = start && startOfLine(start.previousSibling);
-      if (!isBR(end)) end = endOfLine(end, this.container);
-      this.addDirtyNode(start);
-
-      do {
-        var next = endOfLine(current, this.container);
-        if (current) this.highlight(before, next, true);
-        this.indentLineAfter(current, direction);
-        before = current;
-        current = next;
-      } while (current != end);
-      select.setCursorPos(this.container, {node: start, offset: 0}, {node: end, offset: 0});
-    },
-
-    // Find the node that the cursor is in, mark it as dirty, and make
-    // sure a highlight pass is scheduled.
-    cursorActivity: function(safe) {
-      // pagehide event hack above
-      if (this.unloaded) {
-        this.win.document.designMode = "off";
-        this.win.document.designMode = "on";
-        this.unloaded = false;
-      }
-
-      if (internetExplorer) {
-        this.container.createTextRange().execCommand("unlink");
-        this.selectionSnapshot = select.getBookmark(this.container);
-      }
-
-      var activity = this.options.cursorActivity;
-      if (!safe || activity) {
-        var cursor = select.selectionTopNode(this.container, false);
-        if (cursor === false || !this.container.firstChild) return;
-        cursor = cursor || this.container.firstChild;
-        if (activity) activity(cursor);
-        if (!safe) {
-          this.scheduleHighlight();
-          this.addDirtyNode(cursor);
-        }
-      }
-    },
-
-    reparseBuffer: function() {
-      forEach(this.container.childNodes, function(node) {node.dirty = true;});
-      if (this.container.firstChild)
-        this.addDirtyNode(this.container.firstChild);
-    },
-
-    // Add a node to the set of dirty nodes, if it isn't already in
-    // there.
-    addDirtyNode: function(node) {
-      node = node || this.container.firstChild;
-      if (!node) return;
-
-      for (var i = 0; i < this.dirty.length; i++)
-        if (this.dirty[i] == node) return;
-
-      if (node.nodeType != 3)
-        node.dirty = true;
-      this.dirty.push(node);
-    },
-
-    allClean: function() {
-      return !this.dirty.length;
-    },
-
-    // Cause a highlight pass to happen in options.passDelay
-    // milliseconds. Clear the existing timeout, if one exists. This
-    // way, the passes do not happen while the user is typing, and
-    // should as unobtrusive as possible.
-    scheduleHighlight: function() {
-      // Timeouts are routed through the parent window, because on
-      // some browsers designMode windows do not fire timeouts.
-      var self = this;
-      this.parent.clearTimeout(this.highlightTimeout);
-      this.highlightTimeout = this.parent.setTimeout(function(){self.highlightDirty();}, this.options.passDelay);
-    },
-
-    // Fetch one dirty node, and remove it from the dirty set.
-    getDirtyNode: function() {
-      while (this.dirty.length > 0) {
-        var found = this.dirty.pop();
-        // IE8 sometimes throws an unexplainable 'invalid argument'
-        // exception for found.parentNode
-        try {
-          // If the node has been coloured in the meantime, or is no
-          // longer in the document, it should not be returned.
-          while (found && found.parentNode != this.container)
-            found = found.parentNode;
-          if (found && (found.dirty || found.nodeType == 3))
-            return found;
-        } catch (e) {}
-      }
-      return null;
-    },
-
-    // Pick dirty nodes, and highlight them, until options.passTime
-    // milliseconds have gone by. The highlight method will continue
-    // to next lines as long as it finds dirty nodes. It returns
-    // information about the place where it stopped. If there are
-    // dirty nodes left after this function has spent all its lines,
-    // it shedules another highlight to finish the job.
-    highlightDirty: function(force) {
-      // Prevent FF from raising an error when it is firing timeouts
-      // on a page that's no longer loaded.
-      if (!window.select) return false;
-
-      if (!this.options.readOnly) select.markSelection(this.win);
-      var start, endTime = force ? null : time() + this.options.passTime;
-      while ((time() < endTime || force) && (start = this.getDirtyNode())) {
-        var result = this.highlight(start, endTime);
-        if (result && result.node && result.dirty)
-          this.addDirtyNode(result.node);
-      }
-      if (!this.options.readOnly) select.selectMarked();
-      if (start) this.scheduleHighlight();
-      return this.dirty.length == 0;
-    },
-
-    // Creates a function that, when called through a timeout, will
-    // continuously re-parse the document.
-    documentScanner: function(passTime) {
-      var self = this, pos = null;
-      return function() {
-        // FF timeout weirdness workaround.
-        if (!window.select) return;
-        // If the current node is no longer in the document... oh
-        // well, we start over.
-        if (pos && pos.parentNode != self.container)
-          pos = null;
-        select.markSelection(self.win);
-        var result = self.highlight(pos, time() + passTime, true);
-        select.selectMarked();
-        var newPos = result ? (result.node && result.node.nextSibling) : null;
-        pos = (pos == newPos) ? null : newPos;
-        self.delayScanning();
-      };
-    },
-
-    // Starts the continuous scanning process for this document after
-    // a given interval.
-    delayScanning: function() {
-      if (this.scanner) {
-        this.parent.clearTimeout(this.documentScan);
-        this.documentScan = this.parent.setTimeout(this.scanner, this.options.continuousScanning);
-      }
-    },
-
-    isIMEOn: function() {
-      // chrome:  keyDown keyCode is 229 while IME on
-      // firefox: no keyUps or keyPresses fires after first keyDown while IME on
-      return this.lastKeyDownCode == 229 || this.keyUpOrPressAfterLastKeyDown === false;
-    },
-
-    // The function that does the actual highlighting/colouring (with
-    // help from the parser and the DOM normalizer). Its interface is
-    // rather overcomplicated, because it is used in different
-    // situations: ensuring that a certain line is highlighted, or
-    // highlighting up to X milliseconds starting from a certain
-    // point. The 'from' argument gives the node at which it should
-    // start. If this is null, it will start at the beginning of the
-    // document. When a timestamp is given with the 'target' argument,
-    // it will stop highlighting at that time. If this argument holds
-    // a DOM node, it will highlight until it reaches that node. If at
-    // any time it comes across two 'clean' lines (no dirty nodes), it
-    // will stop, except when 'cleanLines' is true. maxBacktrack is
-    // the maximum number of lines to backtrack to find an existing
-    // parser instance. This is used to give up in situations where a
-    // highlight would take too long and freeze the browser interface.
-    highlight: function(from, target, cleanLines, maxBacktrack){
-      var container = this.container, self = this, active = this.options.activeTokens;
-      var endTime = (typeof target == "number" ? target : null);
-
-      if (!container.firstChild || this.isIMEOn())
-        return false;
-      // Backtrack to the first node before from that has a partial
-      // parse stored.
-      while (from && (!from.parserFromHere || from.dirty)) {
-        if (maxBacktrack != null && isBR(from) && (--maxBacktrack) < 0)
-          return false;
-        from = from.previousSibling;
-      }
-      // If we are at the end of the document, do nothing.
-      if (from && !from.nextSibling)
-        return false;
-
-      // Check whether a part (<span> node) and the corresponding token
-      // match.
-      function correctPart(token, part){
-        return !part.reduced && part.currentText == token.value && part.className == token.style;
-      }
-      // Shorten the text associated with a part by chopping off
-      // characters from the front. Note that only the currentText
-      // property gets changed. For efficiency reasons, we leave the
-      // nodeValue alone -- we set the reduced flag to indicate that
-      // this part must be replaced.
-      function shortenPart(part, minus){
-        part.currentText = part.currentText.substring(minus);
-        part.reduced = true;
-      }
-      // Create a part corresponding to a given token.
-      function tokenPart(token){
-        var part = makePartSpan(token.value, self.doc);     
-        part.className = token.style;
-        return part;
-      }
-
-      function maybeTouch(node) {
-        if (node) {
-          var old = node.oldNextSibling;
-          if (lineDirty || old === undefined || node.nextSibling != old)
-            self.history.touch(node);
-          node.oldNextSibling = node.nextSibling;
-        }
-        else {
-          var old = self.container.oldFirstChild;
-          if (lineDirty || old === undefined || self.container.firstChild != old)
-            self.history.touch(null);
-          self.container.oldFirstChild = self.container.firstChild;
-        }
-      }
-
-      // Get the token stream. If from is null, we start with a new
-      // parser from the start of the frame, otherwise a partial parse
-      // is resumed.
-      var traversal = traverseDOM(from ? from.nextSibling : container.firstChild),
-          stream = stringStream(traversal),
-          parsed = from ? from.parserFromHere(stream) : Editor.Parser.make(stream);
-
-      function surroundedByBRs(node) {
-        return (node.previousSibling == null || isBR(node.previousSibling)) &&
-               (node.nextSibling == null || isBR(node.nextSibling));
-      }
-
-      // parts is an interface to make it possible to 'delay' fetching
-      // the next DOM node until we are completely done with the one
-      // before it. This is necessary because often the next node is
-      // not yet available when we want to proceed past the current
-      // one.
-      var parts = {
-        current: null,
-        // Fetch current node.
-        get: function(){
-          if (!this.current)
-            this.current = traversal.nodes.shift();
-          return this.current;
-        },
-        // Advance to the next part (do not fetch it yet).
-        next: function(){
-          this.current = null;
-        },
-        // Remove the current part from the DOM tree, and move to the
-        // next.
-        remove: function(){
-          container.removeChild(this.get());
-          this.current = null;
-        },
-        // Advance to the next part that is not empty, discarding empty
-        // parts.
-        getNonEmpty: function(){
-          var part = this.get();
-          // Allow empty nodes when they are alone on a line, needed
-          // for the FF cursor bug workaround (see select.js,
-          // insertNewlineAtCursor).
-          while (part && isSpan(part) && part.currentText == "") {
-            // Leave empty nodes that are alone on a line alone in
-            // Opera, since that browsers doesn't deal well with
-            // having 2 BRs in a row.
-            if (window.opera && surroundedByBRs(part)) {
-              this.next();
-              part = this.get();
-            }
-            else {
-              var old = part;
-              this.remove();
-              part = this.get();
-              // Adjust selection information, if any. See select.js for details.
-              select.snapshotMove(old.firstChild, part && (part.firstChild || part), 0);
-            }
-          }
-          
-          return part;
-        }
-      };
-
-      var lineDirty = false, prevLineDirty = true, lineNodes = 0;
-
-      // This forEach loops over the tokens from the parsed stream, and
-      // at the same time uses the parts object to proceed through the
-      // corresponding DOM nodes.
-      forEach(parsed, function(token){
-        var part = parts.getNonEmpty();
-
-        if (token.value == "\n"){
-          // The idea of the two streams actually staying synchronized
-          // is such a long shot that we explicitly check.
-          if (!isBR(part))
-            throw "Parser out of sync. Expected BR.";
-
-          if (part.dirty || !part.indentation) lineDirty = true;
-          maybeTouch(from);
-          from = part;
-
-          // Every <br> gets a copy of the parser state and a lexical
-          // context assigned to it. The first is used to be able to
-          // later resume parsing from this point, the second is used
-          // for indentation.
-          part.parserFromHere = parsed.copy();
-          part.indentation = token.indentation;
-          part.dirty = false;
-
-          // If the target argument wasn't an integer, go at least
-          // until that node.
-          if (endTime == null && part == target) throw StopIteration;
-
-          // A clean line with more than one node means we are done.
-          // Throwing a StopIteration is the way to break out of a
-          // MochiKit forEach loop.
-          if ((endTime != null && time() >= endTime) || (!lineDirty && !prevLineDirty && lineNodes > 1 && !cleanLines))
-            throw StopIteration;
-          prevLineDirty = lineDirty; lineDirty = false; lineNodes = 0;
-          parts.next();
-        }
-        else {
-          if (!isSpan(part))
-            throw "Parser out of sync. Expected SPAN.";
-          if (part.dirty)
-            lineDirty = true;
-          lineNodes++;
-
-          // If the part matches the token, we can leave it alone.
-          if (correctPart(token, part)){
-            part.dirty = false;
-            parts.next();
-          }
-          // Otherwise, we have to fix it.
-          else {
-            lineDirty = true;
-            // Insert the correct part.
-            var newPart = tokenPart(token);
-            container.insertBefore(newPart, part);
-            if (active) active(newPart, token, self);
-            var tokensize = token.value.length;
-            var offset = 0;
-            // Eat up parts until the text for this token has been
-            // removed, adjusting the stored selection info (see
-            // select.js) in the process.
-            while (tokensize > 0) {
-              part = parts.get();
-              var partsize = part.currentText.length;
-              select.snapshotReplaceNode(part.firstChild, newPart.firstChild, tokensize, offset);
-              if (partsize > tokensize){
-                shortenPart(part, tokensize);
-                tokensize = 0;
-              }
-              else {
-                tokensize -= partsize;
-                offset += partsize;
-                parts.remove();
-              }
-            }
-          }
-        }
-      });
-      maybeTouch(from);
-
-      // The function returns some status information that is used by
-      // hightlightDirty to determine whether and where it has to
-      // continue.
-      return {node: parts.getNonEmpty(),
-              dirty: lineDirty};
-    }
-  };
-
-  return Editor;
-})();
-
-addEventHandler(window, "load", function() {
-  var CodeMirror = window.frameElement.CodeMirror;
-  var e = CodeMirror.editor = new Editor(CodeMirror.options);
-  this.parent.setTimeout(method(CodeMirror, "init"), 0);
-});
diff --git a/redakcja/static/js/lib/codemirror-0.8/parsexml.js b/redakcja/static/js/lib/codemirror-0.8/parsexml.js
deleted file mode 100644 (file)
index 994efd3..0000000
+++ /dev/null
@@ -1,286 +0,0 @@
-/* This file defines an XML parser, with a few kludges to make it
- * useable for HTML. autoSelfClosers defines a set of tag names that
- * are expected to not have a closing tag, and doNotIndent specifies
- * the tags inside of which no indentation should happen (see Config
- * object). These can be disabled by passing the editor an object like
- * {useHTMLKludges: false} as parserConfig option.
- */
-
-var XMLParser = Editor.Parser = (function() {
-  var Kludges = {
-    autoSelfClosers: {"br": true, "img": true, "hr": true, "link": true, "input": true,
-                      "meta": true, "col": true, "frame": true, "base": true, "area": true},
-    doNotIndent: {"pre": true, "!cdata": true}
-  };
-  var NoKludges = {autoSelfClosers: {}, doNotIndent: {"!cdata": true}};
-  var UseKludges = Kludges;
-  var alignCDATA = false;
-
-  // Simple stateful tokenizer for XML documents. Returns a
-  // MochiKit-style iterator, with a state property that contains a
-  // function encapsulating the current state. See tokenize.js.
-  var tokenizeXML = (function() {
-    function inText(source, setState) {
-      var ch = source.next();
-      if (ch == "<") {
-        if (source.equals("!")) {
-          source.next();
-          if (source.equals("[")) {
-            if (source.lookAhead("[CDATA[", true)) {
-              setState(inBlock("xml-cdata", "]]>"));
-              return null;
-            }
-            else {
-              return "xml-text";
-            }
-          }
-          else if (source.lookAhead("--", true)) {
-            setState(inBlock("xml-comment", "-->"));
-            return null;
-          }
-          else {
-            return "xml-text";
-          }
-        }
-        else if (source.equals("?")) {
-          source.next();
-          source.nextWhileMatches(/[\w\._\-]/);
-          setState(inBlock("xml-processing", "?>"));
-          return "xml-processing";
-        }
-        else {
-          if (source.equals("/")) source.next();
-          setState(inTag);
-          return "xml-punctuation";
-        }
-      }
-      else if (ch == "&") {
-        while (!source.endOfLine()) {
-          if (source.next() == ";")
-            break;
-        }
-        return "xml-entity";
-      }
-      else {
-        source.nextWhileMatches(/[^&<\n]/);
-        return "xml-text";
-      }
-    }
-
-    function inTag(source, setState) {
-      var ch = source.next();
-      if (ch == ">") {
-        setState(inText);
-        return "xml-punctuation";
-      }
-      else if (/[?\/]/.test(ch) && source.equals(">")) {
-        source.next();
-        setState(inText);
-        return "xml-punctuation";
-      }
-      else if (ch == "=") {
-        return "xml-punctuation";
-      }
-      else if (/[\'\"]/.test(ch)) {
-        setState(inAttribute(ch));
-        return null;
-      }
-      else {
-        source.nextWhileMatches(/[^\s\u00a0=<>\"\'\/?]/);
-        return "xml-name";
-      }
-    }
-
-    function inAttribute(quote) {
-      return function(source, setState) {
-        while (!source.endOfLine()) {
-          if (source.next() == quote) {
-            setState(inTag);
-            break;
-          }
-        }
-        return "xml-attribute";
-      };
-    }
-
-    function inBlock(style, terminator) {
-      return function(source, setState) {
-        while (!source.endOfLine()) {
-          if (source.lookAhead(terminator, true)) {
-            setState(inText);
-            break;
-          }
-          source.next();
-        }
-        return style;
-      };
-    }
-
-    return function(source, startState) {
-      return tokenizer(source, startState || inText);
-    };
-  })();
-
-  // The parser. The structure of this function largely follows that of
-  // parseJavaScript in parsejavascript.js (there is actually a bit more
-  // shared code than I'd like), but it is quite a bit simpler.
-  function parseXML(source) {
-    var tokens = tokenizeXML(source), token;
-    var cc = [base];
-    var tokenNr = 0, indented = 0;
-    var currentTag = null, context = null;
-    var consume;
-    
-    function push(fs) {
-      for (var i = fs.length - 1; i >= 0; i--)
-        cc.push(fs[i]);
-    }
-    function cont() {
-      push(arguments);
-      consume = true;
-    }
-    function pass() {
-      push(arguments);
-      consume = false;
-    }
-
-    function markErr() {
-      token.style += " xml-error";
-    }
-    function expect(text) {
-      return function(style, content) {
-        if (content == text) cont();
-        else {markErr(); cont(arguments.callee);}
-      };
-    }
-
-    function pushContext(tagname, startOfLine) {
-      var noIndent = UseKludges.doNotIndent.hasOwnProperty(tagname) || (context && context.noIndent);
-      context = {prev: context, name: tagname, indent: indented, startOfLine: startOfLine, noIndent: noIndent};
-    }
-    function popContext() {
-      context = context.prev;
-    }
-    function computeIndentation(baseContext) {
-      return function(nextChars, current) {
-        var context = baseContext;
-        if (context && context.noIndent)
-          return current;
-        if (alignCDATA && /<!\[CDATA\[/.test(nextChars))
-          return 0;
-        if (context && /^<\//.test(nextChars))
-          context = context.prev;
-        while (context && !context.startOfLine)
-          context = context.prev;
-        if (context)
-          return context.indent + indentUnit;
-        else
-          return 0;
-      };
-    }
-
-    function base() {
-      return pass(element, base);
-    }
-    var harmlessTokens = {"xml-text": true, "xml-entity": true, "xml-comment": true, "xml-processing": true};
-    function element(style, content) {
-      if (content == "<") cont(tagname, attributes, endtag(tokenNr == 1));
-      else if (content == "</") cont(closetagname, expect(">"));
-      else if (style == "xml-cdata") {
-        if (!context || context.name != "!cdata") pushContext("!cdata");
-        if (/\]\]>$/.test(content)) popContext();
-        cont();
-      }
-      else if (harmlessTokens.hasOwnProperty(style)) cont();
-      else {markErr(); cont();}
-    }
-    function tagname(style, content) {
-      if (style == "xml-name") {
-        currentTag = content.toLowerCase();
-        token.style = "xml-tagname";
-        cont();
-      }
-      else {
-        currentTag = null;
-        pass();
-      }
-    }
-    function closetagname(style, content) {
-      if (style == "xml-name") {
-        token.style = "xml-tagname";
-        if (context && content.toLowerCase() == context.name) popContext();
-        else markErr();
-      }
-      cont();
-    }
-    function endtag(startOfLine) {
-      return function(style, content) {
-        if (content == "/>" || (content == ">" && UseKludges.autoSelfClosers.hasOwnProperty(currentTag))) cont();
-        else if (content == ">") {pushContext(currentTag, startOfLine); cont();}
-        else {markErr(); cont(arguments.callee);}
-      };
-    }
-    function attributes(style) {
-      if (style == "xml-name") {token.style = "xml-attname"; cont(attribute, attributes);}
-      else pass();
-    }
-    function attribute(style, content) {
-      if (content == "=") cont(value);
-      else if (content == ">" || content == "/>") pass(endtag);
-      else pass();
-    }
-    function value(style) {
-      if (style == "xml-attribute") cont(value);
-      else pass();
-    }
-
-    return {
-      indentation: function() {return indented;},
-
-      next: function(){
-        token = tokens.next();
-        if (token.style == "whitespace" && tokenNr == 0)
-          indented = token.value.length;
-        else
-          tokenNr++;
-        if (token.content == "\n") {
-          indented = tokenNr = 0;
-          token.indentation = computeIndentation(context);
-        }
-
-        if (token.style == "whitespace" || token.type == "xml-comment")
-          return token;
-
-        while(true){
-          consume = false;
-          cc.pop()(token.style, token.content);
-          if (consume) return token;
-        }
-      },
-
-      copy: function(){
-        var _cc = cc.concat([]), _tokenState = tokens.state, _context = context;
-        var parser = this;
-        
-        return function(input){
-          cc = _cc.concat([]);
-          tokenNr = indented = 0;
-          context = _context;
-          tokens = tokenizeXML(input, _tokenState);
-          return parser;
-        };
-      }
-    };
-  }
-
-  return {
-    make: parseXML,
-    electricChars: "/",
-    configure: function(config) {
-      if (config.useHTMLKludges != null)
-        UseKludges = config.useHTMLKludges ? Kludges : NoKludges;
-      if (config.alignCDATA)
-        alignCDATA = config.alignCDATA;
-    }
-  };
-})();
diff --git a/redakcja/static/js/lib/codemirror-0.8/select.js b/redakcja/static/js/lib/codemirror-0.8/select.js
deleted file mode 100644 (file)
index 01fe996..0000000
+++ /dev/null
@@ -1,672 +0,0 @@
-/* Functionality for finding, storing, and restoring selections
- *
- * This does not provide a generic API, just the minimal functionality
- * required by the CodeMirror system.
- */
-
-// Namespace object.
-var select = {};
-
-(function() {
-  select.ie_selection = document.selection && document.selection.createRangeCollection;
-
-  // Find the 'top-level' (defined as 'a direct child of the node
-  // passed as the top argument') node that the given node is
-  // contained in. Return null if the given node is not inside the top
-  // node.
-  function topLevelNodeAt(node, top) {
-    while (node && node.parentNode != top)
-      node = node.parentNode;
-    return node;
-  }
-
-  // Find the top-level node that contains the node before this one.
-  function topLevelNodeBefore(node, top) {
-    while (!node.previousSibling && node.parentNode != top)
-      node = node.parentNode;
-    return topLevelNodeAt(node.previousSibling, top);
-  }
-
-  var fourSpaces = "\u00a0\u00a0\u00a0\u00a0";
-
-  select.scrollToNode = function(node, cursor) {
-    if (!node) return;
-    var element = node,
-        doc = element.ownerDocument, body = doc.body,
-        win = (doc.defaultView || doc.parentWindow),
-        html = doc.documentElement,
-        atEnd = !element.nextSibling || !element.nextSibling.nextSibling
-                || !element.nextSibling.nextSibling.nextSibling;
-    // In Opera (and recent Webkit versions), BR elements *always*
-    // have a offsetTop property of zero.
-    var compensateHack = 0;
-    while (element && !element.offsetTop) {
-      compensateHack++;
-      element = element.previousSibling;
-    }
-    // atEnd is another kludge for these browsers -- if the cursor is
-    // at the end of the document, and the node doesn't have an
-    // offset, just scroll to the end.
-    if (compensateHack == 0) atEnd = false;
-
-    // WebKit has a bad habit of (sometimes) happily returning bogus
-    // offsets when the document has just been changed. This seems to
-    // always be 5/5, so we don't use those.
-    if (webkit && element && element.offsetTop == 5 && element.offsetLeft == 5)
-      return;
-
-    var y = compensateHack * (element ? element.offsetHeight : 0), x = 0,
-        width = (node ? node.offsetWidth : 0), pos = element;
-    while (pos && pos.offsetParent) {
-      y += pos.offsetTop;
-      // Don't count X offset for <br> nodes
-      if (!isBR(pos))
-        x += pos.offsetLeft;
-      pos = pos.offsetParent;
-    }
-
-    var scroll_x = body.scrollLeft || html.scrollLeft || 0,
-        scroll_y = body.scrollTop || html.scrollTop || 0,
-        scroll = false, screen_width = win.innerWidth || html.clientWidth || 0;
-
-    if (cursor || width < screen_width) {
-      if (cursor) {
-        var off = select.offsetInNode(win, node), size = nodeText(node).length;
-        if (size) x += width * (off / size);
-      }
-      var screen_x = x - scroll_x;
-      if (screen_x < 0 || screen_x > screen_width) {
-        scroll_x = x;
-        scroll = true;
-      }
-    }
-    var screen_y = y - scroll_y;
-    if (screen_y < 0 || atEnd || screen_y > (win.innerHeight || html.clientHeight || 0) - 50) {
-      scroll_y = atEnd ? 1e6 : y;
-      scroll = true;
-    }
-    if (scroll) win.scrollTo(scroll_x, scroll_y);
-  };
-
-  select.scrollToCursor = function(container) {
-    select.scrollToNode(select.selectionTopNode(container, true) || container.firstChild, true);
-  };
-
-  // Used to prevent restoring a selection when we do not need to.
-  var currentSelection = null;
-
-  select.snapshotChanged = function() {
-    if (currentSelection) currentSelection.changed = true;
-  };
-
-  // This is called by the code in editor.js whenever it is replacing
-  // a text node. The function sees whether the given oldNode is part
-  // of the current selection, and updates this selection if it is.
-  // Because nodes are often only partially replaced, the length of
-  // the part that gets replaced has to be taken into account -- the
-  // selection might stay in the oldNode if the newNode is smaller
-  // than the selection's offset. The offset argument is needed in
-  // case the selection does move to the new object, and the given
-  // length is not the whole length of the new node (part of it might
-  // have been used to replace another node).
-  select.snapshotReplaceNode = function(from, to, length, offset) {
-    if (!currentSelection) return;
-
-    function replace(point) {
-      if (from == point.node) {
-        currentSelection.changed = true;
-        if (length && point.offset > length) {
-          point.offset -= length;
-        }
-        else {
-          point.node = to;
-          point.offset += (offset || 0);
-        }
-      }
-    }
-    replace(currentSelection.start);
-    replace(currentSelection.end);
-  };
-
-  select.snapshotMove = function(from, to, distance, relative, ifAtStart) {
-    if (!currentSelection) return;
-
-    function move(point) {
-      if (from == point.node && (!ifAtStart || point.offset == 0)) {
-        currentSelection.changed = true;
-        point.node = to;
-        if (relative) point.offset = Math.max(0, point.offset + distance);
-        else point.offset = distance;
-      }
-    }
-    move(currentSelection.start);
-    move(currentSelection.end);
-  };
-
-  // Most functions are defined in two ways, one for the IE selection
-  // model, one for the W3C one.
-  if (select.ie_selection) {
-    function selectionNode(win, start) {
-      var range = win.document.selection.createRange();
-      range.collapse(start);
-
-      function nodeAfter(node) {
-        var found = null;
-        while (!found && node) {
-          found = node.nextSibling;
-          node = node.parentNode;
-        }
-        return nodeAtStartOf(found);
-      }
-
-      function nodeAtStartOf(node) {
-        while (node && node.firstChild) node = node.firstChild;
-        return {node: node, offset: 0};
-      }
-
-      var containing = range.parentElement();
-      if (!isAncestor(win.document.body, containing)) return null;
-      if (!containing.firstChild) return nodeAtStartOf(containing);
-
-      var working = range.duplicate();
-      working.moveToElementText(containing);
-      working.collapse(true);
-      for (var cur = containing.firstChild; cur; cur = cur.nextSibling) {
-        if (cur.nodeType == 3) {
-          var size = cur.nodeValue.length;
-          working.move("character", size);
-        }
-        else {
-          working.moveToElementText(cur);
-          working.collapse(false);
-        }
-
-        var dir = range.compareEndPoints("StartToStart", working);
-        if (dir == 0) return nodeAfter(cur);
-        if (dir == 1) continue;
-        if (cur.nodeType != 3) return nodeAtStartOf(cur);
-
-        working.setEndPoint("StartToEnd", range);
-        return {node: cur, offset: size - working.text.length};
-      }
-      return nodeAfter(containing);
-    }
-
-    select.markSelection = function(win) {
-      currentSelection = null;
-      var sel = win.document.selection;
-      if (!sel) return;
-      var start = selectionNode(win, true),
-          end = selectionNode(win, false);
-      if (!start || !end) return;
-      currentSelection = {start: start, end: end, window: win, changed: false};
-    };
-
-    select.selectMarked = function() {
-      if (!currentSelection || !currentSelection.changed) return;
-      var win = currentSelection.window, doc = win.document;
-
-      function makeRange(point) {
-        var range = doc.body.createTextRange(),
-            node = point.node;
-        if (!node) {
-          range.moveToElementText(currentSelection.window.document.body);
-          range.collapse(false);
-        }
-        else if (node.nodeType == 3) {
-          range.moveToElementText(node.parentNode);
-          var offset = point.offset;
-          while (node.previousSibling) {
-            node = node.previousSibling;
-            offset += (node.innerText || "").length;
-          }
-          range.move("character", offset);
-        }
-        else {
-          range.moveToElementText(node);
-          range.collapse(true);
-        }
-        return range;
-      }
-
-      var start = makeRange(currentSelection.start), end = makeRange(currentSelection.end);
-      start.setEndPoint("StartToEnd", end);
-      start.select();
-    };
-
-    select.offsetInNode = function(win, node) {
-      var sel = win.document.selection;
-      if (!sel) return 0;
-      var range = sel.createRange(), range2 = range.duplicate();
-      try {range2.moveToElementText(node);} catch(e){return 0;}
-      range.setEndPoint("StartToStart", range2);
-      return range.text.length;
-    };
-
-    // Get the top-level node that one end of the cursor is inside or
-    // after. Note that this returns false for 'no cursor', and null
-    // for 'start of document'.
-    select.selectionTopNode = function(container, start) {
-      var selection = container.ownerDocument.selection;
-      if (!selection) return false;
-
-      var range = selection.createRange(), range2 = range.duplicate();
-      range.collapse(start);
-      var around = range.parentElement();
-      if (around && isAncestor(container, around)) {
-        // Only use this node if the selection is not at its start.
-        range2.moveToElementText(around);
-        if (range.compareEndPoints("StartToStart", range2) == 1)
-          return topLevelNodeAt(around, container);
-      }
-
-      // Move the start of a range to the start of a node,
-      // compensating for the fact that you can't call
-      // moveToElementText with text nodes.
-      function moveToNodeStart(range, node) {
-        if (node.nodeType == 3) {
-          var count = 0, cur = node.previousSibling;
-          while (cur && cur.nodeType == 3) {
-            count += cur.nodeValue.length;
-            cur = cur.previousSibling;
-          }
-          if (cur) {
-            try{range.moveToElementText(cur);}
-            catch(e){return false;}
-            range.collapse(false);
-          }
-          else range.moveToElementText(node.parentNode);
-          if (count) range.move("character", count);
-        }
-        else {
-          try{range.moveToElementText(node);}
-          catch(e){return false;}
-        }
-        return true;
-      }
-
-      // Do a binary search through the container object, comparing
-      // the start of each node to the selection
-      var start = 0, end = container.childNodes.length - 1;
-      while (start < end) {
-        var middle = Math.ceil((end + start) / 2), node = container.childNodes[middle];
-        if (!node) return false; // Don't ask. IE6 manages this sometimes.
-        if (!moveToNodeStart(range2, node)) return false;
-        if (range.compareEndPoints("StartToStart", range2) == 1)
-          start = middle;
-        else
-          end = middle - 1;
-      }
-      return container.childNodes[start] || null;
-    };
-
-    // Place the cursor after this.start. This is only useful when
-    // manually moving the cursor instead of restoring it to its old
-    // position.
-    select.focusAfterNode = function(node, container) {
-      var range = container.ownerDocument.body.createTextRange();
-      range.moveToElementText(node || container);
-      range.collapse(!node);
-      range.select();
-    };
-
-    select.somethingSelected = function(win) {
-      var sel = win.document.selection;
-      return sel && (sel.createRange().text != "");
-    };
-
-    function insertAtCursor(window, html) {
-      var selection = window.document.selection;
-      if (selection) {
-        var range = selection.createRange();
-        range.pasteHTML(html);
-        range.collapse(false);
-        range.select();
-      }
-    }
-
-    // Used to normalize the effect of the enter key, since browsers
-    // do widely different things when pressing enter in designMode.
-    select.insertNewlineAtCursor = function(window) {
-      insertAtCursor(window, "<br>");
-    };
-
-    select.insertTabAtCursor = function(window) {
-      insertAtCursor(window, fourSpaces);
-    };
-
-    // Get the BR node at the start of the line on which the cursor
-    // currently is, and the offset into the line. Returns null as
-    // node if cursor is on first line.
-    select.cursorPos = function(container, start) {
-      var selection = container.ownerDocument.selection;
-      if (!selection) return null;
-
-      var topNode = select.selectionTopNode(container, start);
-      while (topNode && !isBR(topNode))
-        topNode = topNode.previousSibling;
-
-      var range = selection.createRange(), range2 = range.duplicate();
-      range.collapse(start);
-      if (topNode) {
-        range2.moveToElementText(topNode);
-        range2.collapse(false);
-      }
-      else {
-        // When nothing is selected, we can get all kinds of funky errors here.
-        try { range2.moveToElementText(container); }
-        catch (e) { return null; }
-        range2.collapse(true);
-      }
-      range.setEndPoint("StartToStart", range2);
-
-      return {node: topNode, offset: range.text.length};
-    };
-
-    select.setCursorPos = function(container, from, to) {
-      function rangeAt(pos) {
-        var range = container.ownerDocument.body.createTextRange();
-        if (!pos.node) {
-          range.moveToElementText(container);
-          range.collapse(true);
-        }
-        else {
-          range.moveToElementText(pos.node);
-          range.collapse(false);
-        }
-        range.move("character", pos.offset);
-        return range;
-      }
-
-      var range = rangeAt(from);
-      if (to && to != from)
-        range.setEndPoint("EndToEnd", rangeAt(to));
-      range.select();
-    }
-
-    // Some hacks for storing and re-storing the selection when the editor loses and regains focus.
-    select.getBookmark = function (container) {
-      var from = select.cursorPos(container, true), to = select.cursorPos(container, false);
-      if (from && to) return {from: from, to: to};
-    };
-
-    // Restore a stored selection.
-    select.setBookmark = function(container, mark) {
-      if (!mark) return;
-      select.setCursorPos(container, mark.from, mark.to);
-    };
-  }
-  // W3C model
-  else {
-    // Find the node right at the cursor, not one of its
-    // ancestors with a suitable offset. This goes down the DOM tree
-    // until a 'leaf' is reached (or is it *up* the DOM tree?).
-    function innerNode(node, offset) {
-      while (node.nodeType != 3 && !isBR(node)) {
-        var newNode = node.childNodes[offset] || node.nextSibling;
-        offset = 0;
-        while (!newNode && node.parentNode) {
-          node = node.parentNode;
-          newNode = node.nextSibling;
-        }
-        node = newNode;
-        if (!newNode) break;
-      }
-      return {node: node, offset: offset};
-    }
-
-    // Store start and end nodes, and offsets within these, and refer
-    // back to the selection object from those nodes, so that this
-    // object can be updated when the nodes are replaced before the
-    // selection is restored.
-    select.markSelection = function (win) {
-      var selection = win.getSelection();
-      if (!selection || selection.rangeCount == 0)
-        return (currentSelection = null);
-      var range = selection.getRangeAt(0);
-
-      currentSelection = {
-        start: innerNode(range.startContainer, range.startOffset),
-        end: innerNode(range.endContainer, range.endOffset),
-        window: win,
-        changed: false
-      };
-    };
-
-    select.selectMarked = function () {
-      var cs = currentSelection;
-      // on webkit-based browsers, it is apparently possible that the
-      // selection gets reset even when a node that is not one of the
-      // endpoints get messed with. the most common situation where
-      // this occurs is when a selection is deleted or overwitten. we
-      // check for that here.
-      function focusIssue() {
-        if (cs.start.node == cs.end.node && cs.start.offset == cs.end.offset) {
-          var selection = cs.window.getSelection();
-          if (!selection || selection.rangeCount == 0) return true;
-          var range = selection.getRangeAt(0), point = innerNode(range.startContainer, range.startOffset);
-          return cs.start.node != point.node || cs.start.offset != point.offset;
-        }
-      }
-      if (!cs || !(cs.changed || (webkit && focusIssue()))) return;
-      var win = cs.window, range = win.document.createRange();
-
-      function setPoint(point, which) {
-        if (point.node) {
-          // Some magic to generalize the setting of the start and end
-          // of a range.
-          if (point.offset == 0)
-            range["set" + which + "Before"](point.node);
-          else
-            range["set" + which](point.node, point.offset);
-        }
-        else {
-          range.setStartAfter(win.document.body.lastChild || win.document.body);
-        }
-      }
-
-      setPoint(cs.end, "End");
-      setPoint(cs.start, "Start");
-      selectRange(range, win);
-    };
-
-    // Helper for selecting a range object.
-    function selectRange(range, window) {
-      var selection = window.getSelection();
-      if (!selection) return;
-      selection.removeAllRanges();
-      selection.addRange(range);
-    }
-    function selectionRange(window) {
-      var selection = window.getSelection();
-      if (!selection || selection.rangeCount == 0)
-        return false;
-      else
-        return selection.getRangeAt(0);
-    }
-
-    // Finding the top-level node at the cursor in the W3C is, as you
-    // can see, quite an involved process.
-    select.selectionTopNode = function(container, start) {
-      var range = selectionRange(container.ownerDocument.defaultView);
-      if (!range) return false;
-
-      var node = start ? range.startContainer : range.endContainer;
-      var offset = start ? range.startOffset : range.endOffset;
-      // Work around (yet another) bug in Opera's selection model.
-      if (window.opera && !start && range.endContainer == container && range.endOffset == range.startOffset + 1 &&
-          container.childNodes[range.startOffset] && isBR(container.childNodes[range.startOffset]))
-        offset--;
-
-      // For text nodes, we look at the node itself if the cursor is
-      // inside, or at the node before it if the cursor is at the
-      // start.
-      if (node.nodeType == 3){
-        if (offset > 0)
-          return topLevelNodeAt(node, container);
-        else
-          return topLevelNodeBefore(node, container);
-      }
-      // Occasionally, browsers will return the HTML node as
-      // selection. If the offset is 0, we take the start of the frame
-      // ('after null'), otherwise, we take the last node.
-      else if (node.nodeName.toUpperCase() == "HTML") {
-        return (offset == 1 ? null : container.lastChild);
-      }
-      // If the given node is our 'container', we just look up the
-      // correct node by using the offset.
-      else if (node == container) {
-        return (offset == 0) ? null : node.childNodes[offset - 1];
-      }
-      // In any other case, we have a regular node. If the cursor is
-      // at the end of the node, we use the node itself, if it is at
-      // the start, we use the node before it, and in any other
-      // case, we look up the child before the cursor and use that.
-      else {
-        if (offset == node.childNodes.length)
-          return topLevelNodeAt(node, container);
-        else if (offset == 0)
-          return topLevelNodeBefore(node, container);
-        else
-          return topLevelNodeAt(node.childNodes[offset - 1], container);
-      }
-    };
-
-    select.focusAfterNode = function(node, container) {
-      var win = container.ownerDocument.defaultView,
-          range = win.document.createRange();
-      range.setStartBefore(container.firstChild || container);
-      // In Opera, setting the end of a range at the end of a line
-      // (before a BR) will cause the cursor to appear on the next
-      // line, so we set the end inside of the start node when
-      // possible.
-      if (node && !node.firstChild)
-        range.setEndAfter(node);
-      else if (node)
-        range.setEnd(node, node.childNodes.length);
-      else
-        range.setEndBefore(container.firstChild || container);
-      range.collapse(false);
-      selectRange(range, win);
-    };
-
-    select.somethingSelected = function(win) {
-      var range = selectionRange(win);
-      return range && !range.collapsed;
-    };
-
-    select.offsetInNode = function(win, node) {
-      var range = selectionRange(win);
-      if (!range) return 0;
-      range = range.cloneRange();
-      range.setStartBefore(node);
-      return range.toString().length;
-    };
-
-    function insertNodeAtCursor(window, node) {
-      var range = selectionRange(window);
-      if (!range) return;
-
-      range.deleteContents();
-      range.insertNode(node);
-
-      // work around weirdness where Opera will magically insert a new
-      // BR node when a BR node inside a span is moved around. makes
-      // sure the BR ends up outside of spans.
-      if (window.opera && isBR(node) && isSpan(node.parentNode)) {
-        var next = node.nextSibling, p = node.parentNode, outer = p.parentNode;
-        outer.insertBefore(node, p.nextSibling);
-        var textAfter = "";
-        for (; next && next.nodeType == 3; next = next.nextSibling) {
-          textAfter += next.nodeValue;
-          removeElement(next);
-        }
-        outer.insertBefore(makePartSpan(textAfter, window.document), node.nextSibling);
-      }
-      range = window.document.createRange();
-      range.selectNode(node);
-      range.collapse(false);
-      selectRange(range, window);
-    }
-
-    select.insertNewlineAtCursor = function(window) {
-      if (webkit)
-        document.execCommand('insertLineBreak');
-      else
-        insertNodeAtCursor(window, window.document.createElement("BR"));
-    };
-
-    select.insertTabAtCursor = function(window) {
-      insertNodeAtCursor(window, window.document.createTextNode(fourSpaces));
-    };
-
-    select.cursorPos = function(container, start) {
-      var range = selectionRange(window);
-      if (!range) return;
-
-      var topNode = select.selectionTopNode(container, start);
-      while (topNode && !isBR(topNode))
-        topNode = topNode.previousSibling;
-
-      range = range.cloneRange();
-      range.collapse(start);
-      if (topNode)
-        range.setStartAfter(topNode);
-      else
-        range.setStartBefore(container);
-
-      return {node: topNode, offset: range.toString().length};
-    };
-
-    select.setCursorPos = function(container, from, to) {
-      var win = container.ownerDocument.defaultView,
-          range = win.document.createRange();
-
-      function setPoint(node, offset, side) {
-        if (offset == 0 && node && !node.nextSibling) {
-          range["set" + side + "After"](node);
-          return true;
-        }
-
-        if (!node)
-          node = container.firstChild;
-        else
-          node = node.nextSibling;
-
-        if (!node) return;
-
-        if (offset == 0) {
-          range["set" + side + "Before"](node);
-          return true;
-        }
-
-        var backlog = []
-        function decompose(node) {
-          if (node.nodeType == 3)
-            backlog.push(node);
-          else
-            forEach(node.childNodes, decompose);
-        }
-        while (true) {
-          while (node && !backlog.length) {
-            decompose(node);
-            node = node.nextSibling;
-          }
-          var cur = backlog.shift();
-          if (!cur) return false;
-
-          var length = cur.nodeValue.length;
-          if (length >= offset) {
-            range["set" + side](cur, offset);
-            return true;
-          }
-          offset -= length;
-        }
-      }
-
-      to = to || from;
-      if (setPoint(to.node, to.offset, "End") && setPoint(from.node, from.offset, "Start"))
-        selectRange(range, win);
-    };
-  }
-})();
diff --git a/redakcja/static/js/lib/codemirror-0.8/stringstream.js b/redakcja/static/js/lib/codemirror-0.8/stringstream.js
deleted file mode 100644 (file)
index 4f5bc61..0000000
+++ /dev/null
@@ -1,140 +0,0 @@
-/* String streams are the things fed to parsers (which can feed them
- * to a tokenizer if they want). They provide peek and next methods
- * for looking at the current character (next 'consumes' this
- * character, peek does not), and a get method for retrieving all the
- * text that was consumed since the last time get was called.
- *
- * An easy mistake to make is to let a StopIteration exception finish
- * the token stream while there are still characters pending in the
- * string stream (hitting the end of the buffer while parsing a
- * token). To make it easier to detect such errors, the stringstreams
- * throw an exception when this happens.
- */
-
-// Make a stringstream stream out of an iterator that returns strings.
-// This is applied to the result of traverseDOM (see codemirror.js),
-// and the resulting stream is fed to the parser.
-var stringStream = function(source){
-  // String that's currently being iterated over.
-  var current = "";
-  // Position in that string.
-  var pos = 0;
-  // Accumulator for strings that have been iterated over but not
-  // get()-ed yet.
-  var accum = "";
-  // Make sure there are more characters ready, or throw
-  // StopIteration.
-  function ensureChars() {
-    while (pos == current.length) {
-      accum += current;
-      current = ""; // In case source.next() throws
-      pos = 0;
-      try {current = source.next();}
-      catch (e) {
-        if (e != StopIteration) throw e;
-        else return false;
-      }
-    }
-    return true;
-  }
-
-  return {
-    // Return the next character in the stream.
-    peek: function() {
-      if (!ensureChars()) return null;
-      return current.charAt(pos);
-    },
-    // Get the next character, throw StopIteration if at end, check
-    // for unused content.
-    next: function() {
-      if (!ensureChars()) {
-        if (accum.length > 0)
-          throw "End of stringstream reached without emptying buffer ('" + accum + "').";
-        else
-          throw StopIteration;
-      }
-      return current.charAt(pos++);
-    },
-    // Return the characters iterated over since the last call to
-    // .get().
-    get: function() {
-      var temp = accum;
-      accum = "";
-      if (pos > 0){
-        temp += current.slice(0, pos);
-        current = current.slice(pos);
-        pos = 0;
-      }
-      return temp;
-    },
-    // Push a string back into the stream.
-    push: function(str) {
-      current = current.slice(0, pos) + str + current.slice(pos);
-    },
-    lookAhead: function(str, consume, skipSpaces, caseInsensitive) {
-      function cased(str) {return caseInsensitive ? str.toLowerCase() : str;}
-      str = cased(str);
-      var found = false;
-
-      var _accum = accum, _pos = pos;
-      if (skipSpaces) this.nextWhileMatches(/[\s\u00a0]/);
-
-      while (true) {
-        var end = pos + str.length, left = current.length - pos;
-        if (end <= current.length) {
-          found = str == cased(current.slice(pos, end));
-          pos = end;
-          break;
-        }
-        else if (str.slice(0, left) == cased(current.slice(pos))) {
-          accum += current; current = "";
-          try {current = source.next();}
-          catch (e) {break;}
-          pos = 0;
-          str = str.slice(left);
-        }
-        else {
-          break;
-        }
-      }
-
-      if (!(found && consume)) {
-        current = accum.slice(_accum.length) + current;
-        pos = _pos;
-        accum = _accum;
-      }
-
-      return found;
-    },
-
-    // Utils built on top of the above
-    more: function() {
-      return this.peek() !== null;
-    },
-    applies: function(test) {
-      var next = this.peek();
-      return (next !== null && test(next));
-    },
-    nextWhile: function(test) {
-      var next;
-      while ((next = this.peek()) !== null && test(next))
-        this.next();
-    },
-    matches: function(re) {
-      var next = this.peek();
-      return (next !== null && re.test(next));
-    },
-    nextWhileMatches: function(re) {
-      var next;
-      while ((next = this.peek()) !== null && re.test(next))
-        this.next();
-    },
-    equals: function(ch) {
-      return ch === this.peek();
-    },
-    endOfLine: function() {
-      var next = this.peek();
-      return next == null || next == "\n";
-    }
-  };
-};
diff --git a/redakcja/static/js/lib/codemirror-0.8/tokenize.js b/redakcja/static/js/lib/codemirror-0.8/tokenize.js
deleted file mode 100644 (file)
index 071970c..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-// A framework for simple tokenizers. Takes care of newlines and
-// white-space, and of getting the text from the source stream into
-// the token object. A state is a function of two arguments -- a
-// string stream and a setState function. The second can be used to
-// change the tokenizer's state, and can be ignored for stateless
-// tokenizers. This function should advance the stream over a token
-// and return a string or object containing information about the next
-// token, or null to pass and have the (new) state be called to finish
-// the token. When a string is given, it is wrapped in a {style, type}
-// object. In the resulting object, the characters consumed are stored
-// under the content property. Any whitespace following them is also
-// automatically consumed, and added to the value property. (Thus,
-// content is the actual meaningful part of the token, while value
-// contains all the text it spans.)
-
-function tokenizer(source, state) {
-  // Newlines are always a separate token.
-  function isWhiteSpace(ch) {
-    // The messy regexp is because IE's regexp matcher is of the
-    // opinion that non-breaking spaces are no whitespace.
-    return ch != "\n" && /^[\s\u00a0]*$/.test(ch);
-  }
-
-  var tokenizer = {
-    state: state,
-
-    take: function(type) {
-      if (typeof(type) == "string")
-        type = {style: type, type: type};
-
-      type.content = (type.content || "") + source.get();
-      if (!/\n$/.test(type.content))
-        source.nextWhile(isWhiteSpace);
-      type.value = type.content + source.get();
-      return type;
-    },
-
-    next: function () {
-      if (!source.more()) throw StopIteration;
-
-      var type;
-      if (source.equals("\n")) {
-        source.next();
-        return this.take("whitespace");
-      }
-      
-      if (source.applies(isWhiteSpace))
-        type = "whitespace";
-      else
-        while (!type)
-          type = this.state(source, function(s) {tokenizer.state = s;});
-
-      return this.take(type);
-    }
-  };
-  return tokenizer;
-}
diff --git a/redakcja/static/js/lib/codemirror-0.8/undo.js b/redakcja/static/js/lib/codemirror-0.8/undo.js
deleted file mode 100644 (file)
index 4ea2385..0000000
+++ /dev/null
@@ -1,410 +0,0 @@
-/**
- * Storage and control for undo information within a CodeMirror
- * editor. 'Why on earth is such a complicated mess required for
- * that?', I hear you ask. The goal, in implementing this, was to make
- * the complexity of storing and reverting undo information depend
- * only on the size of the edited or restored content, not on the size
- * of the whole document. This makes it necessary to use a kind of
- * 'diff' system, which, when applied to a DOM tree, causes some
- * complexity and hackery.
- *
- * In short, the editor 'touches' BR elements as it parses them, and
- * the UndoHistory stores these. When nothing is touched in commitDelay
- * milliseconds, the changes are committed: It goes over all touched
- * nodes, throws out the ones that did not change since last commit or
- * are no longer in the document, and assembles the rest into zero or
- * more 'chains' -- arrays of adjacent lines. Links back to these
- * chains are added to the BR nodes, while the chain that previously
- * spanned these nodes is added to the undo history. Undoing a change
- * means taking such a chain off the undo history, restoring its
- * content (text is saved per line) and linking it back into the
- * document.
- */
-
-// A history object needs to know about the DOM container holding the
-// document, the maximum amount of undo levels it should store, the
-// delay (of no input) after which it commits a set of changes, and,
-// unfortunately, the 'parent' window -- a window that is not in
-// designMode, and on which setTimeout works in every browser.
-function UndoHistory(container, maxDepth, commitDelay, editor) {
-  this.container = container;
-  this.maxDepth = maxDepth; this.commitDelay = commitDelay;
-  this.editor = editor; this.parent = editor.parent;
-  // This line object represents the initial, empty editor.
-  var initial = {text: "", from: null, to: null};
-  // As the borders between lines are represented by BR elements, the
-  // start of the first line and the end of the last one are
-  // represented by null. Since you can not store any properties
-  // (links to line objects) in null, these properties are used in
-  // those cases.
-  this.first = initial; this.last = initial;
-  // Similarly, a 'historyTouched' property is added to the BR in
-  // front of lines that have already been touched, and 'firstTouched'
-  // is used for the first line.
-  this.firstTouched = false;
-  // History is the set of committed changes, touched is the set of
-  // nodes touched since the last commit.
-  this.history = []; this.redoHistory = []; this.touched = [];
-}
-
-UndoHistory.prototype = {
-  // Schedule a commit (if no other touches come in for commitDelay
-  // milliseconds).
-  scheduleCommit: function() {
-    var self = this;
-    this.parent.clearTimeout(this.commitTimeout);
-    this.commitTimeout = this.parent.setTimeout(function(){self.tryCommit();}, this.commitDelay);
-  },
-
-  // Mark a node as touched. Null is a valid argument.
-  touch: function(node) {
-    this.setTouched(node);
-    this.scheduleCommit();
-  },
-
-  // Undo the last change.
-  undo: function() {
-    // Make sure pending changes have been committed.
-    this.commit();
-
-    if (this.history.length) {
-      // Take the top diff from the history, apply it, and store its
-      // shadow in the redo history.
-      var item = this.history.pop();
-      this.redoHistory.push(this.updateTo(item, "applyChain"));
-      this.notifyEnvironment();
-      return this.chainNode(item);
-    }
-  },
-
-  // Redo the last undone change.
-  redo: function() {
-    this.commit();
-    if (this.redoHistory.length) {
-      // The inverse of undo, basically.
-      var item = this.redoHistory.pop();
-      this.addUndoLevel(this.updateTo(item, "applyChain"));
-      this.notifyEnvironment();
-      return this.chainNode(item);
-    }
-  },
-
-  clear: function() {
-    this.history = [];
-    this.redoHistory = [];
-  },
-
-  // Ask for the size of the un/redo histories.
-  historySize: function() {
-    return {undo: this.history.length, redo: this.redoHistory.length};
-  },
-
-  // Push a changeset into the document.
-  push: function(from, to, lines) {
-    var chain = [];
-    for (var i = 0; i < lines.length; i++) {
-      var end = (i == lines.length - 1) ? to : this.container.ownerDocument.createElement("BR");
-      chain.push({from: from, to: end, text: cleanText(lines[i])});
-      from = end;
-    }
-    this.pushChains([chain], from == null && to == null);
-    this.notifyEnvironment();
-  },
-
-  pushChains: function(chains, doNotHighlight) {
-    this.commit(doNotHighlight);
-    this.addUndoLevel(this.updateTo(chains, "applyChain"));
-    this.redoHistory = [];
-  },
-
-  // Retrieve a DOM node from a chain (for scrolling to it after undo/redo).
-  chainNode: function(chains) {
-    for (var i = 0; i < chains.length; i++) {
-      var start = chains[i][0], node = start && (start.from || start.to);
-      if (node) return node;
-    }
-  },
-
-  // Clear the undo history, make the current document the start
-  // position.
-  reset: function() {
-    this.history = []; this.redoHistory = [];
-  },
-
-  textAfter: function(br) {
-    return this.after(br).text;
-  },
-
-  nodeAfter: function(br) {
-    return this.after(br).to;
-  },
-
-  nodeBefore: function(br) {
-    return this.before(br).from;
-  },
-
-  // Commit unless there are pending dirty nodes.
-  tryCommit: function() {
-    if (!window.UndoHistory) return; // Stop when frame has been unloaded
-    if (this.editor.highlightDirty()) this.commit(true);
-    else this.scheduleCommit();
-  },
-
-  // Check whether the touched nodes hold any changes, if so, commit
-  // them.
-  commit: function(doNotHighlight) {
-    this.parent.clearTimeout(this.commitTimeout);
-    // Make sure there are no pending dirty nodes.
-    if (!doNotHighlight) this.editor.highlightDirty(true);
-    // Build set of chains.
-    var chains = this.touchedChains(), self = this;
-
-    if (chains.length) {
-      this.addUndoLevel(this.updateTo(chains, "linkChain"));
-      this.redoHistory = [];
-      this.notifyEnvironment();
-    }
-  },
-
-  // [ end of public interface ]
-
-  // Update the document with a given set of chains, return its
-  // shadow. updateFunc should be "applyChain" or "linkChain". In the
-  // second case, the chains are taken to correspond the the current
-  // document, and only the state of the line data is updated. In the
-  // first case, the content of the chains is also pushed iinto the
-  // document.
-  updateTo: function(chains, updateFunc) {
-    var shadows = [], dirty = [];
-    for (var i = 0; i < chains.length; i++) {
-      shadows.push(this.shadowChain(chains[i]));
-      dirty.push(this[updateFunc](chains[i]));
-    }
-    if (updateFunc == "applyChain")
-      this.notifyDirty(dirty);
-    return shadows;
-  },
-
-  // Notify the editor that some nodes have changed.
-  notifyDirty: function(nodes) {
-    forEach(nodes, method(this.editor, "addDirtyNode"))
-    this.editor.scheduleHighlight();
-  },
-
-  notifyEnvironment: function() {
-    if (this.onChange) this.onChange();
-    // Used by the line-wrapping line-numbering code.
-    if (window.frameElement && window.frameElement.CodeMirror.updateNumbers)
-      window.frameElement.CodeMirror.updateNumbers();
-  },
-
-  // Link a chain into the DOM nodes (or the first/last links for null
-  // nodes).
-  linkChain: function(chain) {
-    for (var i = 0; i < chain.length; i++) {
-      var line = chain[i];
-      if (line.from) line.from.historyAfter = line;
-      else this.first = line;
-      if (line.to) line.to.historyBefore = line;
-      else this.last = line;
-    }
-  },
-
-  // Get the line object after/before a given node.
-  after: function(node) {
-    return node ? node.historyAfter : this.first;
-  },
-  before: function(node) {
-    return node ? node.historyBefore : this.last;
-  },
-
-  // Mark a node as touched if it has not already been marked.
-  setTouched: function(node) {
-    if (node) {
-      if (!node.historyTouched) {
-        this.touched.push(node);
-        node.historyTouched = true;
-      }
-    }
-    else {
-      this.firstTouched = true;
-    }
-  },
-
-  // Store a new set of undo info, throw away info if there is more of
-  // it than allowed.
-  addUndoLevel: function(diffs) {
-    this.history.push(diffs);
-    if (this.history.length > this.maxDepth)
-      this.history.shift();
-  },
-
-  // Build chains from a set of touched nodes.
-  touchedChains: function() {
-    var self = this;
-
-    // The temp system is a crummy hack to speed up determining
-    // whether a (currently touched) node has a line object associated
-    // with it. nullTemp is used to store the object for the first
-    // line, other nodes get it stored in their historyTemp property.
-    var nullTemp = null;
-    function temp(node) {return node ? node.historyTemp : nullTemp;}
-    function setTemp(node, line) {
-      if (node) node.historyTemp = line;
-      else nullTemp = line;
-    }
-
-    function buildLine(node) {
-      var text = [];
-      for (var cur = node ? node.nextSibling : self.container.firstChild;
-           cur && !isBR(cur); cur = cur.nextSibling)
-        if (cur.currentText) text.push(cur.currentText);
-      return {from: node, to: cur, text: cleanText(text.join(""))};
-    }
-
-    // Filter out unchanged lines and nodes that are no longer in the
-    // document. Build up line objects for remaining nodes.
-    var lines = [];
-    if (self.firstTouched) self.touched.push(null);
-    forEach(self.touched, function(node) {
-      if (node && node.parentNode != self.container) return;
-
-      if (node) node.historyTouched = false;
-      else self.firstTouched = false;
-
-      var line = buildLine(node), shadow = self.after(node);
-      if (!shadow || shadow.text != line.text || shadow.to != line.to) {
-        lines.push(line);
-        setTemp(node, line);
-      }
-    });
-
-    // Get the BR element after/before the given node.
-    function nextBR(node, dir) {
-      var link = dir + "Sibling", search = node[link];
-      while (search && !isBR(search))
-        search = search[link];
-      return search;
-    }
-
-    // Assemble line objects into chains by scanning the DOM tree
-    // around them.
-    var chains = []; self.touched = [];
-    forEach(lines, function(line) {
-      // Note that this makes the loop skip line objects that have
-      // been pulled into chains by lines before them.
-      if (!temp(line.from)) return;
-
-      var chain = [], curNode = line.from, safe = true;
-      // Put any line objects (referred to by temp info) before this
-      // one on the front of the array.
-      while (true) {
-        var curLine = temp(curNode);
-        if (!curLine) {
-          if (safe) break;
-          else curLine = buildLine(curNode);
-        }
-        chain.unshift(curLine);
-        setTemp(curNode, null);
-        if (!curNode) break;
-        safe = self.after(curNode);
-        curNode = nextBR(curNode, "previous");
-      }
-      curNode = line.to; safe = self.before(line.from);
-      // Add lines after this one at end of array.
-      while (true) {
-        if (!curNode) break;
-        var curLine = temp(curNode);
-        if (!curLine) {
-          if (safe) break;
-          else curLine = buildLine(curNode);
-        }
-        chain.push(curLine);
-        setTemp(curNode, null);
-        safe = self.before(curNode);
-        curNode = nextBR(curNode, "next");
-      }
-      chains.push(chain);
-    });
-
-    return chains;
-  },
-
-  // Find the 'shadow' of a given chain by following the links in the
-  // DOM nodes at its start and end.
-  shadowChain: function(chain) {
-    var shadows = [], next = this.after(chain[0].from), end = chain[chain.length - 1].to;
-    while (true) {
-      shadows.push(next);
-      var nextNode = next.to;
-      if (!nextNode || nextNode == end)
-        break;
-      else
-        next = nextNode.historyAfter || this.before(end);
-      // (The this.before(end) is a hack -- FF sometimes removes
-      // properties from BR nodes, in which case the best we can hope
-      // for is to not break.)
-    }
-    return shadows;
-  },
-
-  // Update the DOM tree to contain the lines specified in a given
-  // chain, link this chain into the DOM nodes.
-  applyChain: function(chain) {
-    // Some attempt is made to prevent the cursor from jumping
-    // randomly when an undo or redo happens. It still behaves a bit
-    // strange sometimes.
-    var cursor = select.cursorPos(this.container, false), self = this;
-
-    // Remove all nodes in the DOM tree between from and to (null for
-    // start/end of container).
-    function removeRange(from, to) {
-      var pos = from ? from.nextSibling : self.container.firstChild;
-      while (pos != to) {
-        var temp = pos.nextSibling;
-        removeElement(pos);
-        pos = temp;
-      }
-    }
-
-    var start = chain[0].from, end = chain[chain.length - 1].to;
-    // Clear the space where this change has to be made.
-    removeRange(start, end);
-
-    // Insert the content specified by the chain into the DOM tree.
-    for (var i = 0; i < chain.length; i++) {
-      var line = chain[i];
-      // The start and end of the space are already correct, but BR
-      // tags inside it have to be put back.
-      if (i > 0)
-        self.container.insertBefore(line.from, end);
-
-      // Add the text.
-      var node = makePartSpan(fixSpaces(line.text), this.container.ownerDocument);
-      self.container.insertBefore(node, end);
-      // See if the cursor was on this line. Put it back, adjusting
-      // for changed line length, if it was.
-      if (cursor && cursor.node == line.from) {
-        var cursordiff = 0;
-        var prev = this.after(line.from);
-        if (prev && i == chain.length - 1) {
-          // Only adjust if the cursor is after the unchanged part of
-          // the line.
-          for (var match = 0; match < cursor.offset &&
-               line.text.charAt(match) == prev.text.charAt(match); match++);
-          if (cursor.offset > match)
-            cursordiff = line.text.length - prev.text.length;
-        }
-        select.setCursorPos(this.container, {node: line.from, offset: Math.max(0, cursor.offset + cursordiff)});
-      }
-      // Cursor was in removed line, this is last new line.
-      else if (cursor && (i == chain.length - 1) && cursor.node && cursor.node.parentNode != this.container) {
-        select.setCursorPos(this.container, {node: line.from, offset: line.text.length});
-      }
-    }
-
-    // Anchor the chain in the DOM tree.
-    this.linkChain(chain);
-    return start;
-  }
-};
diff --git a/redakcja/static/js/lib/codemirror-0.8/util.js b/redakcja/static/js/lib/codemirror-0.8/util.js
deleted file mode 100644 (file)
index c7021c2..0000000
+++ /dev/null
@@ -1,130 +0,0 @@
-/* A few useful utility functions. */
-
-// Capture a method on an object.
-function method(obj, name) {
-  return function() {obj[name].apply(obj, arguments);};
-}
-
-// The value used to signal the end of a sequence in iterators.
-var StopIteration = {toString: function() {return "StopIteration"}};
-
-// Apply a function to each element in a sequence.
-function forEach(iter, f) {
-  if (iter.next) {
-    try {while (true) f(iter.next());}
-    catch (e) {if (e != StopIteration) throw e;}
-  }
-  else {
-    for (var i = 0; i < iter.length; i++)
-      f(iter[i]);
-  }
-}
-
-// Map a function over a sequence, producing an array of results.
-function map(iter, f) {
-  var accum = [];
-  forEach(iter, function(val) {accum.push(f(val));});
-  return accum;
-}
-
-// Create a predicate function that tests a string againsts a given
-// regular expression. No longer used but might be used by 3rd party
-// parsers.
-function matcher(regexp){
-  return function(value){return regexp.test(value);};
-}
-
-// Test whether a DOM node has a certain CSS class. Much faster than
-// the MochiKit equivalent, for some reason.
-function hasClass(element, className){
-  var classes = element.className;
-  return classes && new RegExp("(^| )" + className + "($| )").test(classes);
-}
-
-// Insert a DOM node after another node.
-function insertAfter(newNode, oldNode) {
-  var parent = oldNode.parentNode;
-  parent.insertBefore(newNode, oldNode.nextSibling);
-  return newNode;
-}
-
-function removeElement(node) {
-  if (node.parentNode)
-    node.parentNode.removeChild(node);
-}
-
-function clearElement(node) {
-  while (node.firstChild)
-    node.removeChild(node.firstChild);
-}
-
-// Check whether a node is contained in another one.
-function isAncestor(node, child) {
-  while (child = child.parentNode) {
-    if (node == child)
-      return true;
-  }
-  return false;
-}
-
-// The non-breaking space character.
-var nbsp = "\u00a0";
-var matching = {"{": "}", "[": "]", "(": ")",
-                "}": "{", "]": "[", ")": "("};
-
-// Standardize a few unportable event properties.
-function normalizeEvent(event) {
-  if (!event.stopPropagation) {
-    event.stopPropagation = function() {this.cancelBubble = true;};
-    event.preventDefault = function() {this.returnValue = false;};
-  }
-  if (!event.stop) {
-    event.stop = function() {
-      this.stopPropagation();
-      this.preventDefault();
-    };
-  }
-
-  if (event.type == "keypress") {
-    event.code = (event.charCode == null) ? event.keyCode : event.charCode;
-    event.character = String.fromCharCode(event.code);
-  }
-  return event;
-}
-
-// Portably register event handlers.
-function addEventHandler(node, type, handler, removeFunc) {
-  function wrapHandler(event) {
-    handler(normalizeEvent(event || window.event));
-  }
-  if (typeof node.addEventListener == "function") {
-    node.addEventListener(type, wrapHandler, false);
-    if (removeFunc) return function() {node.removeEventListener(type, wrapHandler, false);};
-  }
-  else {
-    node.attachEvent("on" + type, wrapHandler);
-    if (removeFunc) return function() {node.detachEvent("on" + type, wrapHandler);};
-  }
-}
-
-function nodeText(node) {
-  return node.textContent || node.innerText || node.nodeValue || "";
-}
-
-function nodeTop(node) {
-  var top = 0;
-  while (node.offsetParent) {
-    top += node.offsetTop;
-    node = node.offsetParent;
-  }
-  return top;
-}
-
-function isBR(node) {
-  var nn = node.nodeName;
-  return nn == "BR" || nn == "br";
-}
-function isSpan(node) {
-  var nn = node.nodeName;
-  return nn == "SPAN" || nn == "span";
-}
diff --git a/redakcja/static/js/lib/jquery-1.7.2.min.js b/redakcja/static/js/lib/jquery-1.7.2.min.js
deleted file mode 100644 (file)
index 16ad06c..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-/*! jQuery v1.7.2 jquery.com | jquery.org/license */
-(function(a,b){function cy(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cu(a){if(!cj[a]){var b=c.body,d=f("<"+a+">").appendTo(b),e=d.css("display");d.remove();if(e==="none"||e===""){ck||(ck=c.createElement("iframe"),ck.frameBorder=ck.width=ck.height=0),b.appendChild(ck);if(!cl||!ck.createElement)cl=(ck.contentWindow||ck.contentDocument).document,cl.write((f.support.boxModel?"<!doctype html>":"")+"<html><body>"),cl.close();d=cl.createElement(a),cl.body.appendChild(d),e=f.css(d,"display"),b.removeChild(ck)}cj[a]=e}return cj[a]}function ct(a,b){var c={};f.each(cp.concat.apply([],cp.slice(0,b)),function(){c[this]=a});return c}function cs(){cq=b}function cr(){setTimeout(cs,0);return cq=f.now()}function ci(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ch(){try{return new a.XMLHttpRequest}catch(b){}}function cb(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g<i;g++){if(g===1)for(h in a.converters)typeof h=="string"&&(e[h.toLowerCase()]=a.converters[h]);l=k,k=d[g];if(k==="*")k=l;else if(l!=="*"&&l!==k){m=l+" "+k,n=e[m]||e["* "+k];if(!n){p=b;for(o in e){j=o.split(" ");if(j[0]===l||j[0]==="*"){p=e[j[1]+" "+k];if(p){o=e[o],o===!0?n=p:p===!0&&(n=o);break}}}}!n&&!p&&f.error("No conversion from "+m.replace(" "," to ")),n!==!0&&(c=n?n(c):p(o(c)))}}return c}function ca(a,c,d){var e=a.contents,f=a.dataTypes,g=a.responseFields,h,i,j,k;for(i in g)i in d&&(c[g[i]]=d[i]);while(f[0]==="*")f.shift(),h===b&&(h=a.mimeType||c.getResponseHeader("content-type"));if(h)for(i in e)if(e[i]&&e[i].test(h)){f.unshift(i);break}if(f[0]in d)j=f[0];else{for(i in d){if(!f[0]||a.converters[i+" "+f[0]]){j=i;break}k||(k=i)}j=j||k}if(j){j!==f[0]&&f.unshift(j);return d[j]}}function b_(a,b,c,d){if(f.isArray(b))f.each(b,function(b,e){c||bD.test(a)?d(a,e):b_(a+"["+(typeof e=="object"?b:"")+"]",e,c,d)});else if(!c&&f.type(b)==="object")for(var e in b)b_(a+"["+e+"]",b[e],c,d);else d(a,b)}function b$(a,c){var d,e,g=f.ajaxSettings.flatOptions||{};for(d in c)c[d]!==b&&((g[d]?a:e||(e={}))[d]=c[d]);e&&f.extend(!0,a,e)}function bZ(a,c,d,e,f,g){f=f||c.dataTypes[0],g=g||{},g[f]=!0;var h=a[f],i=0,j=h?h.length:0,k=a===bS,l;for(;i<j&&(k||!l);i++)l=h[i](c,d,e),typeof l=="string"&&(!k||g[l]?l=b:(c.dataTypes.unshift(l),l=bZ(a,c,d,e,l,g)));(k||!l)&&!g["*"]&&(l=bZ(a,c,d,e,"*",g));return l}function bY(a){return function(b,c){typeof b!="string"&&(c=b,b="*");if(f.isFunction(c)){var d=b.toLowerCase().split(bO),e=0,g=d.length,h,i,j;for(;e<g;e++)h=d[e],j=/^\+/.test(h),j&&(h=h.substr(1)||"*"),i=a[h]=a[h]||[],i[j?"unshift":"push"](c)}}}function bB(a,b,c){var d=b==="width"?a.offsetWidth:a.offsetHeight,e=b==="width"?1:0,g=4;if(d>0){if(c!=="border")for(;e<g;e+=2)c||(d-=parseFloat(f.css(a,"padding"+bx[e]))||0),c==="margin"?d+=parseFloat(f.css(a,c+bx[e]))||0:d-=parseFloat(f.css(a,"border"+bx[e]+"Width"))||0;return d+"px"}d=by(a,b);if(d<0||d==null)d=a.style[b];if(bt.test(d))return d;d=parseFloat(d)||0;if(c)for(;e<g;e+=2)d+=parseFloat(f.css(a,"padding"+bx[e]))||0,c!=="padding"&&(d+=parseFloat(f.css(a,"border"+bx[e]+"Width"))||0),c==="margin"&&(d+=parseFloat(f.css(a,c+bx[e]))||0);return d+"px"}function bo(a){var b=c.createElement("div");bh.appendChild(b),b.innerHTML=a.outerHTML;return b.firstChild}function bn(a){var b=(a.nodeName||"").toLowerCase();b==="input"?bm(a):b!=="script"&&typeof a.getElementsByTagName!="undefined"&&f.grep(a.getElementsByTagName("input"),bm)}function bm(a){if(a.type==="checkbox"||a.type==="radio")a.defaultChecked=a.checked}function bl(a){return typeof a.getElementsByTagName!="undefined"?a.getElementsByTagName("*"):typeof a.querySelectorAll!="undefined"?a.querySelectorAll("*"):[]}function bk(a,b){var c;b.nodeType===1&&(b.clearAttributes&&b.clearAttributes(),b.mergeAttributes&&b.mergeAttributes(a),c=b.nodeName.toLowerCase(),c==="object"?b.outerHTML=a.outerHTML:c!=="input"||a.type!=="checkbox"&&a.type!=="radio"?c==="option"?b.selected=a.defaultSelected:c==="input"||c==="textarea"?b.defaultValue=a.defaultValue:c==="script"&&b.text!==a.text&&(b.text=a.text):(a.checked&&(b.defaultChecked=b.checked=a.checked),b.value!==a.value&&(b.value=a.value)),b.removeAttribute(f.expando),b.removeAttribute("_submit_attached"),b.removeAttribute("_change_attached"))}function bj(a,b){if(b.nodeType===1&&!!f.hasData(a)){var c,d,e,g=f._data(a),h=f._data(b,g),i=g.events;if(i){delete h.handle,h.events={};for(c in i)for(d=0,e=i[c].length;d<e;d++)f.event.add(b,c,i[c][d])}h.data&&(h.data=f.extend({},h.data))}}function bi(a,b){return f.nodeName(a,"table")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function U(a){var b=V.split("|"),c=a.createDocumentFragment();if(c.createElement)while(b.length)c.createElement(b.pop());return c}function T(a,b,c){b=b||0;if(f.isFunction(b))return f.grep(a,function(a,d){var e=!!b.call(a,d,a);return e===c});if(b.nodeType)return f.grep(a,function(a,d){return a===b===c});if(typeof b=="string"){var d=f.grep(a,function(a){return a.nodeType===1});if(O.test(b))return f.filter(b,d,!c);b=f.filter(b,d)}return f.grep(a,function(a,d){return f.inArray(a,b)>=0===c})}function S(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function K(){return!0}function J(){return!1}function n(a,b,c){var d=b+"defer",e=b+"queue",g=b+"mark",h=f._data(a,d);h&&(c==="queue"||!f._data(a,e))&&(c==="mark"||!f._data(a,g))&&setTimeout(function(){!f._data(a,e)&&!f._data(a,g)&&(f.removeData(a,d,!0),h.fire())},0)}function m(a){for(var b in a){if(b==="data"&&f.isEmptyObject(a[b]))continue;if(b!=="toJSON")return!1}return!0}function l(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(k,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNumeric(d)?+d:j.test(d)?f.parseJSON(d):d}catch(g){}f.data(a,c,d)}else d=b}return d}function h(a){var b=g[a]={},c,d;a=a.split(/\s+/);for(c=0,d=a.length;c<d;c++)b[a[c]]=!0;return b}var c=a.document,d=a.navigator,e=a.location,f=function(){function J(){if(!e.isReady){try{c.documentElement.doScroll("left")}catch(a){setTimeout(J,1);return}e.ready()}}var e=function(a,b){return new e.fn.init(a,b,h)},f=a.jQuery,g=a.$,h,i=/^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,n=/^[\],:{}\s]*$/,o=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,p=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,q=/(?:^|:|,)(?:\s*\[)+/g,r=/(webkit)[ \/]([\w.]+)/,s=/(opera)(?:.*version)?[ \/]([\w.]+)/,t=/(msie) ([\w.]+)/,u=/(mozilla)(?:.*? rv:([\w.]+))?/,v=/-([a-z]|[0-9])/ig,w=/^-ms-/,x=function(a,b){return(b+"").toUpperCase()},y=d.userAgent,z,A,B,C=Object.prototype.toString,D=Object.prototype.hasOwnProperty,E=Array.prototype.push,F=Array.prototype.slice,G=String.prototype.trim,H=Array.prototype.indexOf,I={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=m.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.7.2",length:0,size:function(){return this.length},toArray:function(){return F.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?E.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),A.add(a);return this},eq:function(a){a=+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(F.apply(this,arguments),"slice",F.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:E,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j<k;j++)if((a=arguments[j])!=null)for(c in a){d=i[c],f=a[c];if(i===f)continue;l&&f&&(e.isPlainObject(f)||(g=e.isArray(f)))?(g?(g=!1,h=d&&e.isArray(d)?d:[]):h=d&&e.isPlainObject(d)?d:{},i[c]=e.extend(l,h,f)):f!==b&&(i[c]=f)}return i},e.extend({noConflict:function(b){a.$===e&&(a.$=g),b&&a.jQuery===e&&(a.jQuery=f);return e},isReady:!1,readyWait:1,holdReady:function(a){a?e.readyWait++:e.ready(!0)},ready:function(a){if(a===!0&&!--e.readyWait||a!==!0&&!e.isReady){if(!c.body)return setTimeout(e.ready,1);e.isReady=!0;if(a!==!0&&--e.readyWait>0)return;A.fireWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").off("ready")}},bindReady:function(){if(!A){A=e.Callbacks("once memory");if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",B,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",B),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&J()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a!=null&&a==a.window},isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)},type:function(a){return a==null?String(a):I[C.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;try{if(a.constructor&&!D.call(a,"constructor")&&!D.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||D.call(a,d)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw new Error(a)},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(n.test(b.replace(o,"@").replace(p,"]").replace(q,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(c){if(typeof c!="string"||!c)return null;var d,f;try{a.DOMParser?(f=new DOMParser,d=f.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(g){d=b}(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&e.error("Invalid XML: "+c);return d},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(w,"ms-").replace(v,x)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g<h;)if(c.apply(a[g++],d)===!1)break}else if(i){for(f in a)if(c.call(a[f],f,a[f])===!1)break}else for(;g<h;)if(c.call(a[g],g,a[g++])===!1)break;return a},trim:G?function(a){return a==null?"":G.call(a)}:function(a){return a==null?"":(a+"").replace(k,"").replace(l,"")},makeArray:function(a,b){var c=b||[];if(a!=null){var d=e.type(a);a.length==null||d==="string"||d==="function"||d==="regexp"||e.isWindow(a)?E.call(c,a):e.merge(c,a)}return c},inArray:function(a,b,c){var d;if(b){if(H)return H.call(b,a,c);d=b.length,c=c?c<0?Math.max(0,d+c):c:0;for(;c<d;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,c){var d=a.length,e=0;if(typeof c.length=="number")for(var f=c.length;e<f;e++)a[d++]=c[e];else while(c[e]!==b)a[d++]=c[e++];a.length=d;return a},grep:function(a,b,c){var d=[],e;c=!!c;for(var f=0,g=a.length;f<g;f++)e=!!b(a[f],f),c!==e&&d.push(a[f]);return d},map:function(a,c,d){var f,g,h=[],i=0,j=a.length,k=a instanceof e||j!==b&&typeof j=="number"&&(j>0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i<j;i++)f=c(a[i],i,d),f!=null&&(h[h.length]=f);else for(g in a)f=c(a[g],g,d),f!=null&&(h[h.length]=f);return h.concat.apply([],h)},guid:1,proxy:function(a,c){if(typeof c=="string"){var d=a[c];c=a,a=d}if(!e.isFunction(a))return b;var f=F.call(arguments,2),g=function(){return a.apply(c,f.concat(F.call(arguments)))};g.guid=a.guid=a.guid||g.guid||e.guid++;return g},access:function(a,c,d,f,g,h,i){var j,k=d==null,l=0,m=a.length;if(d&&typeof d=="object"){for(l in d)e.access(a,c,l,d[l],1,h,f);g=1}else if(f!==b){j=i===b&&e.isFunction(f),k&&(j?(j=c,c=function(a,b,c){return j.call(e(a),c)}):(c.call(a,f),c=null));if(c)for(;l<m;l++)c(a[l],d,j?f.call(a[l],l,c(a[l],d)):f,i);g=1}return g?a:k?c.call(a):m?c(a[0],d):h},now:function(){return(new Date).getTime()},uaMatch:function(a){a=a.toLowerCase();var b=r.exec(a)||s.exec(a)||t.exec(a)||a.indexOf("compatible")<0&&u.exec(a)||[];return{browser:b[1]||"",version:b[2]||"0"}},sub:function(){function a(b,c){return new a.fn.init(b,c)}e.extend(!0,a,this),a.superclass=this,a.fn=a.prototype=this(),a.fn.constructor=a,a.sub=this.sub,a.fn.init=function(d,f){f&&f instanceof e&&!(f instanceof a)&&(f=a(f));return e.fn.init.call(this,d,f,b)},a.fn.init.prototype=a.fn;var b=a(c);return a},browser:{}}),e.each("Boolean Number String Function Array Date RegExp Object".split(" "),function(a,b){I["[object "+b+"]"]=b.toLowerCase()}),z=e.uaMatch(y),z.browser&&(e.browser[z.browser]=!0,e.browser.version=z.version),e.browser.webkit&&(e.browser.safari=!0),j.test(" ")&&(k=/^[\s\xA0]+/,l=/[\s\xA0]+$/),h=e(c),c.addEventListener?B=function(){c.removeEventListener("DOMContentLoaded",B,!1),e.ready()}:c.attachEvent&&(B=function(){c.readyState==="complete"&&(c.detachEvent("onreadystatechange",B),e.ready())});return e}(),g={};f.Callbacks=function(a){a=a?g[a]||h(a):{};var c=[],d=[],e,i,j,k,l,m,n=function(b){var d,e,g,h,i;for(d=0,e=b.length;d<e;d++)g=b[d],h=f.type(g),h==="array"?n(g):h==="function"&&(!a.unique||!p.has(g))&&c.push(g)},o=function(b,f){f=f||[],e=!a.memory||[b,f],i=!0,j=!0,m=k||0,k=0,l=c.length;for(;c&&m<l;m++)if(c[m].apply(b,f)===!1&&a.stopOnFalse){e=!0;break}j=!1,c&&(a.once?e===!0?p.disable():c=[]:d&&d.length&&(e=d.shift(),p.fireWith(e[0],e[1])))},p={add:function(){if(c){var a=c.length;n(arguments),j?l=c.length:e&&e!==!0&&(k=a,o(e[0],e[1]))}return this},remove:function(){if(c){var b=arguments,d=0,e=b.length;for(;d<e;d++)for(var f=0;f<c.length;f++)if(b[d]===c[f]){j&&f<=l&&(l--,f<=m&&m--),c.splice(f--,1);if(a.unique)break}}return this},has:function(a){if(c){var b=0,d=c.length;for(;b<d;b++)if(a===c[b])return!0}return!1},empty:function(){c=[];return this},disable:function(){c=d=e=b;return this},disabled:function(){return!c},lock:function(){d=b,(!e||e===!0)&&p.disable();return this},locked:function(){return!d},fireWith:function(b,c){d&&(j?a.once||d.push([b,c]):(!a.once||!e)&&o(b,c));return this},fire:function(){p.fireWith(this,arguments);return this},fired:function(){return!!i}};return p};var i=[].slice;f.extend({Deferred:function(a){var b=f.Callbacks("once memory"),c=f.Callbacks("once memory"),d=f.Callbacks("memory"),e="pending",g={resolve:b,reject:c,notify:d},h={done:b.add,fail:c.add,progress:d.add,state:function(){return e},isResolved:b.fired,isRejected:c.fired,then:function(a,b,c){i.done(a).fail(b).progress(c);return this},always:function(){i.done.apply(i,arguments).fail.apply(i,arguments);return this},pipe:function(a,b,c){return f.Deferred(function(d){f.each({done:[a,"resolve"],fail:[b,"reject"],progress:[c,"notify"]},function(a,b){var c=b[0],e=b[1],g;f.isFunction(c)?i[a](function(){g=c.apply(this,arguments),g&&f.isFunction(g.promise)?g.promise().then(d.resolve,d.reject,d.notify):d[e+"With"](this===i?d:this,[g])}):i[a](d[e])})}).promise()},promise:function(a){if(a==null)a=h;else for(var b in h)a[b]=h[b];return a}},i=h.promise({}),j;for(j in g)i[j]=g[j].fire,i[j+"With"]=g[j].fireWith;i.done(function(){e="resolved"},c.disable,d.lock).fail(function(){e="rejected"},b.disable,d.lock),a&&a.call(i,i);return i},when:function(a){function m(a){return function(b){e[a]=arguments.length>1?i.call(arguments,0):b,j.notifyWith(k,e)}}function l(a){return function(c){b[a]=arguments.length>1?i.call(arguments,0):c,--g||j.resolveWith(j,b)}}var b=i.call(arguments,0),c=0,d=b.length,e=Array(d),g=d,h=d,j=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred(),k=j.promise();if(d>1){for(;c<d;c++)b[c]&&b[c].promise&&f.isFunction(b[c].promise)?b[c].promise().then(l(c),j.reject,m(c)):--g;g||j.resolveWith(j,b)}else j!==a&&j.resolveWith(j,d?[a]:[]);return k}}),f.support=function(){var b,d,e,g,h,i,j,k,l,m,n,o,p=c.createElement("div"),q=c.documentElement;p.setAttribute("className","t"),p.innerHTML="   <link/><table></table><a href='/a' style='top:1px;float:left;opacity:.55;'>a</a><input type='checkbox'/>",d=p.getElementsByTagName("*"),e=p.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=p.getElementsByTagName("input")[0],b={leadingWhitespace:p.firstChild.nodeType===3,tbody:!p.getElementsByTagName("tbody").length,htmlSerialize:!!p.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,getSetAttribute:p.className!=="t",enctype:!!c.createElement("form").enctype,html5Clone:c.createElement("nav").cloneNode(!0).outerHTML!=="<:nav></:nav>",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,pixelMargin:!0},f.boxModel=b.boxModel=c.compatMode==="CSS1Compat",i.checked=!0,b.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,b.optDisabled=!h.disabled;try{delete p.test}catch(r){b.deleteExpando=!1}!p.addEventListener&&p.attachEvent&&p.fireEvent&&(p.attachEvent("onclick",function(){b.noCloneEvent=!1}),p.cloneNode(!0).fireEvent("onclick")),i=c.createElement("input"),i.value="t",i.setAttribute("type","radio"),b.radioValue=i.value==="t",i.setAttribute("checked","checked"),i.setAttribute("name","t"),p.appendChild(i),j=c.createDocumentFragment(),j.appendChild(p.lastChild),b.checkClone=j.cloneNode(!0).cloneNode(!0).lastChild.checked,b.appendChecked=i.checked,j.removeChild(i),j.appendChild(p);if(p.attachEvent)for(n in{submit:1,change:1,focusin:1})m="on"+n,o=m in p,o||(p.setAttribute(m,"return;"),o=typeof p[m]=="function"),b[n+"Bubbles"]=o;j.removeChild(p),j=g=h=p=i=null,f(function(){var d,e,g,h,i,j,l,m,n,q,r,s,t,u=c.getElementsByTagName("body")[0];!u||(m=1,t="padding:0;margin:0;border:",r="position:absolute;top:0;left:0;width:1px;height:1px;",s=t+"0;visibility:hidden;",n="style='"+r+t+"5px solid #000;",q="<div "+n+"display:block;'><div style='"+t+"0;display:block;overflow:hidden;'></div></div>"+"<table "+n+"' cellpadding='0' cellspacing='0'>"+"<tr><td></td></tr></table>",d=c.createElement("div"),d.style.cssText=s+"width:0;height:0;position:static;top:0;margin-top:"+m+"px",u.insertBefore(d,u.firstChild),p=c.createElement("div"),d.appendChild(p),p.innerHTML="<table><tr><td style='"+t+"0;display:none'></td><td>t</td></tr></table>",k=p.getElementsByTagName("td"),o=k[0].offsetHeight===0,k[0].style.display="",k[1].style.display="none",b.reliableHiddenOffsets=o&&k[0].offsetHeight===0,a.getComputedStyle&&(p.innerHTML="",l=c.createElement("div"),l.style.width="0",l.style.marginRight="0",p.style.width="2px",p.appendChild(l),b.reliableMarginRight=(parseInt((a.getComputedStyle(l,null)||{marginRight:0}).marginRight,10)||0)===0),typeof p.style.zoom!="undefined"&&(p.innerHTML="",p.style.width=p.style.padding="1px",p.style.border=0,p.style.overflow="hidden",p.style.display="inline",p.style.zoom=1,b.inlineBlockNeedsLayout=p.offsetWidth===3,p.style.display="block",p.style.overflow="visible",p.innerHTML="<div style='width:5px;'></div>",b.shrinkWrapBlocks=p.offsetWidth!==3),p.style.cssText=r+s,p.innerHTML=q,e=p.firstChild,g=e.firstChild,i=e.nextSibling.firstChild.firstChild,j={doesNotAddBorder:g.offsetTop!==5,doesAddBorderForTableAndCells:i.offsetTop===5},g.style.position="fixed",g.style.top="20px",j.fixedPosition=g.offsetTop===20||g.offsetTop===15,g.style.position=g.style.top="",e.style.overflow="hidden",e.style.position="relative",j.subtractsBorderForOverflowNotVisible=g.offsetTop===-5,j.doesNotIncludeMarginInBodyOffset=u.offsetTop!==m,a.getComputedStyle&&(p.style.marginTop="1%",b.pixelMargin=(a.getComputedStyle(p,null)||{marginTop:0}).marginTop!=="1%"),typeof d.style.zoom!="undefined"&&(d.style.zoom=1),u.removeChild(d),l=p=d=null,f.extend(b,j))});return b}();var j=/^(?:\{.*\}|\[.*\])$/,k=/([A-Z])/g;f.extend({cache:{},uuid:0,expando:"jQuery"+(f.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?f.cache[a[f.expando]]:a[f.expando];return!!a&&!m(a)},data:function(a,c,d,e){if(!!f.acceptData(a)){var g,h,i,j=f.expando,k=typeof c=="string",l=a.nodeType,m=l?f.cache:a,n=l?a[j]:a[j]&&j,o=c==="events";if((!n||!m[n]||!o&&!e&&!m[n].data)&&k&&d===b)return;n||(l?a[j]=n=++f.uuid:n=j),m[n]||(m[n]={},l||(m[n].toJSON=f.noop));if(typeof c=="object"||typeof c=="function")e?m[n]=f.extend(m[n],c):m[n].data=f.extend(m[n].data,c);g=h=m[n],e||(h.data||(h.data={}),h=h.data),d!==b&&(h[f.camelCase(c)]=d);if(o&&!h[c])return g.events;k?(i=h[c],i==null&&(i=h[f.camelCase(c)])):i=h;return i}},removeData:function(a,b,c){if(!!f.acceptData(a)){var d,e,g,h=f.expando,i=a.nodeType,j=i?f.cache:a,k=i?a[h]:h;if(!j[k])return;if(b){d=c?j[k]:j[k].data;if(d){f.isArray(b)||(b in d?b=[b]:(b=f.camelCase(b),b in d?b=[b]:b=b.split(" ")));for(e=0,g=b.length;e<g;e++)delete d[b[e]];if(!(c?m:f.isEmptyObject)(d))return}}if(!c){delete j[k].data;if(!m(j[k]))return}f.support.deleteExpando||!j.setInterval?delete j[k]:j[k]=null,i&&(f.support.deleteExpando?delete a[h]:a.removeAttribute?a.removeAttribute(h):a[h]=null)}},_data:function(a,b,c){return f.data(a,b,c,!0)},acceptData:function(a){if(a.nodeName){var b=f.noData[a.nodeName.toLowerCase()];if(b)return b!==!0&&a.getAttribute("classid")===b}return!0}}),f.fn.extend({data:function(a,c){var d,e,g,h,i,j=this[0],k=0,m=null;if(a===b){if(this.length){m=f.data(j);if(j.nodeType===1&&!f._data(j,"parsedAttrs")){g=j.attributes;for(i=g.length;k<i;k++)h=g[k].name,h.indexOf("data-")===0&&(h=f.camelCase(h.substring(5)),l(j,h,m[h]));f._data(j,"parsedAttrs",!0)}}return m}if(typeof a=="object")return this.each(function(){f.data(this,a)});d=a.split(".",2),d[1]=d[1]?"."+d[1]:"",e=d[1]+"!";return f.access(this,function(c){if(c===b){m=this.triggerHandler("getData"+e,[d[0]]),m===b&&j&&(m=f.data(j,a),m=l(j,a,m));return m===b&&d[1]?this.data(d[0]):m}d[1]=c,this.each(function(){var b=f(this);b.triggerHandler("setData"+e,d),f.data(this,a,c),b.triggerHandler("changeData"+e,d)})},null,c,arguments.length>1,null,!1)},removeData:function(a){return this.each(function(){f.removeData(this,a)})}}),f.extend({_mark:function(a,b){a&&(b=(b||"fx")+"mark",f._data(a,b,(f._data(a,b)||0)+1))},_unmark:function(a,b,c){a!==!0&&(c=b,b=a,a=!1);if(b){c=c||"fx";var d=c+"mark",e=a?0:(f._data(b,d)||1)-1;e?f._data(b,d,e):(f.removeData(b,d,!0),n(b,c,"mark"))}},queue:function(a,b,c){var d;if(a){b=(b||"fx")+"queue",d=f._data(a,b),c&&(!d||f.isArray(c)?d=f._data(a,b,f.makeArray(c)):d.push(c));return d||[]}},dequeue:function(a,b){b=b||"fx";var c=f.queue(a,b),d=c.shift(),e={};d==="inprogress"&&(d=c.shift()),d&&(b==="fx"&&c.unshift("inprogress"),f._data(a,b+".run",e),d.call(a,function(){f.dequeue(a,b)},e)),c.length||(f.removeData(a,b+"queue "+b+".run",!0),n(a,b,"queue"))}}),f.fn.extend({queue:function(a,c){var d=2;typeof a!="string"&&(c=a,a="fx",d--);if(arguments.length<d)return f.queue(this[0],a);return c===b?this:this.each(function(){var b=f.queue(this,a,c);a==="fx"&&b[0]!=="inprogress"&&f.dequeue(this,a)})},dequeue:function(a){return this.each(function(){f.dequeue(this,a)})},delay:function(a,b){a=f.fx?f.fx.speeds[a]||a:a,b=b||"fx";return this.queue(b,function(b,c){var d=setTimeout(b,a);c.stop=function(){clearTimeout(d)}})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,c){function m(){--h||d.resolveWith(e,[e])}typeof a!="string"&&(c=a,a=b),a=a||"fx";var d=f.Deferred(),e=this,g=e.length,h=1,i=a+"defer",j=a+"queue",k=a+"mark",l;while(g--)if(l=f.data(e[g],i,b,!0)||(f.data(e[g],j,b,!0)||f.data(e[g],k,b,!0))&&f.data(e[g],i,f.Callbacks("once memory"),!0))h++,l.add(m);m();return d.promise(c)}});var o=/[\n\t\r]/g,p=/\s+/,q=/\r/g,r=/^(?:button|input)$/i,s=/^(?:button|input|object|select|textarea)$/i,t=/^a(?:rea)?$/i,u=/^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,v=f.support.getSetAttribute,w,x,y;f.fn.extend({attr:function(a,b){return f.access(this,f.attr,a,b,arguments.length>1)},removeAttr:function(a){return this.each(function(){f.removeAttr(this,a)})},prop:function(a,b){return f.access(this,f.prop,a,b,arguments.length>1)},removeProp:function(a){a=f.propFix[a]||a;return this.each(function(){try{this[a]=b,delete this[a]}catch(c){}})},addClass:function(a){var b,c,d,e,g,h,i;if(f.isFunction(a))return this.each(function(b){f(this).addClass(a.call(this,b,this.className))});if(a&&typeof a=="string"){b=a.split(p);for(c=0,d=this.length;c<d;c++){e=this[c];if(e.nodeType===1)if(!e.className&&b.length===1)e.className=a;else{g=" "+e.className+" ";for(h=0,i=b.length;h<i;h++)~g.indexOf(" "+b[h]+" ")||(g+=b[h]+" ");e.className=f.trim(g)}}}return this},removeClass:function(a){var c,d,e,g,h,i,j;if(f.isFunction(a))return this.each(function(b){f(this).removeClass(a.call(this,b,this.className))});if(a&&typeof a=="string"||a===b){c=(a||"").split(p);for(d=0,e=this.length;d<e;d++){g=this[d];if(g.nodeType===1&&g.className)if(a){h=(" "+g.className+" ").replace(o," ");for(i=0,j=c.length;i<j;i++)h=h.replace(" "+c[i]+" "," ");g.className=f.trim(h)}else g.className=""}}return this},toggleClass:function(a,b){var c=typeof a,d=typeof b=="boolean";if(f.isFunction(a))return this.each(function(c){f(this).toggleClass(a.call(this,c,this.className,b),b)});return this.each(function(){if(c==="string"){var e,g=0,h=f(this),i=b,j=a.split(p);while(e=j[g++])i=d?i:!h.hasClass(e),h[i?"addClass":"removeClass"](e)}else if(c==="undefined"||c==="boolean")this.className&&f._data(this,"__className__",this.className),this.className=this.className||a===!1?"":f._data(this,"__className__")||""})},hasClass:function(a){var b=" "+a+" ",c=0,d=this.length;for(;c<d;c++)if(this[c].nodeType===1&&(" "+this[c].className+" ").replace(o," ").indexOf(b)>-1)return!0;return!1},val:function(a){var c,d,e,g=this[0];{if(!!arguments.length){e=f.isFunction(a);return this.each(function(d){var g=f(this),h;if(this.nodeType===1){e?h=a.call(this,d,g.val()):h=a,h==null?h="":typeof h=="number"?h+="":f.isArray(h)&&(h=f.map(h,function(a){return a==null?"":a+""})),c=f.valHooks[this.type]||f.valHooks[this.nodeName.toLowerCase()];if(!c||!("set"in c)||c.set(this,h,"value")===b)this.value=h}})}if(g){c=f.valHooks[g.type]||f.valHooks[g.nodeName.toLowerCase()];if(c&&"get"in c&&(d=c.get(g,"value"))!==b)return d;d=g.value;return typeof d=="string"?d.replace(q,""):d==null?"":d}}}}),f.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c,d,e,g=a.selectedIndex,h=[],i=a.options,j=a.type==="select-one";if(g<0)return null;c=j?g:0,d=j?g+1:i.length;for(;c<d;c++){e=i[c];if(e.selected&&(f.support.optDisabled?!e.disabled:e.getAttribute("disabled")===null)&&(!e.parentNode.disabled||!f.nodeName(e.parentNode,"optgroup"))){b=f(e).val();if(j)return b;h.push(b)}}if(j&&!h.length&&i.length)return f(i[g]).val();return h},set:function(a,b){var c=f.makeArray(b);f(a).find("option").each(function(){this.selected=f.inArray(f(this).val(),c)>=0}),c.length||(a.selectedIndex=-1);return c}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attr:function(a,c,d,e){var g,h,i,j=a.nodeType;if(!!a&&j!==3&&j!==8&&j!==2){if(e&&c in f.attrFn)return f(a)[c](d);if(typeof a.getAttribute=="undefined")return f.prop(a,c,d);i=j!==1||!f.isXMLDoc(a),i&&(c=c.toLowerCase(),h=f.attrHooks[c]||(u.test(c)?x:w));if(d!==b){if(d===null){f.removeAttr(a,c);return}if(h&&"set"in h&&i&&(g=h.set(a,d,c))!==b)return g;a.setAttribute(c,""+d);return d}if(h&&"get"in h&&i&&(g=h.get(a,c))!==null)return g;g=a.getAttribute(c);return g===null?b:g}},removeAttr:function(a,b){var c,d,e,g,h,i=0;if(b&&a.nodeType===1){d=b.toLowerCase().split(p),g=d.length;for(;i<g;i++)e=d[i],e&&(c=f.propFix[e]||e,h=u.test(e),h||f.attr(a,e,""),a.removeAttribute(v?e:c),h&&c in a&&(a[c]=!1))}},attrHooks:{type:{set:function(a,b){if(r.test(a.nodeName)&&a.parentNode)f.error("type property can't be changed");else if(!f.support.radioValue&&b==="radio"&&f.nodeName(a,"input")){var c=a.value;a.setAttribute("type",b),c&&(a.value=c);return b}}},value:{get:function(a,b){if(w&&f.nodeName(a,"button"))return w.get(a,b);return b in a?a.value:null},set:function(a,b,c){if(w&&f.nodeName(a,"button"))return w.set(a,b,c);a.value=b}}},propFix:{tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},prop:function(a,c,d){var e,g,h,i=a.nodeType;if(!!a&&i!==3&&i!==8&&i!==2){h=i!==1||!f.isXMLDoc(a),h&&(c=f.propFix[c]||c,g=f.propHooks[c]);return d!==b?g&&"set"in g&&(e=g.set(a,d,c))!==b?e:a[c]=d:g&&"get"in g&&(e=g.get(a,c))!==null?e:a[c]}},propHooks:{tabIndex:{get:function(a){var c=a.getAttributeNode("tabindex");return c&&c.specified?parseInt(c.value,10):s.test(a.nodeName)||t.test(a.nodeName)&&a.href?0:b}}}}),f.attrHooks.tabindex=f.propHooks.tabIndex,x={get:function(a,c){var d,e=f.prop(a,c);return e===!0||typeof e!="boolean"&&(d=a.getAttributeNode(c))&&d.nodeValue!==!1?c.toLowerCase():b},set:function(a,b,c){var d;b===!1?f.removeAttr(a,c):(d=f.propFix[c]||c,d in a&&(a[d]=!0),a.setAttribute(c,c.toLowerCase()));return c}},v||(y={name:!0,id:!0,coords:!0},w=f.valHooks.button={get:function(a,c){var d;d=a.getAttributeNode(c);return d&&(y[c]?d.nodeValue!=="":d.specified)?d.nodeValue:b},set:function(a,b,d){var e=a.getAttributeNode(d);e||(e=c.createAttribute(d),a.setAttributeNode(e));return e.nodeValue=b+""}},f.attrHooks.tabindex.set=w.set,f.each(["width","height"],function(a,b){f.attrHooks[b]=f.extend(f.attrHooks[b],{set:function(a,c){if(c===""){a.setAttribute(b,"auto");return c}}})}),f.attrHooks.contenteditable={get:w.get,set:function(a,b,c){b===""&&(b="false"),w.set(a,b,c)}}),f.support.hrefNormalized||f.each(["href","src","width","height"],function(a,c){f.attrHooks[c]=f.extend(f.attrHooks[c],{get:function(a){var d=a.getAttribute(c,2);return d===null?b:d}})}),f.support.style||(f.attrHooks.style={get:function(a){return a.style.cssText.toLowerCase()||b},set:function(a,b){return a.style.cssText=""+b}}),f.support.optSelected||(f.propHooks.selected=f.extend(f.propHooks.selected,{get:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex);return null}})),f.support.enctype||(f.propFix.enctype="encoding"),f.support.checkOn||f.each(["radio","checkbox"],function(){f.valHooks[this]={get:function(a){return a.getAttribute("value")===null?"on":a.value}}}),f.each(["radio","checkbox"],function(){f.valHooks[this]=f.extend(f.valHooks[this],{set:function(a,b){if(f.isArray(b))return a.checked=f.inArray(f(a).val(),b)>=0}})});var z=/^(?:textarea|input|select)$/i,A=/^([^\.]*)?(?:\.(.+))?$/,B=/(?:^|\s)hover(\.\S+)?\b/,C=/^key/,D=/^(?:mouse|contextmenu)|click/,E=/^(?:focusinfocus|focusoutblur)$/,F=/^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/,G=function(
-a){var b=F.exec(a);b&&(b[1]=(b[1]||"").toLowerCase(),b[3]=b[3]&&new RegExp("(?:^|\\s)"+b[3]+"(?:\\s|$)"));return b},H=function(a,b){var c=a.attributes||{};return(!b[1]||a.nodeName.toLowerCase()===b[1])&&(!b[2]||(c.id||{}).value===b[2])&&(!b[3]||b[3].test((c["class"]||{}).value))},I=function(a){return f.event.special.hover?a:a.replace(B,"mouseenter$1 mouseleave$1")};f.event={add:function(a,c,d,e,g){var h,i,j,k,l,m,n,o,p,q,r,s;if(!(a.nodeType===3||a.nodeType===8||!c||!d||!(h=f._data(a)))){d.handler&&(p=d,d=p.handler,g=p.selector),d.guid||(d.guid=f.guid++),j=h.events,j||(h.events=j={}),i=h.handle,i||(h.handle=i=function(a){return typeof f!="undefined"&&(!a||f.event.triggered!==a.type)?f.event.dispatch.apply(i.elem,arguments):b},i.elem=a),c=f.trim(I(c)).split(" ");for(k=0;k<c.length;k++){l=A.exec(c[k])||[],m=l[1],n=(l[2]||"").split(".").sort(),s=f.event.special[m]||{},m=(g?s.delegateType:s.bindType)||m,s=f.event.special[m]||{},o=f.extend({type:m,origType:l[1],data:e,handler:d,guid:d.guid,selector:g,quick:g&&G(g),namespace:n.join(".")},p),r=j[m];if(!r){r=j[m]=[],r.delegateCount=0;if(!s.setup||s.setup.call(a,e,n,i)===!1)a.addEventListener?a.addEventListener(m,i,!1):a.attachEvent&&a.attachEvent("on"+m,i)}s.add&&(s.add.call(a,o),o.handler.guid||(o.handler.guid=d.guid)),g?r.splice(r.delegateCount++,0,o):r.push(o),f.event.global[m]=!0}a=null}},global:{},remove:function(a,b,c,d,e){var g=f.hasData(a)&&f._data(a),h,i,j,k,l,m,n,o,p,q,r,s;if(!!g&&!!(o=g.events)){b=f.trim(I(b||"")).split(" ");for(h=0;h<b.length;h++){i=A.exec(b[h])||[],j=k=i[1],l=i[2];if(!j){for(j in o)f.event.remove(a,j+b[h],c,d,!0);continue}p=f.event.special[j]||{},j=(d?p.delegateType:p.bindType)||j,r=o[j]||[],m=r.length,l=l?new RegExp("(^|\\.)"+l.split(".").sort().join("\\.(?:.*\\.)?")+"(\\.|$)"):null;for(n=0;n<r.length;n++)s=r[n],(e||k===s.origType)&&(!c||c.guid===s.guid)&&(!l||l.test(s.namespace))&&(!d||d===s.selector||d==="**"&&s.selector)&&(r.splice(n--,1),s.selector&&r.delegateCount--,p.remove&&p.remove.call(a,s));r.length===0&&m!==r.length&&((!p.teardown||p.teardown.call(a,l)===!1)&&f.removeEvent(a,j,g.handle),delete o[j])}f.isEmptyObject(o)&&(q=g.handle,q&&(q.elem=null),f.removeData(a,["events","handle"],!0))}},customEvent:{getData:!0,setData:!0,changeData:!0},trigger:function(c,d,e,g){if(!e||e.nodeType!==3&&e.nodeType!==8){var h=c.type||c,i=[],j,k,l,m,n,o,p,q,r,s;if(E.test(h+f.event.triggered))return;h.indexOf("!")>=0&&(h=h.slice(0,-1),k=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i.shift(),i.sort());if((!e||f.event.customEvent[h])&&!f.event.global[h])return;c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.isTrigger=!0,c.exclusive=k,c.namespace=i.join("."),c.namespace_re=c.namespace?new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)"):null,o=h.indexOf(":")<0?"on"+h:"";if(!e){j=f.cache;for(l in j)j[l].events&&j[l].events[h]&&f.event.trigger(c,d,j[l].handle.elem,!0);return}c.result=b,c.target||(c.target=e),d=d!=null?f.makeArray(d):[],d.unshift(c),p=f.event.special[h]||{};if(p.trigger&&p.trigger.apply(e,d)===!1)return;r=[[e,p.bindType||h]];if(!g&&!p.noBubble&&!f.isWindow(e)){s=p.delegateType||h,m=E.test(s+h)?e:e.parentNode,n=null;for(;m;m=m.parentNode)r.push([m,s]),n=m;n&&n===e.ownerDocument&&r.push([n.defaultView||n.parentWindow||a,s])}for(l=0;l<r.length&&!c.isPropagationStopped();l++)m=r[l][0],c.type=r[l][1],q=(f._data(m,"events")||{})[c.type]&&f._data(m,"handle"),q&&q.apply(m,d),q=o&&m[o],q&&f.acceptData(m)&&q.apply(m,d)===!1&&c.preventDefault();c.type=h,!g&&!c.isDefaultPrevented()&&(!p._default||p._default.apply(e.ownerDocument,d)===!1)&&(h!=="click"||!f.nodeName(e,"a"))&&f.acceptData(e)&&o&&e[h]&&(h!=="focus"&&h!=="blur"||c.target.offsetWidth!==0)&&!f.isWindow(e)&&(n=e[o],n&&(e[o]=null),f.event.triggered=h,e[h](),f.event.triggered=b,n&&(e[o]=n));return c.result}},dispatch:function(c){c=f.event.fix(c||a.event);var d=(f._data(this,"events")||{})[c.type]||[],e=d.delegateCount,g=[].slice.call(arguments,0),h=!c.exclusive&&!c.namespace,i=f.event.special[c.type]||{},j=[],k,l,m,n,o,p,q,r,s,t,u;g[0]=c,c.delegateTarget=this;if(!i.preDispatch||i.preDispatch.call(this,c)!==!1){if(e&&(!c.button||c.type!=="click")){n=f(this),n.context=this.ownerDocument||this;for(m=c.target;m!=this;m=m.parentNode||this)if(m.disabled!==!0){p={},r=[],n[0]=m;for(k=0;k<e;k++)s=d[k],t=s.selector,p[t]===b&&(p[t]=s.quick?H(m,s.quick):n.is(t)),p[t]&&r.push(s);r.length&&j.push({elem:m,matches:r})}}d.length>e&&j.push({elem:this,matches:d.slice(e)});for(k=0;k<j.length&&!c.isPropagationStopped();k++){q=j[k],c.currentTarget=q.elem;for(l=0;l<q.matches.length&&!c.isImmediatePropagationStopped();l++){s=q.matches[l];if(h||!c.namespace&&!s.namespace||c.namespace_re&&c.namespace_re.test(s.namespace))c.data=s.data,c.handleObj=s,o=((f.event.special[s.origType]||{}).handle||s.handler).apply(q.elem,g),o!==b&&(c.result=o,o===!1&&(c.preventDefault(),c.stopPropagation()))}}i.postDispatch&&i.postDispatch.call(this,c);return c.result}},props:"attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(a,b){a.which==null&&(a.which=b.charCode!=null?b.charCode:b.keyCode);return a}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(a,d){var e,f,g,h=d.button,i=d.fromElement;a.pageX==null&&d.clientX!=null&&(e=a.target.ownerDocument||c,f=e.documentElement,g=e.body,a.pageX=d.clientX+(f&&f.scrollLeft||g&&g.scrollLeft||0)-(f&&f.clientLeft||g&&g.clientLeft||0),a.pageY=d.clientY+(f&&f.scrollTop||g&&g.scrollTop||0)-(f&&f.clientTop||g&&g.clientTop||0)),!a.relatedTarget&&i&&(a.relatedTarget=i===a.target?d.toElement:i),!a.which&&h!==b&&(a.which=h&1?1:h&2?3:h&4?2:0);return a}},fix:function(a){if(a[f.expando])return a;var d,e,g=a,h=f.event.fixHooks[a.type]||{},i=h.props?this.props.concat(h.props):this.props;a=f.Event(g);for(d=i.length;d;)e=i[--d],a[e]=g[e];a.target||(a.target=g.srcElement||c),a.target.nodeType===3&&(a.target=a.target.parentNode),a.metaKey===b&&(a.metaKey=a.ctrlKey);return h.filter?h.filter(a,g):a},special:{ready:{setup:f.bindReady},load:{noBubble:!0},focus:{delegateType:"focusin"},blur:{delegateType:"focusout"},beforeunload:{setup:function(a,b,c){f.isWindow(this)&&(this.onbeforeunload=c)},teardown:function(a,b){this.onbeforeunload===b&&(this.onbeforeunload=null)}}},simulate:function(a,b,c,d){var e=f.extend(new f.Event,c,{type:a,isSimulated:!0,originalEvent:{}});d?f.event.trigger(e,null,b):f.event.dispatch.call(b,e),e.isDefaultPrevented()&&c.preventDefault()}},f.event.handle=f.event.dispatch,f.removeEvent=c.removeEventListener?function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c,!1)}:function(a,b,c){a.detachEvent&&a.detachEvent("on"+b,c)},f.Event=function(a,b){if(!(this instanceof f.Event))return new f.Event(a,b);a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||a.returnValue===!1||a.getPreventDefault&&a.getPreventDefault()?K:J):this.type=a,b&&f.extend(this,b),this.timeStamp=a&&a.timeStamp||f.now(),this[f.expando]=!0},f.Event.prototype={preventDefault:function(){this.isDefaultPrevented=K;var a=this.originalEvent;!a||(a.preventDefault?a.preventDefault():a.returnValue=!1)},stopPropagation:function(){this.isPropagationStopped=K;var a=this.originalEvent;!a||(a.stopPropagation&&a.stopPropagation(),a.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=K,this.stopPropagation()},isDefaultPrevented:J,isPropagationStopped:J,isImmediatePropagationStopped:J},f.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){f.event.special[a]={delegateType:b,bindType:b,handle:function(a){var c=this,d=a.relatedTarget,e=a.handleObj,g=e.selector,h;if(!d||d!==c&&!f.contains(c,d))a.type=e.origType,h=e.handler.apply(this,arguments),a.type=b;return h}}}),f.support.submitBubbles||(f.event.special.submit={setup:function(){if(f.nodeName(this,"form"))return!1;f.event.add(this,"click._submit keypress._submit",function(a){var c=a.target,d=f.nodeName(c,"input")||f.nodeName(c,"button")?c.form:b;d&&!d._submit_attached&&(f.event.add(d,"submit._submit",function(a){a._submit_bubble=!0}),d._submit_attached=!0)})},postDispatch:function(a){a._submit_bubble&&(delete a._submit_bubble,this.parentNode&&!a.isTrigger&&f.event.simulate("submit",this.parentNode,a,!0))},teardown:function(){if(f.nodeName(this,"form"))return!1;f.event.remove(this,"._submit")}}),f.support.changeBubbles||(f.event.special.change={setup:function(){if(z.test(this.nodeName)){if(this.type==="checkbox"||this.type==="radio")f.event.add(this,"propertychange._change",function(a){a.originalEvent.propertyName==="checked"&&(this._just_changed=!0)}),f.event.add(this,"click._change",function(a){this._just_changed&&!a.isTrigger&&(this._just_changed=!1,f.event.simulate("change",this,a,!0))});return!1}f.event.add(this,"beforeactivate._change",function(a){var b=a.target;z.test(b.nodeName)&&!b._change_attached&&(f.event.add(b,"change._change",function(a){this.parentNode&&!a.isSimulated&&!a.isTrigger&&f.event.simulate("change",this.parentNode,a,!0)}),b._change_attached=!0)})},handle:function(a){var b=a.target;if(this!==b||a.isSimulated||a.isTrigger||b.type!=="radio"&&b.type!=="checkbox")return a.handleObj.handler.apply(this,arguments)},teardown:function(){f.event.remove(this,"._change");return z.test(this.nodeName)}}),f.support.focusinBubbles||f.each({focus:"focusin",blur:"focusout"},function(a,b){var d=0,e=function(a){f.event.simulate(b,a.target,f.event.fix(a),!0)};f.event.special[b]={setup:function(){d++===0&&c.addEventListener(a,e,!0)},teardown:function(){--d===0&&c.removeEventListener(a,e,!0)}}}),f.fn.extend({on:function(a,c,d,e,g){var h,i;if(typeof a=="object"){typeof c!="string"&&(d=d||c,c=b);for(i in a)this.on(i,c,d,a[i],g);return this}d==null&&e==null?(e=c,d=c=b):e==null&&(typeof c=="string"?(e=d,d=b):(e=d,d=c,c=b));if(e===!1)e=J;else if(!e)return this;g===1&&(h=e,e=function(a){f().off(a);return h.apply(this,arguments)},e.guid=h.guid||(h.guid=f.guid++));return this.each(function(){f.event.add(this,a,e,d,c)})},one:function(a,b,c,d){return this.on(a,b,c,d,1)},off:function(a,c,d){if(a&&a.preventDefault&&a.handleObj){var e=a.handleObj;f(a.delegateTarget).off(e.namespace?e.origType+"."+e.namespace:e.origType,e.selector,e.handler);return this}if(typeof a=="object"){for(var g in a)this.off(g,c,a[g]);return this}if(c===!1||typeof c=="function")d=c,c=b;d===!1&&(d=J);return this.each(function(){f.event.remove(this,a,d,c)})},bind:function(a,b,c){return this.on(a,null,b,c)},unbind:function(a,b){return this.off(a,null,b)},live:function(a,b,c){f(this.context).on(a,this.selector,b,c);return this},die:function(a,b){f(this.context).off(a,this.selector||"**",b);return this},delegate:function(a,b,c,d){return this.on(b,a,c,d)},undelegate:function(a,b,c){return arguments.length==1?this.off(a,"**"):this.off(b,a,c)},trigger:function(a,b){return this.each(function(){f.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0])return f.event.trigger(a,b,this[0],!0)},toggle:function(a){var b=arguments,c=a.guid||f.guid++,d=0,e=function(c){var e=(f._data(this,"lastToggle"+a.guid)||0)%d;f._data(this,"lastToggle"+a.guid,e+1),c.preventDefault();return b[e].apply(this,arguments)||!1};e.guid=c;while(d<b.length)b[d++].guid=c;return this.click(e)},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}}),f.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 contextmenu".split(" "),function(a,b){f.fn[b]=function(a,c){c==null&&(c=a,a=null);return arguments.length>0?this.on(b,null,a,c):this.trigger(b)},f.attrFn&&(f.attrFn[b]=!0),C.test(b)&&(f.event.fixHooks[b]=f.event.keyHooks),D.test(b)&&(f.event.fixHooks[b]=f.event.mouseHooks)}),function(){function x(a,b,c,e,f,g){for(var h=0,i=e.length;h<i;h++){var j=e[h];if(j){var k=!1;j=j[a];while(j){if(j[d]===c){k=e[j.sizset];break}if(j.nodeType===1){g||(j[d]=c,j.sizset=h);if(typeof b!="string"){if(j===b){k=!0;break}}else if(m.filter(b,[j]).length>0){k=j;break}}j=j[a]}e[h]=k}}}function w(a,b,c,e,f,g){for(var h=0,i=e.length;h<i;h++){var j=e[h];if(j){var k=!1;j=j[a];while(j){if(j[d]===c){k=e[j.sizset];break}j.nodeType===1&&!g&&(j[d]=c,j.sizset=h);if(j.nodeName.toLowerCase()===b){k=j;break}j=j[a]}e[h]=k}}}var a=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,d="sizcache"+(Math.random()+"").replace(".",""),e=0,g=Object.prototype.toString,h=!1,i=!0,j=/\\/g,k=/\r\n/g,l=/\W/;[0,0].sort(function(){i=!1;return 0});var m=function(b,d,e,f){e=e||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!="string")return e;var i,j,k,l,n,q,r,t,u=!0,v=m.isXML(d),w=[],x=b;do{a.exec(""),i=a.exec(x);if(i){x=i[3],w.push(i[1]);if(i[2]){l=i[3];break}}}while(i);if(w.length>1&&p.exec(b))if(w.length===2&&o.relative[w[0]])j=y(w[0]+w[1],d,f);else{j=o.relative[w[0]]?[d]:m(w.shift(),d);while(w.length)b=w.shift(),o.relative[b]&&(b+=w.shift()),j=y(b,j,f)}else{!f&&w.length>1&&d.nodeType===9&&!v&&o.match.ID.test(w[0])&&!o.match.ID.test(w[w.length-1])&&(n=m.find(w.shift(),d,v),d=n.expr?m.filter(n.expr,n.set)[0]:n.set[0]);if(d){n=f?{expr:w.pop(),set:s(f)}:m.find(w.pop(),w.length===1&&(w[0]==="~"||w[0]==="+")&&d.parentNode?d.parentNode:d,v),j=n.expr?m.filter(n.expr,n.set):n.set,w.length>0?k=s(j):u=!1;while(w.length)q=w.pop(),r=q,o.relative[q]?r=w.pop():q="",r==null&&(r=d),o.relative[q](k,r,v)}else k=w=[]}k||(k=j),k||m.error(q||b);if(g.call(k)==="[object Array]")if(!u)e.push.apply(e,k);else if(d&&d.nodeType===1)for(t=0;k[t]!=null;t++)k[t]&&(k[t]===!0||k[t].nodeType===1&&m.contains(d,k[t]))&&e.push(j[t]);else for(t=0;k[t]!=null;t++)k[t]&&k[t].nodeType===1&&e.push(j[t]);else s(k,e);l&&(m(l,h,e,f),m.uniqueSort(e));return e};m.uniqueSort=function(a){if(u){h=i,a.sort(u);if(h)for(var b=1;b<a.length;b++)a[b]===a[b-1]&&a.splice(b--,1)}return a},m.matches=function(a,b){return m(a,null,null,b)},m.matchesSelector=function(a,b){return m(b,null,null,[a]).length>0},m.find=function(a,b,c){var d,e,f,g,h,i;if(!a)return[];for(e=0,f=o.order.length;e<f;e++){h=o.order[e];if(g=o.leftMatch[h].exec(a)){i=g[1],g.splice(1,1);if(i.substr(i.length-1)!=="\\"){g[1]=(g[1]||"").replace(j,""),d=o.find[h](g,b,c);if(d!=null){a=a.replace(o.match[h],"");break}}}}d||(d=typeof b.getElementsByTagName!="undefined"?b.getElementsByTagName("*"):[]);return{set:d,expr:a}},m.filter=function(a,c,d,e){var f,g,h,i,j,k,l,n,p,q=a,r=[],s=c,t=c&&c[0]&&m.isXML(c[0]);while(a&&c.length){for(h in o.filter)if((f=o.leftMatch[h].exec(a))!=null&&f[2]){k=o.filter[h],l=f[1],g=!1,f.splice(1,1);if(l.substr(l.length-1)==="\\")continue;s===r&&(r=[]);if(o.preFilter[h]){f=o.preFilter[h](f,s,d,r,e,t);if(!f)g=i=!0;else if(f===!0)continue}if(f)for(n=0;(j=s[n])!=null;n++)j&&(i=k(j,f,n,s),p=e^i,d&&i!=null?p?g=!0:s[n]=!1:p&&(r.push(j),g=!0));if(i!==b){d||(s=r),a=a.replace(o.match[h],"");if(!g)return[];break}}if(a===q)if(g==null)m.error(a);else break;q=a}return s},m.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)};var n=m.getText=function(a){var b,c,d=a.nodeType,e="";if(d){if(d===1||d===9||d===11){if(typeof a.textContent=="string")return a.textContent;if(typeof a.innerText=="string")return a.innerText.replace(k,"");for(a=a.firstChild;a;a=a.nextSibling)e+=n(a)}else if(d===3||d===4)return a.nodeValue}else for(b=0;c=a[b];b++)c.nodeType!==8&&(e+=n(c));return e},o=m.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|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(a){return a.getAttribute("href")},type:function(a){return a.getAttribute("type")}},relative:{"+":function(a,b){var c=typeof b=="string",d=c&&!l.test(b),e=c&&!d;d&&(b=b.toLowerCase());for(var f=0,g=a.length,h;f<g;f++)if(h=a[f]){while((h=h.previousSibling)&&h.nodeType!==1);a[f]=e||h&&h.nodeName.toLowerCase()===b?h||!1:h===b}e&&m.filter(b,a,!0)},">":function(a,b){var c,d=typeof b=="string",e=0,f=a.length;if(d&&!l.test(b)){b=b.toLowerCase();for(;e<f;e++){c=a[e];if(c){var g=c.parentNode;a[e]=g.nodeName.toLowerCase()===b?g:!1}}}else{for(;e<f;e++)c=a[e],c&&(a[e]=d?c.parentNode:c.parentNode===b);d&&m.filter(b,a,!0)}},"":function(a,b,c){var d,f=e++,g=x;typeof b=="string"&&!l.test(b)&&(b=b.toLowerCase(),d=b,g=w),g("parentNode",b,f,a,d,c)},"~":function(a,b,c){var d,f=e++,g=x;typeof b=="string"&&!l.test(b)&&(b=b.toLowerCase(),d=b,g=w),g("previousSibling",b,f,a,d,c)}},find:{ID:function(a,b,c){if(typeof b.getElementById!="undefined"&&!c){var d=b.getElementById(a[1]);return d&&d.parentNode?[d]:[]}},NAME:function(a,b){if(typeof b.getElementsByName!="undefined"){var c=[],d=b.getElementsByName(a[1]);for(var e=0,f=d.length;e<f;e++)d[e].getAttribute("name")===a[1]&&c.push(d[e]);return c.length===0?null:c}},TAG:function(a,b){if(typeof b.getElementsByTagName!="undefined")return b.getElementsByTagName(a[1])}},preFilter:{CLASS:function(a,b,c,d,e,f){a=" "+a[1].replace(j,"")+" ";if(f)return a;for(var g=0,h;(h=b[g])!=null;g++)h&&(e^(h.className&&(" "+h.className+" ").replace(/[\t\n\r]/g," ").indexOf(a)>=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(j,"")},TAG:function(a,b){return a[1].replace(j,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||m.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&m.error(a[0]);a[0]=e++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(j,"");!f&&o.attrMap[g]&&(a[1]=o.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(j,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=m(b[3],null,null,c);else{var g=m.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(o.match.POS.test(b[0])||o.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!m(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return a.nodeName.toLowerCase()==="input"&&"text"===c&&(b===c||b===null)},radio:function(a){return a.nodeName.toLowerCase()==="input"&&"radio"===a.type},checkbox:function(a){return a.nodeName.toLowerCase()==="input"&&"checkbox"===a.type},file:function(a){return a.nodeName.toLowerCase()==="input"&&"file"===a.type},password:function(a){return a.nodeName.toLowerCase()==="input"&&"password"===a.type},submit:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"submit"===a.type},image:function(a){return a.nodeName.toLowerCase()==="input"&&"image"===a.type},reset:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"reset"===a.type},button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&"button"===a.type||b==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)},focus:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return b<c[3]-0},gt:function(a,b,c){return b>c[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=o.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||n([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h<i;h++)if(g[h]===a)return!1;return!0}m.error(e)},CHILD:function(a,b){var c,e,f,g,h,i,j,k=b[1],l=a;switch(k){case"only":case"first":while(l=l.previousSibling)if(l.nodeType===1)return!1;if(k==="first")return!0;l=a;case"last":while(l=l.nextSibling)if(l.nodeType===1)return!1;return!0;case"nth":c=b[2],e=b[3];if(c===1&&e===0)return!0;f=b[0],g=a.parentNode;if(g&&(g[d]!==f||!a.nodeIndex)){i=0;for(l=g.firstChild;l;l=l.nextSibling)l.nodeType===1&&(l.nodeIndex=++i);g[d]=f}j=a.nodeIndex-e;return c===0?j===0:j%c===0&&j/c>=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||!!a.nodeName&&a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=m.attr?m.attr(a,c):o.attrHandle[c]?o.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":!f&&m.attr?d!=null:f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=o.setFilters[e];if(f)return f(a,c,b,d)}}},p=o.match.POS,q=function(a,b){return"\\"+(b-0+1)};for(var r in o.match)o.match[r]=new RegExp(o.match[r].source+/(?![^\[]*\])(?![^\(]*\))/.source),o.leftMatch[r]=new RegExp(/(^(?:.|\r|\n)*?)/.source+o.match[r].source.replace(/\\(\d+)/g,q));o.match.globalPOS=p;var s=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(t){s=function(a,b){var c=0,d=b||[];if(g.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length=="number")for(var e=a.length;c<e;c++)d.push(a[c]);else for(;a[c];c++)d.push(a[c]);return d}}var u,v;c.documentElement.compareDocumentPosition?u=function(a,b){if(a===b){h=!0;return 0}if(!a.compareDocumentPosition||!b.compareDocumentPosition)return a.compareDocumentPosition?-1:1;return a.compareDocumentPosition(b)&4?-1:1}:(u=function(a,b){if(a===b){h=!0;return 0}if(a.sourceIndex&&b.sourceIndex)return a.sourceIndex-b.sourceIndex;var c,d,e=[],f=[],g=a.parentNode,i=b.parentNode,j=g;if(g===i)return v(a,b);if(!g)return-1;if(!i)return 1;while(j)e.unshift(j),j=j.parentNode;j=i;while(j)f.unshift(j),j=j.parentNode;c=e.length,d=f.length;for(var k=0;k<c&&k<d;k++)if(e[k]!==f[k])return v(e[k],f[k]);return k===c?v(a,f[k],-1):v(e[k],b,1)},v=function(a,b,c){if(a===b)return c;var d=a.nextSibling;while(d){if(d===b)return-1;d=d.nextSibling}return 1}),function(){var a=c.createElement("div"),d="script"+(new Date).getTime(),e=c.documentElement;a.innerHTML="<a name='"+d+"'/>",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(o.find.ID=function(a,c,d){if(typeof c.getElementById!="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},o.filter.ID=function(a,b){var c=typeof a.getAttributeNode!="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(o.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="<a href='#'></a>",a.firstChild&&typeof a.firstChild.getAttribute!="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(o.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=m,b=c.createElement("div"),d="__sizzle__";b.innerHTML="<p class='TEST'></p>";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){m=function(b,e,f,g){e=e||c;if(!g&&!m.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return s(e.getElementsByTagName(b),f);if(h[2]&&o.find.CLASS&&e.getElementsByClassName)return s(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return s([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return s([],f);if(i.id===h[3])return s([i],f)}try{return s(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var k=e,l=e.getAttribute("id"),n=l||d,p=e.parentNode,q=/^\s*[+~]/.test(b);l?n=n.replace(/'/g,"\\$&"):e.setAttribute("id",n),q&&p&&(e=e.parentNode);try{if(!q||p)return s(e.querySelectorAll("[id='"+n+"'] "+b),f)}catch(r){}finally{l||k.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)m[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}m.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!m.isXML(a))try{if(e||!o.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return m(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="<div class='test e'></div><div class='test'></div>";if(!!a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;o.order.splice(1,0,"CLASS"),o.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?m.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?m.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:m.contains=function(){return!1},m.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var y=function(a,b,c){var d,e=[],f="",g=b.nodeType?[b]:b;while(d=o.match.PSEUDO.exec(a))f+=d[0],a=a.replace(o.match.PSEUDO,"");a=o.relative[a]?a+"*":a;for(var h=0,i=g.length;h<i;h++)m(a,g[h],e,c);return m.filter(f,e)};m.attr=f.attr,m.selectors.attrMap={},f.find=m,f.expr=m.selectors,f.expr[":"]=f.expr.filters,f.unique=m.uniqueSort,f.text=m.getText,f.isXMLDoc=m.isXML,f.contains=m.contains}();var L=/Until$/,M=/^(?:parents|prevUntil|prevAll)/,N=/,/,O=/^.[^:#\[\.,]*$/,P=Array.prototype.slice,Q=f.expr.match.globalPOS,R={children:!0,contents:!0,next:!0,prev:!0};f.fn.extend({find:function(a){var b=this,c,d;if(typeof a!="string")return f(a).filter(function(){for(c=0,d=b.length;c<d;c++)if(f.contains(b[c],this))return!0});var e=this.pushStack("","find",a),g,h,i;for(c=0,d=this.length;c<d;c++){g=e.length,f.find(a,this[c],e);if(c>0)for(h=g;h<e.length;h++)for(i=0;i<g;i++)if(e[i]===e[h]){e.splice(h--,1);break}}return e},has:function(a){var b=f(a);return this.filter(function(){for(var a=0,c=b.length;a<c;a++)if(f.contains(this,b[a]))return!0})},not:function(a){return this.pushStack(T(this,a,!1),"not",a)},filter:function(a){return this.pushStack(T(this,a,!0),"filter",a)},is:function(a){return!!a&&(typeof a=="string"?Q.test(a)?f(a,this.context).index(this[0])>=0:f.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c=[],d,e,g=this[0];if(f.isArray(a)){var h=1;while(g&&g.ownerDocument&&g!==b){for(d=0;d<a.length;d++)f(g).is(a[d])&&c.push({selector:a[d],elem:g,level:h});g=g.parentNode,h++}return c}var i=Q.test(a)||typeof a!="string"?f(a,b||this.context):0;for(d=0,e=this.length;d<e;d++){g=this[d];while(g){if(i?i.index(g)>-1:f.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b||g.nodeType===11)break}}c=c.length>1?f.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a)return this[0]&&this[0].parentNode?this.prevAll().length:-1;if(typeof a=="string")return f.inArray(this[0],f(a));return f.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a=="string"?f(a,b):f.makeArray(a&&a.nodeType?[a]:a),d=f.merge(this.get(),c);return this.pushStack(S(c[0])||S(d[0])?d:f.unique(d))},andSelf:function(){return this.add(this.prevObject)}}),f.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return f.dir(a,"parentNode")},parentsUntil:function(a,b,c){return f.dir(a,"parentNode",c)},next:function(a){return f.nth(a,2,"nextSibling")},prev:function(a){return f.nth(a,2,"previousSibling")},nextAll:function(a){return f.dir(a,"nextSibling")},prevAll:function(a){return f.dir(a,"previousSibling")},nextUntil:function(a,b,c){return f.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return f.dir(a,"previousSibling",c)},siblings:function(a){return f.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return f.sibling(a.firstChild)},contents:function(a){return f.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:f.makeArray(a.childNodes)}},function(a,b){f.fn[a]=function(c,d){var e=f.map(this,b,c);L.test(a)||(d=c),d&&typeof d=="string"&&(e=f.filter(d,e)),e=this.length>1&&!R[a]?f.unique(e):e,(this.length>1||N.test(d))&&M.test(a)&&(e=e.reverse());return this.pushStack(e,a,P.call(arguments).join(","))}}),f.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?f.find.matchesSelector(b[0],a)?[b[0]]:[]:f.find.matches(a,b)},dir:function(a,c,d){var e=[],g=a[c];while(g&&g.nodeType!==9&&(d===b||g.nodeType!==1||!f(g).is(d)))g.nodeType===1&&e.push(g),g=g[c];return e},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var V="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",W=/ jQuery\d+="(?:\d+|null)"/g,X=/^\s+/,Y=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,Z=/<([\w:]+)/,$=/<tbody/i,_=/<|&#?\w+;/,ba=/<(?:script|style)/i,bb=/<(?:script|object|embed|option|style)/i,bc=new RegExp("<(?:"+V+")[\\s/>]","i"),bd=/checked\s*(?:[^=]|=\s*.checked.)/i,be=/\/(java|ecma)script/i,bf=/^\s*<!(?:\[CDATA\[|\-\-)/,bg={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,"",""]},bh=U(c);bg.optgroup=bg.option,bg.tbody=bg.tfoot=bg.colgroup=bg.caption=bg.thead,bg.th=bg.td,f.support.htmlSerialize||(bg._default=[1,"div<div>","</div>"]),f.fn.extend({text:function(a){return f.access(this,function(a){return a===b?f.text(this):this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a))},null,a,arguments.length)},wrapAll:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapAll(a.call(this,b))});if(this[0]){var b=f(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapInner(a.call(this,b))});return this.each(function(){var b=f(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=f.isFunction(a);return this.each(function(c){f(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){f.nodeName(this,"body")||f(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=f
-.clean(arguments);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,f.clean(arguments));return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++)if(!a||f.filter(a,[d]).length)!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagName("*")),f.cleanData([d])),d.parentNode&&d.parentNode.removeChild(d);return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&f.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return f.clone(this,a,b)})},html:function(a){return f.access(this,function(a){var c=this[0]||{},d=0,e=this.length;if(a===b)return c.nodeType===1?c.innerHTML.replace(W,""):null;if(typeof a=="string"&&!ba.test(a)&&(f.support.leadingWhitespace||!X.test(a))&&!bg[(Z.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Y,"<$1></$2>");try{for(;d<e;d++)c=this[d]||{},c.nodeType===1&&(f.cleanData(c.getElementsByTagName("*")),c.innerHTML=a);c=0}catch(g){}}c&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(a){if(this[0]&&this[0].parentNode){if(f.isFunction(a))return this.each(function(b){var c=f(this),d=c.html();c.replaceWith(a.call(this,b,d))});typeof a!="string"&&(a=f(a).detach());return this.each(function(){var b=this.nextSibling,c=this.parentNode;f(this).remove(),b?f(b).before(a):f(c).append(a)})}return this.length?this.pushStack(f(f.isFunction(a)?a():a),"replaceWith",a):this},detach:function(a){return this.remove(a,!0)},domManip:function(a,c,d){var e,g,h,i,j=a[0],k=[];if(!f.support.checkClone&&arguments.length===3&&typeof j=="string"&&bd.test(j))return this.each(function(){f(this).domManip(a,c,d,!0)});if(f.isFunction(j))return this.each(function(e){var g=f(this);a[0]=j.call(this,e,c?g.html():b),g.domManip(a,c,d)});if(this[0]){i=j&&j.parentNode,f.support.parentNode&&i&&i.nodeType===11&&i.childNodes.length===this.length?e={fragment:i}:e=f.buildFragment(a,this,k),h=e.fragment,h.childNodes.length===1?g=h=h.firstChild:g=h.firstChild;if(g){c=c&&f.nodeName(g,"tr");for(var l=0,m=this.length,n=m-1;l<m;l++)d.call(c?bi(this[l],g):this[l],e.cacheable||m>1&&l<n?f.clone(h,!0,!0):h)}k.length&&f.each(k,function(a,b){b.src?f.ajax({type:"GET",global:!1,url:b.src,async:!1,dataType:"script"}):f.globalEval((b.text||b.textContent||b.innerHTML||"").replace(bf,"/*$0*/")),b.parentNode&&b.parentNode.removeChild(b)})}return this}}),f.buildFragment=function(a,b,d){var e,g,h,i,j=a[0];b&&b[0]&&(i=b[0].ownerDocument||b[0]),i.createDocumentFragment||(i=c),a.length===1&&typeof j=="string"&&j.length<512&&i===c&&j.charAt(0)==="<"&&!bb.test(j)&&(f.support.checkClone||!bd.test(j))&&(f.support.html5Clone||!bc.test(j))&&(g=!0,h=f.fragments[j],h&&h!==1&&(e=h)),e||(e=i.createDocumentFragment(),f.clean(a,i,e,d)),g&&(f.fragments[j]=h?e:1);return{fragment:e,cacheable:g}},f.fragments={},f.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){f.fn[a]=function(c){var d=[],e=f(c),g=this.length===1&&this[0].parentNode;if(g&&g.nodeType===11&&g.childNodes.length===1&&e.length===1){e[b](this[0]);return this}for(var h=0,i=e.length;h<i;h++){var j=(h>0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j)}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d,e,g,h=f.support.html5Clone||f.isXMLDoc(a)||!bc.test("<"+a.nodeName+">")?a.cloneNode(!0):bo(a);if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bk(a,h),d=bl(a),e=bl(h);for(g=0;d[g];++g)e[g]&&bk(d[g],e[g])}if(b){bj(a,h);if(c){d=bl(a),e=bl(h);for(g=0;d[g];++g)bj(d[g],e[g])}}d=e=null;return h},clean:function(a,b,d,e){var g,h,i,j=[];b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);for(var k=0,l;(l=a[k])!=null;k++){typeof l=="number"&&(l+="");if(!l)continue;if(typeof l=="string")if(!_.test(l))l=b.createTextNode(l);else{l=l.replace(Y,"<$1></$2>");var m=(Z.exec(l)||["",""])[1].toLowerCase(),n=bg[m]||bg._default,o=n[0],p=b.createElement("div"),q=bh.childNodes,r;b===c?bh.appendChild(p):U(b).appendChild(p),p.innerHTML=n[1]+l+n[2];while(o--)p=p.lastChild;if(!f.support.tbody){var s=$.test(l),t=m==="table"&&!s?p.firstChild&&p.firstChild.childNodes:n[1]==="<table>"&&!s?p.childNodes:[];for(i=t.length-1;i>=0;--i)f.nodeName(t[i],"tbody")&&!t[i].childNodes.length&&t[i].parentNode.removeChild(t[i])}!f.support.leadingWhitespace&&X.test(l)&&p.insertBefore(b.createTextNode(X.exec(l)[0]),p.firstChild),l=p.childNodes,p&&(p.parentNode.removeChild(p),q.length>0&&(r=q[q.length-1],r&&r.parentNode&&r.parentNode.removeChild(r)))}var u;if(!f.support.appendChecked)if(l[0]&&typeof (u=l.length)=="number")for(i=0;i<u;i++)bn(l[i]);else bn(l);l.nodeType?j.push(l):j=f.merge(j,l)}if(d){g=function(a){return!a.type||be.test(a.type)};for(k=0;j[k];k++){h=j[k];if(e&&f.nodeName(h,"script")&&(!h.type||be.test(h.type)))e.push(h.parentNode?h.parentNode.removeChild(h):h);else{if(h.nodeType===1){var v=f.grep(h.getElementsByTagName("script"),g);j.splice.apply(j,[k+1,0].concat(v))}d.appendChild(h)}}}return j},cleanData:function(a){var b,c,d=f.cache,e=f.event.special,g=f.support.deleteExpando;for(var h=0,i;(i=a[h])!=null;h++){if(i.nodeName&&f.noData[i.nodeName.toLowerCase()])continue;c=i[f.expando];if(c){b=d[c];if(b&&b.events){for(var j in b.events)e[j]?f.event.remove(i,j):f.removeEvent(i,j,b.handle);b.handle&&(b.handle.elem=null)}g?delete i[f.expando]:i.removeAttribute&&i.removeAttribute(f.expando),delete d[c]}}}});var bp=/alpha\([^)]*\)/i,bq=/opacity=([^)]*)/,br=/([A-Z]|^ms)/g,bs=/^[\-+]?(?:\d*\.)?\d+$/i,bt=/^-?(?:\d*\.)?\d+(?!px)[^\d\s]+$/i,bu=/^([\-+])=([\-+.\de]+)/,bv=/^margin/,bw={position:"absolute",visibility:"hidden",display:"block"},bx=["Top","Right","Bottom","Left"],by,bz,bA;f.fn.css=function(a,c){return f.access(this,function(a,c,d){return d!==b?f.style(a,c,d):f.css(a,c)},a,c,arguments.length>1)},f.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=by(a,"opacity");return c===""?"1":c}return a.style.opacity}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":f.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,d,e){if(!!a&&a.nodeType!==3&&a.nodeType!==8&&!!a.style){var g,h,i=f.camelCase(c),j=a.style,k=f.cssHooks[i];c=f.cssProps[i]||i;if(d===b){if(k&&"get"in k&&(g=k.get(a,!1,e))!==b)return g;return j[c]}h=typeof d,h==="string"&&(g=bu.exec(d))&&(d=+(g[1]+1)*+g[2]+parseFloat(f.css(a,c)),h="number");if(d==null||h==="number"&&isNaN(d))return;h==="number"&&!f.cssNumber[i]&&(d+="px");if(!k||!("set"in k)||(d=k.set(a,d))!==b)try{j[c]=d}catch(l){}}},css:function(a,c,d){var e,g;c=f.camelCase(c),g=f.cssHooks[c],c=f.cssProps[c]||c,c==="cssFloat"&&(c="float");if(g&&"get"in g&&(e=g.get(a,!0,d))!==b)return e;if(by)return by(a,c)},swap:function(a,b,c){var d={},e,f;for(f in b)d[f]=a.style[f],a.style[f]=b[f];e=c.call(a);for(f in b)a.style[f]=d[f];return e}}),f.curCSS=f.css,c.defaultView&&c.defaultView.getComputedStyle&&(bz=function(a,b){var c,d,e,g,h=a.style;b=b.replace(br,"-$1").toLowerCase(),(d=a.ownerDocument.defaultView)&&(e=d.getComputedStyle(a,null))&&(c=e.getPropertyValue(b),c===""&&!f.contains(a.ownerDocument.documentElement,a)&&(c=f.style(a,b))),!f.support.pixelMargin&&e&&bv.test(b)&&bt.test(c)&&(g=h.width,h.width=c,c=e.width,h.width=g);return c}),c.documentElement.currentStyle&&(bA=function(a,b){var c,d,e,f=a.currentStyle&&a.currentStyle[b],g=a.style;f==null&&g&&(e=g[b])&&(f=e),bt.test(f)&&(c=g.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),g.left=b==="fontSize"?"1em":f,f=g.pixelLeft+"px",g.left=c,d&&(a.runtimeStyle.left=d));return f===""?"auto":f}),by=bz||bA,f.each(["height","width"],function(a,b){f.cssHooks[b]={get:function(a,c,d){if(c)return a.offsetWidth!==0?bB(a,b,d):f.swap(a,bw,function(){return bB(a,b,d)})},set:function(a,b){return bs.test(b)?b+"px":b}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return bq.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=f.isNumeric(b)?"alpha(opacity="+b*100+")":"",g=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&f.trim(g.replace(bp,""))===""){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bp.test(g)?g.replace(bp,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){return f.swap(a,{display:"inline-block"},function(){return b?by(a,"margin-right"):a.style.marginRight})}})}),f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style&&a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)}),f.each({margin:"",padding:"",border:"Width"},function(a,b){f.cssHooks[a+b]={expand:function(c){var d,e=typeof c=="string"?c.split(" "):[c],f={};for(d=0;d<4;d++)f[a+bx[d]+b]=e[d]||e[d-2]||e[0];return f}}});var bC=/%20/g,bD=/\[\]$/,bE=/\r?\n/g,bF=/#.*$/,bG=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bH=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bI=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,bJ=/^(?:GET|HEAD)$/,bK=/^\/\//,bL=/\?/,bM=/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,bN=/^(?:select|textarea)/i,bO=/\s+/,bP=/([?&])_=[^&]*/,bQ=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bR=f.fn.load,bS={},bT={},bU,bV,bW=["*/"]+["*"];try{bU=e.href}catch(bX){bU=c.createElement("a"),bU.href="",bU=bU.href}bV=bQ.exec(bU.toLowerCase())||[],f.fn.extend({load:function(a,c,d){if(typeof a!="string"&&bR)return bR.apply(this,arguments);if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var g=a.slice(e,a.length);a=a.slice(0,e)}var h="GET";c&&(f.isFunction(c)?(d=c,c=b):typeof c=="object"&&(c=f.param(c,f.ajaxSettings.traditional),h="POST"));var i=this;f.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?f("<div>").append(c.replace(bM,"")).find(g):c)),d&&i.each(d,[c,b,a])}});return this},serialize:function(){return f.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?f.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bN.test(this.nodeName)||bH.test(this.type))}).map(function(a,b){var c=f(this).val();return c==null?null:f.isArray(c)?f.map(c,function(a,c){return{name:b.name,value:a.replace(bE,"\r\n")}}):{name:b.name,value:c.replace(bE,"\r\n")}}).get()}}),f.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){f.fn[b]=function(a){return this.on(b,a)}}),f.each(["get","post"],function(a,c){f[c]=function(a,d,e,g){f.isFunction(d)&&(g=g||e,e=d,d=b);return f.ajax({type:c,url:a,data:d,success:e,dataType:g})}}),f.extend({getScript:function(a,c){return f.get(a,b,c,"script")},getJSON:function(a,b,c){return f.get(a,b,c,"json")},ajaxSetup:function(a,b){b?b$(a,f.ajaxSettings):(b=a,a=f.ajaxSettings),b$(a,b);return a},ajaxSettings:{url:bU,isLocal:bI.test(bV[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded; charset=UTF-8",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":bW},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":f.parseJSON,"text xml":f.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:bY(bS),ajaxTransport:bY(bT),ajax:function(a,c){function w(a,c,l,m){if(s!==2){s=2,q&&clearTimeout(q),p=b,n=m||"",v.readyState=a>0?4:0;var o,r,u,w=c,x=l?ca(d,v,l):b,y,z;if(a>=200&&a<300||a===304){if(d.ifModified){if(y=v.getResponseHeader("Last-Modified"))f.lastModified[k]=y;if(z=v.getResponseHeader("Etag"))f.etag[k]=z}if(a===304)w="notmodified",o=!0;else try{r=cb(d,x),w="success",o=!0}catch(A){w="parsererror",u=A}}else{u=w;if(!w||a)w="error",a<0&&(a=0)}v.status=a,v.statusText=""+(c||w),o?h.resolveWith(e,[r,w,v]):h.rejectWith(e,[v,w,u]),v.statusCode(j),j=b,t&&g.trigger("ajax"+(o?"Success":"Error"),[v,d,o?r:u]),i.fireWith(e,[v,w]),t&&(g.trigger("ajaxComplete",[v,d]),--f.active||f.event.trigger("ajaxStop"))}}typeof a=="object"&&(c=a,a=b),c=c||{};var d=f.ajaxSetup({},c),e=d.context||d,g=e!==d&&(e.nodeType||e instanceof f)?f(e):f.event,h=f.Deferred(),i=f.Callbacks("once memory"),j=d.statusCode||{},k,l={},m={},n,o,p,q,r,s=0,t,u,v={readyState:0,setRequestHeader:function(a,b){if(!s){var c=a.toLowerCase();a=m[c]=m[c]||a,l[a]=b}return this},getAllResponseHeaders:function(){return s===2?n:null},getResponseHeader:function(a){var c;if(s===2){if(!o){o={};while(c=bG.exec(n))o[c[1].toLowerCase()]=c[2]}c=o[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){s||(d.mimeType=a);return this},abort:function(a){a=a||"abort",p&&p.abort(a),w(0,a);return this}};h.promise(v),v.success=v.done,v.error=v.fail,v.complete=i.add,v.statusCode=function(a){if(a){var b;if(s<2)for(b in a)j[b]=[j[b],a[b]];else b=a[v.status],v.then(b,b)}return this},d.url=((a||d.url)+"").replace(bF,"").replace(bK,bV[1]+"//"),d.dataTypes=f.trim(d.dataType||"*").toLowerCase().split(bO),d.crossDomain==null&&(r=bQ.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]==bV[1]&&r[2]==bV[2]&&(r[3]||(r[1]==="http:"?80:443))==(bV[3]||(bV[1]==="http:"?80:443)))),d.data&&d.processData&&typeof d.data!="string"&&(d.data=f.param(d.data,d.traditional)),bZ(bS,d,c,v);if(s===2)return!1;t=d.global,d.type=d.type.toUpperCase(),d.hasContent=!bJ.test(d.type),t&&f.active++===0&&f.event.trigger("ajaxStart");if(!d.hasContent){d.data&&(d.url+=(bL.test(d.url)?"&":"?")+d.data,delete d.data),k=d.url;if(d.cache===!1){var x=f.now(),y=d.url.replace(bP,"$1_="+x);d.url=y+(y===d.url?(bL.test(d.url)?"&":"?")+"_="+x:"")}}(d.data&&d.hasContent&&d.contentType!==!1||c.contentType)&&v.setRequestHeader("Content-Type",d.contentType),d.ifModified&&(k=k||d.url,f.lastModified[k]&&v.setRequestHeader("If-Modified-Since",f.lastModified[k]),f.etag[k]&&v.setRequestHeader("If-None-Match",f.etag[k])),v.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(d.dataTypes[0]!=="*"?", "+bW+"; q=0.01":""):d.accepts["*"]);for(u in d.headers)v.setRequestHeader(u,d.headers[u]);if(d.beforeSend&&(d.beforeSend.call(e,v,d)===!1||s===2)){v.abort();return!1}for(u in{success:1,error:1,complete:1})v[u](d[u]);p=bZ(bT,d,c,v);if(!p)w(-1,"No Transport");else{v.readyState=1,t&&g.trigger("ajaxSend",[v,d]),d.async&&d.timeout>0&&(q=setTimeout(function(){v.abort("timeout")},d.timeout));try{s=1,p.send(l,w)}catch(z){if(s<2)w(-1,z);else throw z}}return v},param:function(a,c){var d=[],e=function(a,b){b=f.isFunction(b)?b():b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=f.ajaxSettings.traditional);if(f.isArray(a)||a.jquery&&!f.isPlainObject(a))f.each(a,function(){e(this.name,this.value)});else for(var g in a)b_(g,a[g],c,e);return d.join("&").replace(bC,"+")}}),f.extend({active:0,lastModified:{},etag:{}});var cc=f.now(),cd=/(\=)\?(&|$)|\?\?/i;f.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return f.expando+"_"+cc++}}),f.ajaxPrefilter("json jsonp",function(b,c,d){var e=typeof b.data=="string"&&/^application\/x\-www\-form\-urlencoded/.test(b.contentType);if(b.dataTypes[0]==="jsonp"||b.jsonp!==!1&&(cd.test(b.url)||e&&cd.test(b.data))){var g,h=b.jsonpCallback=f.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2";b.jsonp!==!1&&(j=j.replace(cd,l),b.url===j&&(e&&(k=k.replace(cd,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},d.always(function(){a[h]=i,g&&f.isFunction(i)&&a[h](g[0])}),b.converters["script json"]=function(){g||f.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),f.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){f.globalEval(a);return a}}}),f.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),f.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(c||!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var ce=a.ActiveXObject?function(){for(var a in cg)cg[a](0,1)}:!1,cf=0,cg;f.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&ch()||ci()}:ch,function(a){f.extend(f.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(f.ajaxSettings.xhr()),f.support.ajax&&f.ajaxTransport(function(c){if(!c.crossDomain||f.support.cors){var d;return{send:function(e,g){var h=c.xhr(),i,j;c.username?h.open(c.type,c.url,c.async,c.username,c.password):h.open(c.type,c.url,c.async);if(c.xhrFields)for(j in c.xhrFields)h[j]=c.xhrFields[j];c.mimeType&&h.overrideMimeType&&h.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(j in e)h.setRequestHeader(j,e[j])}catch(k){}h.send(c.hasContent&&c.data||null),d=function(a,e){var j,k,l,m,n;try{if(d&&(e||h.readyState===4)){d=b,i&&(h.onreadystatechange=f.noop,ce&&delete cg[i]);if(e)h.readyState!==4&&h.abort();else{j=h.status,l=h.getAllResponseHeaders(),m={},n=h.responseXML,n&&n.documentElement&&(m.xml=n);try{m.text=h.responseText}catch(a){}try{k=h.statusText}catch(o){k=""}!j&&c.isLocal&&!c.crossDomain?j=m.text?200:404:j===1223&&(j=204)}}}catch(p){e||g(-1,p)}m&&g(j,k,m,l)},!c.async||h.readyState===4?d():(i=++cf,ce&&(cg||(cg={},f(a).unload(ce)),cg[i]=d),h.onreadystatechange=d)},abort:function(){d&&d(0,1)}}}});var cj={},ck,cl,cm=/^(?:toggle|show|hide)$/,cn=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,co,cp=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]],cq;f.fn.extend({show:function(a,b,c){var d,e;if(a||a===0)return this.animate(ct("show",3),a,b,c);for(var g=0,h=this.length;g<h;g++)d=this[g],d.style&&(e=d.style.display,!f._data(d,"olddisplay")&&e==="none"&&(e=d.style.display=""),(e===""&&f.css(d,"display")==="none"||!f.contains(d.ownerDocument.documentElement,d))&&f._data(d,"olddisplay",cu(d.nodeName)));for(g=0;g<h;g++){d=this[g];if(d.style){e=d.style.display;if(e===""||e==="none")d.style.display=f._data(d,"olddisplay")||""}}return this},hide:function(a,b,c){if(a||a===0)return this.animate(ct("hide",3),a,b,c);var d,e,g=0,h=this.length;for(;g<h;g++)d=this[g],d.style&&(e=f.css(d,"display"),e!=="none"&&!f._data(d,"olddisplay")&&f._data(d,"olddisplay",e));for(g=0;g<h;g++)this[g].style&&(this[g].style.display="none");return this},_toggle:f.fn.toggle,toggle:function(a,b,c){var d=typeof a=="boolean";f.isFunction(a)&&f.isFunction(b)?this._toggle.apply(this,arguments):a==null||d?this.each(function(){var b=d?a:f(this).is(":hidden");f(this)[b?"show":"hide"]()}):this.animate(ct("toggle",3),a,b,c);return this},fadeTo:function(a,b,c,d){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){function g(){e.queue===!1&&f._mark(this);var b=f.extend({},e),c=this.nodeType===1,d=c&&f(this).is(":hidden"),g,h,i,j,k,l,m,n,o,p,q;b.animatedProperties={};for(i in a){g=f.camelCase(i),i!==g&&(a[g]=a[i],delete a[i]);if((k=f.cssHooks[g])&&"expand"in k){l=k.expand(a[g]),delete a[g];for(i in l)i in a||(a[i]=l[i])}}for(g in a){h=a[g],f.isArray(h)?(b.animatedProperties[g]=h[1],h=a[g]=h[0]):b.animatedProperties[g]=b.specialEasing&&b.specialEasing[g]||b.easing||"swing";if(h==="hide"&&d||h==="show"&&!d)return b.complete.call(this);c&&(g==="height"||g==="width")&&(b.overflow=[this.style.overflow,this.style.overflowX,this.style.overflowY],f.css(this,"display")==="inline"&&f.css(this,"float")==="none"&&(!f.support.inlineBlockNeedsLayout||cu(this.nodeName)==="inline"?this.style.display="inline-block":this.style.zoom=1))}b.overflow!=null&&(this.style.overflow="hidden");for(i in a)j=new f.fx(this,b,i),h=a[i],cm.test(h)?(q=f._data(this,"toggle"+i)||(h==="toggle"?d?"show":"hide":0),q?(f._data(this,"toggle"+i,q==="show"?"hide":"show"),j[q]()):j[h]()):(m=cn.exec(h),n=j.cur(),m?(o=parseFloat(m[2]),p=m[3]||(f.cssNumber[i]?"":"px"),p!=="px"&&(f.style(this,i,(o||1)+p),n=(o||1)/j.cur()*n,f.style(this,i,n+p)),m[1]&&(o=(m[1]==="-="?-1:1)*o+n),j.custom(n,o,p)):j.custom(n,h,""));return!0}var e=f.speed(b,c,d);if(f.isEmptyObject(a))return this.each(e.complete,[!1]);a=f.extend({},a);return e.queue===!1?this.each(g):this.queue(e.queue,g)},stop:function(a,c,d){typeof a!="string"&&(d=c,c=a,a=b),c&&a!==!1&&this.queue(a||"fx",[]);return this.each(function(){function h(a,b,c){var e=b[c];f.removeData(a,c,!0),e.stop(d)}var b,c=!1,e=f.timers,g=f._data(this);d||f._unmark(!0,this);if(a==null)for(b in g)g[b]&&g[b].stop&&b.indexOf(".run")===b.length-4&&h(this,g,b);else g[b=a+".run"]&&g[b].stop&&h(this,g,b);for(b=e.length;b--;)e[b].elem===this&&(a==null||e[b].queue===a)&&(d?e[b](!0):e[b].saveState(),c=!0,e.splice(b,1));(!d||!c)&&f.dequeue(this,a)})}}),f.each({slideDown:ct("show",1),slideUp:ct("hide",1),slideToggle:ct("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){f.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),f.extend({speed:function(a,b,c){var d=a&&typeof a=="object"?f.extend({},a):{complete:c||!c&&b||f.isFunction(a)&&a,duration:a,easing:c&&b||b&&!f.isFunction(b)&&b};d.duration=f.fx.off?0:typeof d.duration=="number"?d.duration:d.duration in f.fx.speeds?f.fx.speeds[d.duration]:f.fx.speeds._default;if(d.queue==null||d.queue===!0)d.queue="fx";d.old=d.complete,d.complete=function(a){f.isFunction(d.old)&&d.old.call(this),d.queue?f.dequeue(this,d.queue):a!==!1&&f._unmark(this)};return d},easing:{linear:function(a){return a},swing:function(a){return-Math.cos(a*Math.PI)/2+.5}},timers:[],fx:function(a,b,c){this.options=b,this.elem=a,this.prop=c,b.orig=b.orig||{}}}),f.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this),(f.fx.step[this.prop]||f.fx.step._default)(this)},cur:function(){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];var a,b=f.css(this.elem,this.prop);return isNaN(a=parseFloat(b))?!b||b==="auto"?0:b:a},custom:function(a,c,d){function h(a){return e.step(a)}var e=this,g=f.fx;this.startTime=cq||cr(),this.end=c,this.now=this.start=a,this.pos=this.state=0,this.unit=d||this.unit||(f.cssNumber[this.prop]?"":"px"),h.queue=this.options.queue,h.elem=this.elem,h.saveState=function(){f._data(e.elem,"fxshow"+e.prop)===b&&(e.options.hide?f._data(e.elem,"fxshow"+e.prop,e.start):e.options.show&&f._data(e.elem,"fxshow"+e.prop,e.end))},h()&&f.timers.push(h)&&!co&&(co=setInterval(g.tick,g.interval))},show:function(){var a=f._data(this.elem,"fxshow"+this.prop);this.options.orig[this.prop]=a||f.style(this.elem,this.prop),this.options.show=!0,a!==b?this.custom(this.cur(),a):this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur()),f(this.elem).show()},hide:function(){this.options.orig[this.prop]=f._data(this.elem,"fxshow"+this.prop)||f.style(this.elem,this.prop),this.options.hide=!0,this.custom(this.cur(),0)},step:function(a){var b,c,d,e=cq||cr(),g=!0,h=this.elem,i=this.options;if(a||e>=i.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),i.animatedProperties[this.prop]=!0;for(b in i.animatedProperties)i.animatedProperties[b]!==!0&&(g=!1);if(g){i.overflow!=null&&!f.support.shrinkWrapBlocks&&f.each(["","X","Y"],function(a,b){h.style["overflow"+b]=i.overflow[a]}),i.hide&&f(h).hide();if(i.hide||i.show)for(b in i.animatedProperties)f.style(h,b,i.orig[b]),f.removeData(h,"fxshow"+b,!0),f.removeData(h,"toggle"+b,!0);d=i.complete,d&&(i.complete=!1,d.call(h))}return!1}i.duration==Infinity?this.now=e:(c=e-this.startTime,this.state=c/i.duration,this.pos=f.easing[i.animatedProperties[this.prop]](this.state,c,0,1,i.duration),this.now=this.start+(this.end-this.start)*this.pos),this.update();return!0}},f.extend(f.fx,{tick:function(){var a,b=f.timers,c=0;for(;c<b.length;c++)a=b[c],!a()&&b[c]===a&&b.splice(c--,1);b.length||f.fx.stop()},interval:13,stop:function(){clearInterval(co),co=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){f.style(a.elem,"opacity",a.now)},_default:function(a){a.elem.style&&a.elem.style[a.prop]!=null?a.elem.style[a.prop]=a.now+a.unit:a.elem[a.prop]=a.now}}}),f.each(cp.concat.apply([],cp),function(a,b){b.indexOf("margin")&&(f.fx.step[b]=function(a){f.style(a.elem,b,Math.max(0,a.now)+a.unit)})}),f.expr&&f.expr.filters&&(f.expr.filters.animated=function(a){return f.grep(f.timers,function(b){return a===b.elem}).length});var cv,cw=/^t(?:able|d|h)$/i,cx=/^(?:body|html)$/i;"getBoundingClientRect"in c.documentElement?cv=function(a,b,c,d){try{d=a.getBoundingClientRect()}catch(e){}if(!d||!f.contains(c,a))return d?{top:d.top,left:d.left}:{top:0,left:0};var g=b.body,h=cy(b),i=c.clientTop||g.clientTop||0,j=c.clientLeft||g.clientLeft||0,k=h.pageYOffset||f.support.boxModel&&c.scrollTop||g.scrollTop,l=h.pageXOffset||f.support.boxModel&&c.scrollLeft||g.scrollLeft,m=d.top+k-i,n=d.left+l-j;return{top:m,left:n}}:cv=function(a,b,c){var d,e=a.offsetParent,g=a,h=b.body,i=b.defaultView,j=i?i.getComputedStyle(a,null):a.currentStyle,k=a.offsetTop,l=a.offsetLeft;while((a=a.parentNode)&&a!==h&&a!==c){if(f.support.fixedPosition&&j.position==="fixed")break;d=i?i.getComputedStyle(a,null):a.currentStyle,k-=a.scrollTop,l-=a.scrollLeft,a===e&&(k+=a.offsetTop,l+=a.offsetLeft,f.support.doesNotAddBorder&&(!f.support.doesAddBorderForTableAndCells||!cw.test(a.nodeName))&&(k+=parseFloat(d.borderTopWidth)||0,l+=parseFloat(d.borderLeftWidth)||0),g=e,e=a.offsetParent),f.support.subtractsBorderForOverflowNotVisible&&d.overflow!=="visible"&&(k+=parseFloat(d.borderTopWidth)||0,l+=parseFloat(d.borderLeftWidth)||0),j=d}if(j.position==="relative"||j.position==="static")k+=h.offsetTop,l+=h.offsetLeft;f.support.fixedPosition&&j.position==="fixed"&&(k+=Math.max(c.scrollTop,h.scrollTop),l+=Math.max(c.scrollLeft,h.scrollLeft));return{top:k,left:l}},f.fn.offset=function(a){if(arguments.length)return a===b?this:this.each(function(b){f.offset.setOffset(this,a,b)});var c=this[0],d=c&&c.ownerDocument;if(!d)return null;if(c===d.body)return f.offset.bodyOffset(c);return cv(c,d,d.documentElement)},f.offset={bodyOffset:function(a){var b=a.offsetTop,c=a.offsetLeft;f.support.doesNotIncludeMarginInBodyOffset&&(b+=parseFloat(f.css(a,"marginTop"))||0,c+=parseFloat(f.css(a,"marginLeft"))||0);return{top:b,left:c}},setOffset:function(a,b,c){var d=f.css(a,"position");d==="static"&&(a.style.position="relative");var e=f(a),g=e.offset(),h=f.css(a,"top"),i=f.css(a,"left"),j=(d==="absolute"||d==="fixed")&&f.inArray("auto",[h,i])>-1,k={},l={},m,n;j?(l=e.position(),m=l.top,n=l.left):(m=parseFloat(h)||0,n=parseFloat(i)||0),f.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):e.css(k)}},f.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),d=cx.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(f.css(a,"marginTop"))||0,c.left-=parseFloat(f.css(a,"marginLeft"))||0,d.top+=parseFloat(f.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(f.css(b[0],"borderLeftWidth"))||0;return{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&!cx.test(a.nodeName)&&f.css(a,"position")==="static")a=a.offsetParent;return a})}}),f.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,c){var d=/Y/.test(c);f.fn[a]=function(e){return f.access(this,function(a,e,g){var h=cy(a);if(g===b)return h?c in h?h[c]:f.support.boxModel&&h.document.documentElement[e]||h.document.body[e]:a[e];h?h.scrollTo(d?f(h).scrollLeft():g,d?g:f(h).scrollTop()):a[e]=g},a,e,arguments.length,null)}}),f.each({Height:"height",Width:"width"},function(a,c){var d="client"+a,e="scroll"+a,g="offset"+a;f.fn["inner"+a]=function(){var a=this[0];return a?a.style?parseFloat(f.css(a,c,"padding")):this[c]():null},f.fn["outer"+a]=function(a){var b=this[0];return b?b.style?parseFloat(f.css(b,c,a?"margin":"border")):this[c]():null},f.fn[c]=function(a){return f.access(this,function(a,c,h){var i,j,k,l;if(f.isWindow(a)){i=a.document,j=i.documentElement[d];return f.support.boxModel&&j||i.body&&i.body[d]||j}if(a.nodeType===9){i=a.documentElement;if(i[d]>=i[e])return i[d];return Math.max(a.body[e],i[e],a.body[g],i[g])}if(h===b){k=f.css(a,c),l=parseFloat(k);return f.isNumeric(l)?l:k}f(a).css(c,h)},c,a,arguments.length,null)}}),a.jQuery=a.$=f,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return f})})(window);
\ No newline at end of file
diff --git a/redakcja/static/js/lib/jquery/jquery.autocomplete.js b/redakcja/static/js/lib/jquery/jquery.autocomplete.js
deleted file mode 100644 (file)
index 83d8f9d..0000000
+++ /dev/null
@@ -1,816 +0,0 @@
-/*
- * jQuery Autocomplete plugin 1.1
- *
- * 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 15 2009-08-22 10:30:27Z joern.zaefferer $
- * 
- * changed by Radek Czajka (FNP), 20100907: ignore alt keydown
- */
-
-;(function($) {
-
-$.fn.extend({
-       autocomplete: function(urlOrData, options) {
-               var isUrl = typeof urlOrData == "string";
-               options = $.extend({}, $.Autocompleter.defaults, {
-                       url: isUrl ? urlOrData : null,
-                       data: isUrl ? null : urlOrData,
-                       delay: isUrl ? $.Autocompleter.defaults.delay : 10,
-                       max: options && !options.scroll ? 10 : 150
-               }, options);
-
-               // if highlight is set to false, replace it with a do-nothing function
-               options.highlight = options.highlight || function(value) { return value; };
-
-               // if the formatMatch option is not specified, then use formatItem for backwards compatibility
-               options.formatMatch = options.formatMatch || options.formatItem;
-
-               return this.each(function() {
-                       new $.Autocompleter(this, options);
-               });
-       },
-       result: function(handler) {
-               return this.bind("result", handler);
-       },
-       search: function(handler) {
-               return this.trigger("search", [handler]);
-       },
-       flushCache: function() {
-               return this.trigger("flushCache");
-       },
-       setOptions: function(options){
-               return this.trigger("setOptions", [options]);
-       },
-       unautocomplete: function() {
-               return this.trigger("unautocomplete");
-       }
-});
-
-$.Autocompleter = function(input, options) {
-
-       var KEY = {
-               UP: 38,
-               DOWN: 40,
-               DEL: 46,
-               TAB: 9,
-               RETURN: 13,
-               ESC: 27,
-               COMMA: 188,
-               PAGEUP: 33,
-               PAGEDOWN: 34,
-               BACKSPACE: 8
-       };
-
-       // Create $ object for input element
-       var $input = $(input).attr("autocomplete", "off").addClass(options.inputClass);
-
-       var timeout;
-       var previousValue = "";
-       var cache = $.Autocompleter.Cache(options);
-       var hasFocus = 0;
-       var lastKeyPressCode;
-       var config = {
-               mouseDownOnSelect: false
-       };
-       var select = $.Autocompleter.Select(options, input, selectCurrent, config);
-
-       var blockSubmit;
-
-       // prevent form submit in opera when selecting with return key
-       $.browser.opera && $(input.form).bind("submit.autocomplete", function() {
-               if (blockSubmit) {
-                       blockSubmit = false;
-                       return false;
-               }
-       });
-
-       // 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) {
-
-            // changed by fnp
-            case 18:
-            case 17:
-                break;
-            // changed by fnp
-
-                       case KEY.UP:
-                               event.preventDefault();
-                               if ( select.visible() ) {
-                                       select.prev();
-                               } else {
-                                       onChange(0, true);
-                               }
-                               break;
-
-                       case KEY.DOWN:
-                               event.preventDefault();
-                               if ( select.visible() ) {
-                                       select.next();
-                               } else {
-                                       onChange(0, true);
-                               }
-                               break;
-
-                       case KEY.PAGEUP:
-                               event.preventDefault();
-                               if ( select.visible() ) {
-                                       select.pageUp();
-                               } else {
-                                       onChange(0, true);
-                               }
-                               break;
-
-                       case KEY.PAGEDOWN:
-                               event.preventDefault();
-                               if ( select.visible() ) {
-                                       select.pageDown();
-                               } else {
-                                       onChange(0, true);
-                               }
-                               break;
-
-                       // matches also semicolon
-                       case options.multiple && $.trim(options.multipleSeparator) == "," && KEY.COMMA:
-                       case KEY.TAB:
-                       case KEY.RETURN:
-                               if( selectCurrent() ) {
-                                       // stop default to prevent a form submit, Opera needs special handling
-                                       event.preventDefault();
-                                       blockSubmit = true;
-                                       return false;
-                               }
-                               break;
-
-                       case KEY.ESC:
-                               select.hide();
-                               break;
-
-                       default:
-                               clearTimeout(timeout);
-                               timeout = setTimeout(onChange, options.delay);
-                               break;
-               }
-       }).focus(function(){
-               // track whether the field has focus, we shouldn't process any
-               // results if the field no longer has focus
-               hasFocus++;
-       }).blur(function() {
-               hasFocus = 0;
-               if (!config.mouseDownOnSelect) {
-                       hideResults();
-               }
-       }).click(function() {
-               // show select when clicking in a focused field
-               if ( hasFocus++ > 1 && !select.visible() ) {
-                       onChange(0, true);
-               }
-       }).bind("search", function() {
-               // TODO why not just specifying both arguments?
-               var fn = (arguments.length > 1) ? arguments[1] : null;
-               function findValueCallback(q, data) {
-                       var result;
-                       if( data && data.length ) {
-                               for (var i=0; i < data.length; i++) {
-                                       if( data[i].result.toLowerCase() == q.toLowerCase() ) {
-                                               result = data[i];
-                                               break;
-                                       }
-                               }
-                       }
-                       if( typeof fn == "function" ) fn(result);
-                       else $input.trigger("result", result && [result.data, result.value]);
-               }
-               $.each(trimWords($input.val()), function(i, value) {
-                       request(value, findValueCallback, findValueCallback);
-               });
-       }).bind("flushCache", function() {
-               cache.flush();
-       }).bind("setOptions", function() {
-               $.extend(options, arguments[1]);
-               // if we've updated the data, repopulate
-               if ( "data" in arguments[1] )
-                       cache.populate();
-       }).bind("unautocomplete", function() {
-               select.unbind();
-               $input.unbind();
-               $(input.form).unbind(".autocomplete");
-       });
-
-
-       function selectCurrent() {
-               var selected = select.selected();
-               if( !selected )
-                       return false;
-
-               var v = selected.result;
-               previousValue = v;
-
-               if ( options.multiple ) {
-                       var words = trimWords($input.val());
-                       if ( words.length > 1 ) {
-                               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;
-               }
-
-               $input.val(v);
-               hideResultsNow();
-               $input.trigger("result", [selected.data, selected.value]);
-               return true;
-       }
-
-       function onChange(crap, skipPrevCheck) {
-               if( lastKeyPressCode == KEY.DEL ) {
-                       select.hide();
-                       return;
-               }
-
-               var currentValue = $input.val();
-
-               if ( !skipPrevCheck && currentValue == previousValue )
-                       return;
-
-               previousValue = currentValue;
-
-               currentValue = lastWord(currentValue);
-               if ( currentValue.length >= options.minChars) {
-                       $input.addClass(options.loadingClass);
-                       if (!options.matchCase)
-                               currentValue = currentValue.toLowerCase();
-                       request(currentValue, receiveData, hideResultsNow);
-               } else {
-                       stopLoading();
-                       select.hide();
-               }
-       };
-
-       function trimWords(value) {
-               if (!value)
-                       return [""];
-               if (!options.multiple)
-                       return [$.trim(value)];
-               return $.map(value.split(options.multipleSeparator), function(word) {
-                       return $.trim(value).length ? $.trim(word) : null;
-               });
-       }
-
-       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];
-       }
-
-       // fills in the input box w/the first match (assumed to be the best match)
-       // q: the term entered
-       // sValue: the first matching result
-       function autoFill(q, sValue){
-               // autofill in the complete box w/the first match as long as the user hasn't entered in more data
-               // if the last user key pressed was backspace, don't autofill
-               if( options.autoFill && (lastWord($input.val()).toLowerCase() == q.toLowerCase()) && lastKeyPressCode != KEY.BACKSPACE ) {
-                       // 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)
-                       $(input).selection(previousValue.length, previousValue.length + sValue.length);
-               }
-       };
-
-       function hideResults() {
-               clearTimeout(timeout);
-               timeout = setTimeout(hideResultsNow, 200);
-       };
-
-       function hideResultsNow() {
-               var wasVisible = select.visible();
-               select.hide();
-               clearTimeout(timeout);
-               stopLoading();
-               if (options.mustMatch) {
-                       // call search and run callback
-                       $input.search(
-                               function (result){
-                                       // if no value found, clear the input box
-                                       if( !result ) {
-                                               if (options.multiple) {
-                                                       var words = trimWords($input.val()).slice(0, -1);
-                                                       $input.val( words.join(options.multipleSeparator) + (words.length ? options.multipleSeparator : "") );
-                                               }
-                                               else {
-                                                       $input.val( "" );
-                                                       $input.trigger("result", null);
-                                               }
-                                       }
-                               }
-                       );
-               }
-       };
-
-       function receiveData(q, data) {
-               if ( data && data.length && hasFocus ) {
-                       stopLoading();
-                       select.display(data, q);
-                       autoFill(q, data[0].value);
-                       select.show();
-               } else {
-                       hideResultsNow();
-               }
-       };
-
-       function request(term, success, failure) {
-               if (!options.matchCase)
-                       term = term.toLowerCase();
-               var data = cache.load(term);
-               // recieve the cached data
-               if (data && data.length) {
-                       success(term, data);
-               // if an AJAX url has been supplied, try loading the data now
-               } else if( (typeof options.url == "string") && (options.url.length > 0) ){
-
-                       var extraParams = {
-                               timestamp: +new Date()
-                       };
-                       $.each(options.extraParams, function(key, param) {
-                               extraParams[key] = typeof param == "function" ? param() : param;
-                       });
-
-                       $.ajax({
-                               // try to leverage ajaxQueue plugin to abort previous requests
-                               mode: "abort",
-                               // limit abortion to this input
-                               port: "autocomplete" + input.name,
-                               dataType: options.dataType,
-                               url: options.url,
-                               data: $.extend({
-                                       q: lastWord(term),
-                                       limit: options.max
-                               }, extraParams),
-                               success: function(data) {
-                                       var parsed = options.parse && options.parse(data) || parse(data);
-                                       cache.add(term, parsed);
-                                       success(term, parsed);
-                               }
-                       });
-               } else {
-                       // if we have a failure, we need to empty the list -- this prevents the the [TAB] key from selecting the last successful match
-                       select.emptyList();
-                       failure(term);
-               }
-       };
-
-       function parse(data) {
-               var parsed = [];
-               var rows = data.split("\n");
-               for (var i=0; i < rows.length; i++) {
-                       var row = $.trim(rows[i]);
-                       if (row) {
-                               row = row.split("|");
-                               parsed[parsed.length] = {
-                                       data: row,
-                                       value: row[0],
-                                       result: options.formatResult && options.formatResult(row, row[0]) || row[0]
-                               };
-                       }
-               }
-               return parsed;
-       };
-
-       function stopLoading() {
-               $input.removeClass(options.loadingClass);
-       };
-
-};
-
-$.Autocompleter.defaults = {
-       inputClass: "ac_input",
-       resultsClass: "ac_results",
-       loadingClass: "ac_loading",
-       minChars: 1,
-       delay: 400,
-       matchCase: false,
-       matchSubset: true,
-       matchContains: false,
-       cacheLength: 10,
-       max: 100,
-       mustMatch: false,
-       extraParams: {},
-       selectFirst: true,
-       formatItem: function(row) { return row[0]; },
-       formatMatch: null,
-       autoFill: false,
-       width: 0,
-       multiple: false,
-       multipleSeparator: ", ",
-       highlight: function(value, term) {
-               return value.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + term.replace(/([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi, "\\$1") + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "<strong>$1</strong>");
-       },
-    scroll: true,
-    scrollHeight: 180
-};
-
-$.Autocompleter.Cache = function(options) {
-
-       var data = {};
-       var length = 0;
-
-       function matchSubset(s, sub) {
-               if (!options.matchCase)
-                       s = s.toLowerCase();
-               var i = s.indexOf(sub);
-               if (options.matchContains == "word"){
-                       i = s.toLowerCase().search("\\b" + sub.toLowerCase());
-               }
-               if (i == -1) return false;
-               return i == 0 || options.matchContains;
-       };
-
-       function add(q, value) {
-               if (length > options.cacheLength){
-                       flush();
-               }
-               if (!data[q]){
-                       length++;
-               }
-               data[q] = value;
-       }
-
-       function populate(){
-               if( !options.data ) return false;
-               // track the matches
-               var stMatchSets = {},
-                       nullData = 0;
-
-               // no url was specified, we need to adjust the cache length to make sure it fits the local data store
-               if( !options.url ) options.cacheLength = 1;
-
-               // track all options for minChars = 0
-               stMatchSets[""] = [];
-
-               // loop through the array and create a lookup structure
-               for ( var i = 0, ol = options.data.length; i < ol; i++ ) {
-                       var rawValue = options.data[i];
-                       // if rawValue is a string, make an array otherwise just reference the array
-                       rawValue = (typeof rawValue == "string") ? [rawValue] : rawValue;
-
-                       var value = options.formatMatch(rawValue, i+1, options.data.length);
-                       if ( value === false )
-                               continue;
-
-                       var firstChar = value.charAt(0).toLowerCase();
-                       // if no lookup array for this character exists, look it up now
-                       if( !stMatchSets[firstChar] )
-                               stMatchSets[firstChar] = [];
-
-                       // if the match is a string
-                       var row = {
-                               value: value,
-                               data: rawValue,
-                               result: options.formatResult && options.formatResult(rawValue) || value
-                       };
-
-                       // push the current match into the set list
-                       stMatchSets[firstChar].push(row);
-
-                       // keep track of minChars zero items
-                       if ( nullData++ < options.max ) {
-                               stMatchSets[""].push(row);
-                       }
-               };
-
-               // add the data items to the cache
-               $.each(stMatchSets, function(i, value) {
-                       // increase the cache size
-                       options.cacheLength++;
-                       // add to the cache
-                       add(i, value);
-               });
-       }
-
-       // populate any existing data
-       setTimeout(populate, 25);
-
-       function flush(){
-               data = {};
-               length = 0;
-       }
-
-       return {
-               flush: flush,
-               add: add,
-               populate: populate,
-               load: function(q) {
-                       if (!options.cacheLength || !length)
-                               return null;
-                       /*
-                        * if dealing w/local data and matchContains than we must make sure
-                        * to loop through all the data collections looking for matches
-                        */
-                       if( !options.url && options.matchContains ){
-                               // track all matches
-                               var csub = [];
-                               // loop through all the data grids for matches
-                               for( var k in data ){
-                                       // don't search through the stMatchSets[""] (minChars: 0) cache
-                                       // this prevents duplicates
-                                       if( k.length > 0 ){
-                                               var c = data[k];
-                                               $.each(c, function(i, x) {
-                                                       // if we've got a match, add it to the array
-                                                       if (matchSubset(x.value, q)) {
-                                                               csub.push(x);
-                                                       }
-                                               });
-                                       }
-                               }
-                               return csub;
-                       } else
-                       // if the exact item exists, use it
-                       if (data[q]){
-                               return data[q];
-                       } else
-                       if (options.matchSubset) {
-                               for (var i = q.length - 1; i >= options.minChars; i--) {
-                                       var c = data[q.substr(0, i)];
-                                       if (c) {
-                                               var csub = [];
-                                               $.each(c, function(i, x) {
-                                                       if (matchSubset(x.value, q)) {
-                                                               csub[csub.length] = x;
-                                                       }
-                                               });
-                                               return csub;
-                                       }
-                               }
-                       }
-                       return null;
-               }
-       };
-};
-
-$.Autocompleter.Select = function (options, input, select, config) {
-       var CLASSES = {
-               ACTIVE: "ac_over"
-       };
-
-       var listItems,
-               active = -1,
-               data,
-               term = "",
-               needsInit = true,
-               element,
-               list;
-
-       // Create results
-       function init() {
-               if (!needsInit)
-                       return;
-               element = $("<div/>")
-               .hide()
-               .addClass(options.resultsClass)
-               .css("position", "absolute")
-               .appendTo(document.body);
-
-               list = $("<ul/>").appendTo(element).mouseover( function(event) {
-                       if(target(event).nodeName && target(event).nodeName.toUpperCase() == 'LI') {
-                   active = $("li", list).removeClass(CLASSES.ACTIVE).index(target(event));
-                           $(target(event)).addClass(CLASSES.ACTIVE);
-               }
-               }).click(function(event) {
-                       $(target(event)).addClass(CLASSES.ACTIVE);
-                       select();
-                       // TODO provide option to avoid setting focus again after selection? useful for cleanup-on-focus
-                       input.focus();
-                       return false;
-               }).mousedown(function() {
-                       config.mouseDownOnSelect = true;
-               }).mouseup(function() {
-                       config.mouseDownOnSelect = false;
-               });
-
-               if( options.width > 0 )
-                       element.css("width", options.width);
-
-               needsInit = false;
-       }
-
-       function target(event) {
-               var element = event.target;
-               while(element && element.tagName != "LI")
-                       element = element.parentNode;
-               // more fun with IE, sometimes event.target is empty, just ignore it then
-               if(!element)
-                       return [];
-               return element;
-       }
-
-       function moveSelect(step) {
-               listItems.slice(active, active + 1).removeClass(CLASSES.ACTIVE);
-               movePosition(step);
-        var activeItem = listItems.slice(active, active + 1).addClass(CLASSES.ACTIVE);
-        if(options.scroll) {
-            var offset = 0;
-            listItems.slice(0, active).each(function() {
-                               offset += this.offsetHeight;
-                       });
-            if((offset + activeItem[0].offsetHeight - list.scrollTop()) > list[0].clientHeight) {
-                list.scrollTop(offset + activeItem[0].offsetHeight - list.innerHeight());
-            } else if(offset < list.scrollTop()) {
-                list.scrollTop(offset);
-            }
-        }
-       };
-
-       function movePosition(step) {
-               active += step;
-               if (active < 0) {
-                       active = listItems.size() - 1;
-               } else if (active >= listItems.size()) {
-                       active = 0;
-               }
-       }
-
-       function limitNumberOfItems(available) {
-               return options.max && options.max < available
-                       ? options.max
-                       : available;
-       }
-
-       function fillList() {
-               list.empty();
-               var max = limitNumberOfItems(data.length);
-               for (var i=0; i < max; i++) {
-                       if (!data[i])
-                               continue;
-                       var formatted = options.formatItem(data[i].data, i+1, max, data[i].value, term);
-                       if ( formatted === false )
-                               continue;
-                       var li = $("<li/>").html( options.highlight(formatted, term) ).addClass(i%2 == 0 ? "ac_even" : "ac_odd").appendTo(list)[0];
-                       $.data(li, "ac_data", data[i]);
-               }
-               listItems = list.find("li");
-               if ( options.selectFirst ) {
-                       listItems.slice(0, 1).addClass(CLASSES.ACTIVE);
-                       active = 0;
-               }
-               // apply bgiframe if available
-               if ( $.fn.bgiframe )
-                       list.bgiframe();
-       }
-
-       return {
-               display: function(d, q) {
-                       init();
-                       data = d;
-                       term = q;
-                       fillList();
-               },
-               next: function() {
-                       moveSelect(1);
-               },
-               prev: function() {
-                       moveSelect(-1);
-               },
-               pageUp: function() {
-                       if (active != 0 && active - 8 < 0) {
-                               moveSelect( -active );
-                       } else {
-                               moveSelect(-8);
-                       }
-               },
-               pageDown: function() {
-                       if (active != listItems.size() - 1 && active + 8 > listItems.size()) {
-                               moveSelect( listItems.size() - 1 - active );
-                       } else {
-                               moveSelect(8);
-                       }
-               },
-               hide: function() {
-                       element && element.hide();
-                       listItems && listItems.removeClass(CLASSES.ACTIVE);
-                       active = -1;
-               },
-               visible : function() {
-                       return element && element.is(":visible");
-               },
-               current: function() {
-                       return this.visible() && (listItems.filter("." + CLASSES.ACTIVE)[0] || options.selectFirst && listItems[0]);
-               },
-               show: function() {
-                       var offset = $(input).offset();
-                       element.css({
-                               width: typeof options.width == "string" || options.width > 0 ? options.width : $(input).width(),
-                               top: offset.top + input.offsetHeight,
-                               left: offset.left
-                       }).show();
-            if(options.scroll) {
-                list.scrollTop(0);
-                list.css({
-                                       maxHeight: options.scrollHeight,
-                                       overflow: 'auto'
-                               });
-
-                if($.browser.msie && typeof document.body.style.maxHeight === "undefined") {
-                                       var listHeight = 0;
-                                       listItems.each(function() {
-                                               listHeight += this.offsetHeight;
-                                       });
-                                       var scrollbarsVisible = listHeight > options.scrollHeight;
-                    list.css('height', scrollbarsVisible ? options.scrollHeight : listHeight );
-                                       if (!scrollbarsVisible) {
-                                               // IE doesn't recalculate width when scrollbar disappears
-                                               listItems.width( list.width() - parseInt(listItems.css("padding-left")) - parseInt(listItems.css("padding-right")) );
-                                       }
-                }
-
-            }
-               },
-               selected: function() {
-                       var selected = listItems && listItems.filter("." + CLASSES.ACTIVE).removeClass(CLASSES.ACTIVE);
-                       return selected && selected.length && $.data(selected[0], "ac_data");
-               },
-               emptyList: function (){
-                       list && list.empty();
-               },
-               unbind: function() {
-                       element && element.remove();
-               }
-       };
-};
-
-$.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
-               }
-       }
-};
-
-})(jQuery);
\ No newline at end of file
diff --git a/redakcja/static/js/lib/jquery/jquery.blockui.js b/redakcja/static/js/lib/jquery/jquery.blockui.js
deleted file mode 100644 (file)
index 5491667..0000000
+++ /dev/null
@@ -1,477 +0,0 @@
-/*!
- * jQuery blockUI plugin
- * Version 2.31 (06-JAN-2010)
- * @requires jQuery v1.2.3 or later
- *
- * Examples at: http://malsup.com/jquery/block/
- * Copyright (c) 2007-2008 M. Alsup
- * Dual licensed under the MIT and GPL licenses:
- * http://www.opensource.org/licenses/mit-license.php
- * http://www.gnu.org/licenses/gpl.html
- *
- * Thanks to Amir-Hossein Sobhi for some excellent contributions!
- */
-
-;(function($) {
-
-if (/1\.(0|1|2)\.(0|1|2)/.test($.fn.jquery) || /^1.1/.test($.fn.jquery)) {
-       alert('blockUI requires jQuery v1.2.3 or later!  You are using v' + $.fn.jquery);
-       return;
-}
-
-$.fn._fadeIn = $.fn.fadeIn;
-
-var noOp = function() {};
-
-// this bit is to ensure we don't call setExpression when we shouldn't (with extra muscle to handle
-// retarded userAgent strings on Vista)
-var mode = document.documentMode || 0;
-var setExpr = $.browser.msie && (($.browser.version < 8 && !mode) || mode < 8);
-var ie6 = $.browser.msie && /MSIE 6.0/.test(navigator.userAgent) && !mode;
-
-// global $ methods for blocking/unblocking the entire page
-$.blockUI   = function(opts) { install(window, opts); };
-$.unblockUI = function(opts) { remove(window, opts); };
-
-// convenience method for quick growl-like notifications  (http://www.google.com/search?q=growl)
-$.growlUI = function(title, message, timeout, onClose) {
-       var $m = $('<div class="growlUI"></div>');
-       if (title) $m.append('<h1>'+title+'</h1>');
-       if (message) $m.append('<h2>'+message+'</h2>');
-       if (timeout == undefined) timeout = 3000;
-       $.blockUI({
-               message: $m, fadeIn: 700, fadeOut: 1000, centerY: false,
-               timeout: timeout, showOverlay: false,
-               onUnblock: onClose,
-               css: $.blockUI.defaults.growlCSS
-       });
-};
-
-// plugin method for blocking element content
-$.fn.block = function(opts) {
-       return this.unblock({ fadeOut: 0 }).each(function() {
-               if ($.css(this,'position') == 'static')
-                       this.style.position = 'relative';
-               if ($.browser.msie)
-                       this.style.zoom = 1; // force 'hasLayout'
-               install(this, opts);
-       });
-};
-
-// plugin method for unblocking element content
-$.fn.unblock = function(opts) {
-       return this.each(function() {
-               remove(this, opts);
-       });
-};
-
-$.blockUI.version = 2.31; // 2nd generation blocking at no extra cost!
-
-// override these in your code to change the default behavior and style
-$.blockUI.defaults = {
-       // message displayed when blocking (use null for no message)
-       message:  '<h1>Please wait...</h1>',
-
-       title: null,      // title string; only used when theme == true
-       draggable: true,  // only used when theme == true (requires jquery-ui.js to be loaded)
-
-       theme: false, // set to true to use with jQuery UI themes
-
-       // styles for the message when blocking; if you wish to disable
-       // these and use an external stylesheet then do this in your code:
-       // $.blockUI.defaults.css = {};
-       css: {
-               padding:        0,
-               margin:         0,
-               width:          '30%',
-               top:            '40%',
-               left:           '35%',
-               textAlign:      'center',
-               color:          '#000',
-               border:         '3px solid #aaa',
-               backgroundColor:'#fff',
-               cursor:         'wait'
-       },
-
-       // minimal style set used when themes are used
-       themedCSS: {
-               width:  '30%',
-               top:    '40%',
-               left:   '35%'
-       },
-
-       // styles for the overlay
-       overlayCSS:  {
-               backgroundColor: '#000',
-               opacity:                 0.6,
-               cursor:                  'wait'
-       },
-
-       // styles applied when using $.growlUI
-       growlCSS: {
-               width:          '350px',
-               top:            '10px',
-               left:           '',
-               right:          '10px',
-               border:         'none',
-               padding:        '5px',
-               opacity:        0.6,
-               cursor:         'default',
-               color:          '#fff',
-               backgroundColor: '#000',
-               '-webkit-border-radius': '10px',
-               '-moz-border-radius':    '10px'
-       },
-
-       // IE issues: 'about:blank' fails on HTTPS and javascript:false is s-l-o-w
-       // (hat tip to Jorge H. N. de Vasconcelos)
-       iframeSrc: /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank',
-
-       // force usage of iframe in non-IE browsers (handy for blocking applets)
-       forceIframe: false,
-
-       // z-index for the blocking overlay
-       baseZ: 1000,
-
-       // set these to true to have the message automatically centered
-       centerX: true, // <-- only effects element blocking (page block controlled via css above)
-       centerY: true,
-
-       // allow body element to be stetched in ie6; this makes blocking look better
-       // on "short" pages.  disable if you wish to prevent changes to the body height
-       allowBodyStretch: true,
-
-       // enable if you want key and mouse events to be disabled for content that is blocked
-       bindEvents: true,
-
-       // be default blockUI will supress tab navigation from leaving blocking content
-       // (if bindEvents is true)
-       constrainTabKey: true,
-
-       // fadeIn time in millis; set to 0 to disable fadeIn on block
-       fadeIn:  200,
-
-       // fadeOut time in millis; set to 0 to disable fadeOut on unblock
-       fadeOut:  400,
-
-       // time in millis to wait before auto-unblocking; set to 0 to disable auto-unblock
-       timeout: 0,
-
-       // disable if you don't want to show the overlay
-       showOverlay: true,
-
-       // if true, focus will be placed in the first available input field when
-       // page blocking
-       focusInput: true,
-
-       // suppresses the use of overlay styles on FF/Linux (due to performance issues with opacity)
-       applyPlatformOpacityRules: true,
-
-       // callback method invoked when fadeIn has completed and blocking message is visible
-       onBlock: null,
-
-       // callback method invoked when unblocking has completed; the callback is
-       // passed the element that has been unblocked (which is the window object for page
-       // blocks) and the options that were passed to the unblock call:
-       //       onUnblock(element, options)
-       onUnblock: null,
-
-       // don't ask; if you really must know: http://groups.google.com/group/jquery-en/browse_thread/thread/36640a8730503595/2f6a79a77a78e493#2f6a79a77a78e493
-       quirksmodeOffsetHack: 4
-};
-
-// private data and functions follow...
-
-var pageBlock = null;
-var pageBlockEls = [];
-
-function install(el, opts) {
-       var full = (el == window);
-       var msg = opts && opts.message !== undefined ? opts.message : undefined;
-       opts = $.extend({}, $.blockUI.defaults, opts || {});
-       opts.overlayCSS = $.extend({}, $.blockUI.defaults.overlayCSS, opts.overlayCSS || {});
-       var css = $.extend({}, $.blockUI.defaults.css, opts.css || {});
-       var themedCSS = $.extend({}, $.blockUI.defaults.themedCSS, opts.themedCSS || {});
-       msg = msg === undefined ? opts.message : msg;
-
-       // remove the current block (if there is one)
-       if (full && pageBlock)
-               remove(window, {fadeOut:0});
-
-       // if an existing element is being used as the blocking content then we capture
-       // its current place in the DOM (and current display style) so we can restore
-       // it when we unblock
-       if (msg && typeof msg != 'string' && (msg.parentNode || msg.jquery)) {
-               var node = msg.jquery ? msg[0] : msg;
-               var data = {};
-               $(el).data('blockUI.history', data);
-               data.el = node;
-               data.parent = node.parentNode;
-               data.display = node.style.display;
-               data.position = node.style.position;
-               if (data.parent)
-                       data.parent.removeChild(node);
-       }
-
-       var z = opts.baseZ;
-
-       // blockUI uses 3 layers for blocking, for simplicity they are all used on every platform;
-       // layer1 is the iframe layer which is used to supress bleed through of underlying content
-       // layer2 is the overlay layer which has opacity and a wait cursor (by default)
-       // layer3 is the message content that is displayed while blocking
-
-       var lyr1 = ($.browser.msie || opts.forceIframe)
-               ? $('<iframe class="blockUI" style="z-index:'+ (z++) +';display:none;border:none;margin:0;padding:0;position:absolute;width:100%;height:100%;top:0;left:0" src="'+opts.iframeSrc+'"></iframe>')
-               : $('<div class="blockUI" style="display:none"></div>');
-       var lyr2 = $('<div class="blockUI blockOverlay" style="z-index:'+ (z++) +';display:none;border:none;margin:0;padding:0;width:100%;height:100%;top:0;left:0"></div>');
-
-       var lyr3;
-       if (opts.theme && full) {
-               var s = '<div class="blockUI blockMsg blockPage ui-dialog ui-widget ui-corner-all" style="z-index:'+z+';display:none;position:fixed">' +
-                                       '<div class="ui-widget-header ui-dialog-titlebar blockTitle">'+(opts.title || '&nbsp;')+'</div>' +
-                                       '<div class="ui-widget-content ui-dialog-content"></div>' +
-                               '</div>';
-               lyr3 = $(s);
-       }
-       else {
-               lyr3 = full ? $('<div class="blockUI blockMsg blockPage" style="z-index:'+z+';display:none;position:fixed"></div>')
-                                       : $('<div class="blockUI blockMsg blockElement" style="z-index:'+z+';display:none;position:absolute"></div>');
-       }
-
-       // if we have a message, style it
-       if (msg) {
-               if (opts.theme) {
-                       lyr3.css(themedCSS);
-                       lyr3.addClass('ui-widget-content');
-               }
-               else
-                       lyr3.css(css);
-       }
-
-       // style the overlay
-       if (!opts.applyPlatformOpacityRules || !($.browser.mozilla && /Linux/.test(navigator.platform)))
-               lyr2.css(opts.overlayCSS);
-       lyr2.css('position', full ? 'fixed' : 'absolute');
-
-       // make iframe layer transparent in IE
-       if ($.browser.msie || opts.forceIframe)
-               lyr1.css('opacity',0.0);
-
-       //$([lyr1[0],lyr2[0],lyr3[0]]).appendTo(full ? 'body' : el);
-       var layers = [lyr1,lyr2,lyr3], $par = full ? $('body') : $(el);
-       $.each(layers, function() {
-               this.appendTo($par);
-       });
-
-       if (opts.theme && opts.draggable && $.fn.draggable) {
-               lyr3.draggable({
-                       handle: '.ui-dialog-titlebar',
-                       cancel: 'li'
-               });
-       }
-
-       // ie7 must use absolute positioning in quirks mode and to account for activex issues (when scrolling)
-       var expr = setExpr && (!$.boxModel || $('object,embed', full ? null : el).length > 0);
-       if (ie6 || expr) {
-               // give body 100% height
-               if (full && opts.allowBodyStretch && $.boxModel)
-                       $('html,body').css('height','100%');
-
-               // fix ie6 issue when blocked element has a border width
-               if ((ie6 || !$.boxModel) && !full) {
-                       var t = sz(el,'borderTopWidth'), l = sz(el,'borderLeftWidth');
-                       var fixT = t ? '(0 - '+t+')' : 0;
-                       var fixL = l ? '(0 - '+l+')' : 0;
-               }
-
-               // simulate fixed position
-               $.each([lyr1,lyr2,lyr3], function(i,o) {
-                       var s = o[0].style;
-                       s.position = 'absolute';
-                       if (i < 2) {
-                               full ? s.setExpression('height','Math.max(document.body.scrollHeight, document.body.offsetHeight) - (jQuery.boxModel?0:'+opts.quirksmodeOffsetHack+') + "px"')
-                                        : s.setExpression('height','this.parentNode.offsetHeight + "px"');
-                               full ? s.setExpression('width','jQuery.boxModel && document.documentElement.clientWidth || document.body.clientWidth + "px"')
-                                        : s.setExpression('width','this.parentNode.offsetWidth + "px"');
-                               if (fixL) s.setExpression('left', fixL);
-                               if (fixT) s.setExpression('top', fixT);
-                       }
-                       else if (opts.centerY) {
-                               if (full) s.setExpression('top','(document.documentElement.clientHeight || document.body.clientHeight) / 2 - (this.offsetHeight / 2) + (blah = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop) + "px"');
-                               s.marginTop = 0;
-                       }
-                       else if (!opts.centerY && full) {
-                               var top = (opts.css && opts.css.top) ? parseInt(opts.css.top) : 0;
-                               var expression = '((document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop) + '+top+') + "px"';
-                               s.setExpression('top',expression);
-                       }
-               });
-       }
-
-       // show the message
-       if (msg) {
-               if (opts.theme)
-                       lyr3.find('.ui-widget-content').append(msg);
-               else
-                       lyr3.append(msg);
-               if (msg.jquery || msg.nodeType)
-                       $(msg).show();
-       }
-
-       if (($.browser.msie || opts.forceIframe) && opts.showOverlay)
-               lyr1.show(); // opacity is zero
-       if (opts.fadeIn) {
-               var cb = opts.onBlock ? opts.onBlock : noOp;
-               var cb1 = (opts.showOverlay && !msg) ? cb : noOp;
-               var cb2 = msg ? cb : noOp;
-               if (opts.showOverlay)
-                       lyr2._fadeIn(opts.fadeIn, cb1);
-               if (msg)
-                       lyr3._fadeIn(opts.fadeIn, cb2);
-       }
-       else {
-               if (opts.showOverlay)
-                       lyr2.show();
-               if (msg)
-                       lyr3.show();
-               if (opts.onBlock)
-                       opts.onBlock();
-       }
-
-       // bind key and mouse events
-       bind(1, el, opts);
-
-       if (full) {
-               pageBlock = lyr3[0];
-               pageBlockEls = $(':input:enabled:visible',pageBlock);
-               if (opts.focusInput)
-                       setTimeout(focus, 20);
-       }
-       else
-               center(lyr3[0], opts.centerX, opts.centerY);
-
-       if (opts.timeout) {
-               // auto-unblock
-               var to = setTimeout(function() {
-                       full ? $.unblockUI(opts) : $(el).unblock(opts);
-               }, opts.timeout);
-               $(el).data('blockUI.timeout', to);
-       }
-};
-
-// remove the block
-function remove(el, opts) {
-       var full = (el == window);
-       var $el = $(el);
-       var data = $el.data('blockUI.history');
-       var to = $el.data('blockUI.timeout');
-       if (to) {
-               clearTimeout(to);
-               $el.removeData('blockUI.timeout');
-       }
-       opts = $.extend({}, $.blockUI.defaults, opts || {});
-       bind(0, el, opts); // unbind events
-
-       var els;
-       if (full) // crazy selector to handle odd field errors in ie6/7
-               els = $('body').children().filter('.blockUI').add('body > .blockUI');
-       else
-               els = $('.blockUI', el);
-
-       if (full)
-               pageBlock = pageBlockEls = null;
-
-       if (opts.fadeOut) {
-               els.fadeOut(opts.fadeOut);
-               setTimeout(function() { reset(els,data,opts,el); }, opts.fadeOut);
-       }
-       else
-               reset(els, data, opts, el);
-};
-
-// move blocking element back into the DOM where it started
-function reset(els,data,opts,el) {
-       els.each(function(i,o) {
-               // remove via DOM calls so we don't lose event handlers
-               if (this.parentNode)
-                       this.parentNode.removeChild(this);
-       });
-
-       if (data && data.el) {
-               data.el.style.display = data.display;
-               data.el.style.position = data.position;
-               if (data.parent)
-                       data.parent.appendChild(data.el);
-               $(el).removeData('blockUI.history');
-       }
-
-       if (typeof opts.onUnblock == 'function')
-               opts.onUnblock(el,opts);
-};
-
-// bind/unbind the handler
-function bind(b, el, opts) {
-       var full = el == window, $el = $(el);
-
-       // don't bother unbinding if there is nothing to unbind
-       if (!b && (full && !pageBlock || !full && !$el.data('blockUI.isBlocked')))
-               return;
-       if (!full)
-               $el.data('blockUI.isBlocked', b);
-
-       // don't bind events when overlay is not in use or if bindEvents is false
-       if (!opts.bindEvents || (b && !opts.showOverlay))
-               return;
-
-       // bind anchors and inputs for mouse and key events
-       var events = 'mousedown mouseup keydown keypress';
-       b ? $(document).bind(events, opts, handler) : $(document).unbind(events, handler);
-
-// former impl...
-//        var $e = $('a,:input');
-//        b ? $e.bind(events, opts, handler) : $e.unbind(events, handler);
-};
-
-// event handler to suppress keyboard/mouse events when blocking
-function handler(e) {
-       // allow tab navigation (conditionally)
-       if (e.keyCode && e.keyCode == 9) {
-               if (pageBlock && e.data.constrainTabKey) {
-                       var els = pageBlockEls;
-                       var fwd = !e.shiftKey && e.target == els[els.length-1];
-                       var back = e.shiftKey && e.target == els[0];
-                       if (fwd || back) {
-                               setTimeout(function(){focus(back)},10);
-                               return false;
-                       }
-               }
-       }
-       // allow events within the message content
-       if ($(e.target).parents('div.blockMsg').length > 0)
-               return true;
-
-       // allow events for content that is not being blocked
-       return $(e.target).parents().children().filter('div.blockUI').length == 0;
-};
-
-function focus(back) {
-       if (!pageBlockEls)
-               return;
-       var e = pageBlockEls[back===true ? pageBlockEls.length-1 : 0];
-       if (e)
-               e.focus();
-};
-
-function center(el, x, y) {
-       var p = el.parentNode, s = el.style;
-       var l = ((p.offsetWidth - el.offsetWidth)/2) - sz(p,'borderLeftWidth');
-       var t = ((p.offsetHeight - el.offsetHeight)/2) - sz(p,'borderTopWidth');
-       if (x) s.left = l > 0 ? (l+'px') : '0';
-       if (y) s.top  = t > 0 ? (t+'px') : '0';
-};
-
-function sz(el, p) {
-       return parseInt($.css(el,p))||0;
-};
-
-})(jQuery);
\ No newline at end of file
diff --git a/redakcja/static/js/lib/jquery/jquery.elastic.js b/redakcja/static/js/lib/jquery/jquery.elastic.js
deleted file mode 100644 (file)
index 24e16f4..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-(function(jQuery){jQuery.fn.extend({elastic:function(){var mimics=['paddingTop','paddingRight','paddingBottom','paddingLeft','fontSize','lineHeight','fontFamily','width','fontWeight'];return this.each(function(){if(this.type!='textarea'){return false;}
-var $textarea=jQuery(this),$twin=jQuery('<div />').css({'position':'absolute','display':'none','word-wrap':'break-word'}),lineHeight=parseInt($textarea.css('line-height'),10)||parseInt($textarea.css('font-size'),'10'),minheight=parseInt($textarea.css('height'),10)||lineHeight*3,maxheight=parseInt($textarea.css('max-height'),10)||Number.MAX_VALUE,goalheight=0,i=0;if(maxheight<0){maxheight=Number.MAX_VALUE;}
-$twin.appendTo($textarea.parent());var i=mimics.length;while(i--){$twin.css(mimics[i].toString(),$textarea.css(mimics[i].toString()));}
-function setHeightAndOverflow(height,overflow){curratedHeight=Math.floor(parseInt(height,10));if($textarea.height()!=curratedHeight){$textarea.css({'height':curratedHeight+'px','overflow':overflow});}}
-function update(){var textareaContent=$textarea.val().replace(/&/g,'&amp;').replace(/  /g,'&nbsp;').replace(/<|>/g,'&gt;').replace(/\n/g,'<br />');var twinContent=$twin.html();if(textareaContent+'&nbsp;'!=twinContent){$twin.html(textareaContent+'&nbsp;');if(Math.abs($twin.height()+lineHeight-$textarea.height())>3){var goalheight=$twin.height()+lineHeight;if(goalheight>=maxheight){setHeightAndOverflow(maxheight,'auto');}else if(goalheight<=minheight){setHeightAndOverflow(minheight,'hidden');}else{setHeightAndOverflow(goalheight,'hidden');}}}}
-$textarea.css({'overflow':'hidden'});$textarea.keyup(function(){update();});$textarea.live('input paste',function(e){setTimeout(update,250);});update();});}});})(jQuery);
\ No newline at end of file
diff --git a/redakcja/static/js/lib/jquery/jquery.imgareaselect.js b/redakcja/static/js/lib/jquery/jquery.imgareaselect.js
deleted file mode 100644 (file)
index 7e1b790..0000000
+++ /dev/null
@@ -1,730 +0,0 @@
-/*
- * imgAreaSelect jQuery plugin
- * version 0.9.10
- *
- * Copyright (c) 2008-2013 Michal Wojciechowski (odyniec.net)
- *
- * Dual licensed under the MIT (MIT-LICENSE.txt)
- * and GPL (GPL-LICENSE.txt) licenses.
- *
- * http://odyniec.net/projects/imgareaselect/
- *
- */
-
-(function($) {
-
-var abs = Math.abs,
-    max = Math.max,
-    min = Math.min,
-    round = Math.round;
-
-function div() {
-    return $('<div/>');
-}
-
-$.imgAreaSelect = function (img, options) {
-    var
-
-        $img = $(img),
-
-        imgLoaded,
-
-        $box = div(),
-        $area = div(),
-        $border = div().add(div()).add(div()).add(div()),
-        $outer = div().add(div()).add(div()).add(div()),
-        $handles = $([]),
-
-        $areaOpera,
-
-        left, top,
-
-        imgOfs = { left: 0, top: 0 },
-
-        imgWidth, imgHeight,
-
-        $parent,
-
-        parOfs = { left: 0, top: 0 },
-
-        zIndex = 0,
-
-        position = 'absolute',
-
-        startX, startY,
-
-        scaleX, scaleY,
-
-        resize,
-
-        minWidth, minHeight, maxWidth, maxHeight,
-
-        aspectRatio,
-
-        shown,
-
-        x1, y1, x2, y2,
-
-        selection = { x1: 0, y1: 0, x2: 0, y2: 0, width: 0, height: 0 },
-
-        docElem = document.documentElement,
-
-        ua = navigator.userAgent,
-
-        $p, d, i, o, w, h, adjusted;
-
-    function viewX(x) {
-        return x + imgOfs.left - parOfs.left;
-    }
-
-    function viewY(y) {
-        return y + imgOfs.top - parOfs.top;
-    }
-
-    function selX(x) {
-        return x - imgOfs.left + parOfs.left;
-    }
-
-    function selY(y) {
-        return y - imgOfs.top + parOfs.top;
-    }
-
-    function evX(event) {
-        return event.pageX - parOfs.left;
-    }
-
-    function evY(event) {
-        return event.pageY - parOfs.top;
-    }
-
-    function getSelection(noScale) {
-        var sx = noScale || scaleX, sy = noScale || scaleY;
-
-        return { x1: round(selection.x1 * sx),
-            y1: round(selection.y1 * sy),
-            x2: round(selection.x2 * sx),
-            y2: round(selection.y2 * sy),
-            width: round(selection.x2 * sx) - round(selection.x1 * sx),
-            height: round(selection.y2 * sy) - round(selection.y1 * sy) };
-    }
-
-    function setSelection(x1, y1, x2, y2, noScale) {
-        var sx = noScale || scaleX, sy = noScale || scaleY;
-
-        selection = {
-            x1: round(x1 / sx || 0),
-            y1: round(y1 / sy || 0),
-            x2: round(x2 / sx || 0),
-            y2: round(y2 / sy || 0)
-        };
-
-        selection.width = selection.x2 - selection.x1;
-        selection.height = selection.y2 - selection.y1;
-    }
-
-    function adjust() {
-        if (!imgLoaded || !$img.width())
-            return;
-
-        imgOfs = { left: round($img.offset().left), top: round($img.offset().top) };
-
-        imgWidth = $img.innerWidth();
-        imgHeight = $img.innerHeight();
-
-        imgOfs.top += ($img.outerHeight() - imgHeight) >> 1;
-        imgOfs.left += ($img.outerWidth() - imgWidth) >> 1;
-
-        minWidth = round(options.minWidth / scaleX) || 0;
-        minHeight = round(options.minHeight / scaleY) || 0;
-        maxWidth = round(min(options.maxWidth / scaleX || 1<<24, imgWidth));
-        maxHeight = round(min(options.maxHeight / scaleY || 1<<24, imgHeight));
-
-        if ($().jquery == '1.3.2' && position == 'fixed' &&
-            !docElem['getBoundingClientRect'])
-        {
-            imgOfs.top += max(document.body.scrollTop, docElem.scrollTop);
-            imgOfs.left += max(document.body.scrollLeft, docElem.scrollLeft);
-        }
-
-        parOfs = /absolute|relative/.test($parent.css('position')) ?
-            { left: round($parent.offset().left) - $parent.scrollLeft(),
-                top: round($parent.offset().top) - $parent.scrollTop() } :
-            position == 'fixed' ?
-                { left: $(document).scrollLeft(), top: $(document).scrollTop() } :
-                { left: 0, top: 0 };
-
-        left = viewX(0);
-        top = viewY(0);
-
-        if (selection.x2 > imgWidth || selection.y2 > imgHeight)
-            doResize();
-    }
-
-    function update(resetKeyPress) {
-        if (!shown) return;
-
-        $box.css({ left: viewX(selection.x1), top: viewY(selection.y1) })
-            .add($area).width(w = selection.width).height(h = selection.height);
-
-        $area.add($border).add($handles).css({ left: 0, top: 0 });
-
-        $border
-            .width(max(w - $border.outerWidth() + $border.innerWidth(), 0))
-            .height(max(h - $border.outerHeight() + $border.innerHeight(), 0));
-
-        $($outer[0]).css({ left: left, top: top,
-            width: selection.x1, height: imgHeight });
-        $($outer[1]).css({ left: left + selection.x1, top: top,
-            width: w, height: selection.y1 });
-        $($outer[2]).css({ left: left + selection.x2, top: top,
-            width: imgWidth - selection.x2, height: imgHeight });
-        $($outer[3]).css({ left: left + selection.x1, top: top + selection.y2,
-            width: w, height: imgHeight - selection.y2 });
-
-        w -= $handles.outerWidth();
-        h -= $handles.outerHeight();
-
-        switch ($handles.length) {
-        case 8:
-            $($handles[4]).css({ left: w >> 1 });
-            $($handles[5]).css({ left: w, top: h >> 1 });
-            $($handles[6]).css({ left: w >> 1, top: h });
-            $($handles[7]).css({ top: h >> 1 });
-        case 4:
-            $handles.slice(1,3).css({ left: w });
-            $handles.slice(2,4).css({ top: h });
-        }
-
-        if (resetKeyPress !== false) {
-            if ($.imgAreaSelect.onKeyPress != docKeyPress)
-                $(document).unbind($.imgAreaSelect.keyPress,
-                    $.imgAreaSelect.onKeyPress);
-
-            if (options.keys)
-                $(document)[$.imgAreaSelect.keyPress](
-                    $.imgAreaSelect.onKeyPress = docKeyPress);
-        }
-
-        if (msie && $border.outerWidth() - $border.innerWidth() == 2) {
-            $border.css('margin', 0);
-            setTimeout(function () { $border.css('margin', 'auto'); }, 0);
-        }
-    }
-
-    function doUpdate(resetKeyPress) {
-        adjust();
-        update(resetKeyPress);
-        x1 = viewX(selection.x1); y1 = viewY(selection.y1);
-        x2 = viewX(selection.x2); y2 = viewY(selection.y2);
-    }
-
-    function hide($elem, fn) {
-        options.fadeSpeed ? $elem.fadeOut(options.fadeSpeed, fn) : $elem.hide();
-
-    }
-
-    function areaMouseMove(event) {
-        var x = selX(evX(event)) - selection.x1,
-            y = selY(evY(event)) - selection.y1;
-
-        if (!adjusted) {
-            adjust();
-            adjusted = true;
-
-            $box.one('mouseout', function () { adjusted = false; });
-        }
-
-        resize = '';
-
-        if (options.resizable) {
-            if (y <= options.resizeMargin)
-                resize = 'n';
-            else if (y >= selection.height - options.resizeMargin)
-                resize = 's';
-            if (x <= options.resizeMargin)
-                resize += 'w';
-            else if (x >= selection.width - options.resizeMargin)
-                resize += 'e';
-        }
-
-        $box.css('cursor', resize ? resize + '-resize' :
-            options.movable ? 'move' : '');
-        if ($areaOpera)
-            $areaOpera.toggle();
-    }
-
-    function docMouseUp(event) {
-        $('body').css('cursor', '');
-        if (options.autoHide || selection.width * selection.height == 0)
-            hide($box.add($outer), function () { $(this).hide(); });
-
-        $(document).unbind('mousemove', selectingMouseMove);
-        $box.mousemove(areaMouseMove);
-
-        options.onSelectEnd(img, getSelection());
-    }
-
-    function areaMouseDown(event) {
-        if (event.which != 1) return false;
-
-        adjust();
-
-        if (resize) {
-            $('body').css('cursor', resize + '-resize');
-
-            x1 = viewX(selection[/w/.test(resize) ? 'x2' : 'x1']);
-            y1 = viewY(selection[/n/.test(resize) ? 'y2' : 'y1']);
-
-            $(document).mousemove(selectingMouseMove)
-                .one('mouseup', docMouseUp);
-            $box.unbind('mousemove', areaMouseMove);
-        }
-        else if (options.movable) {
-            startX = left + selection.x1 - evX(event);
-            startY = top + selection.y1 - evY(event);
-
-            $box.unbind('mousemove', areaMouseMove);
-
-            $(document).mousemove(movingMouseMove)
-                .one('mouseup', function () {
-                    options.onSelectEnd(img, getSelection());
-
-                    $(document).unbind('mousemove', movingMouseMove);
-                    $box.mousemove(areaMouseMove);
-                });
-        }
-        else
-            $img.mousedown(event);
-
-        return false;
-    }
-
-    function fixAspectRatio(xFirst) {
-        if (aspectRatio)
-            if (xFirst) {
-                x2 = max(left, min(left + imgWidth,
-                    x1 + abs(y2 - y1) * aspectRatio * (x2 > x1 || -1)));
-
-                y2 = round(max(top, min(top + imgHeight,
-                    y1 + abs(x2 - x1) / aspectRatio * (y2 > y1 || -1))));
-                x2 = round(x2);
-            }
-            else {
-                y2 = max(top, min(top + imgHeight,
-                    y1 + abs(x2 - x1) / aspectRatio * (y2 > y1 || -1)));
-                x2 = round(max(left, min(left + imgWidth,
-                    x1 + abs(y2 - y1) * aspectRatio * (x2 > x1 || -1))));
-                y2 = round(y2);
-            }
-    }
-
-    function doResize() {
-        x1 = min(x1, left + imgWidth);
-        y1 = min(y1, top + imgHeight);
-
-        if (abs(x2 - x1) < minWidth) {
-            x2 = x1 - minWidth * (x2 < x1 || -1);
-
-            if (x2 < left)
-                x1 = left + minWidth;
-            else if (x2 > left + imgWidth)
-                x1 = left + imgWidth - minWidth;
-        }
-
-        if (abs(y2 - y1) < minHeight) {
-            y2 = y1 - minHeight * (y2 < y1 || -1);
-
-            if (y2 < top)
-                y1 = top + minHeight;
-            else if (y2 > top + imgHeight)
-                y1 = top + imgHeight - minHeight;
-        }
-
-        x2 = max(left, min(x2, left + imgWidth));
-        y2 = max(top, min(y2, top + imgHeight));
-
-        fixAspectRatio(abs(x2 - x1) < abs(y2 - y1) * aspectRatio);
-
-        if (abs(x2 - x1) > maxWidth) {
-            x2 = x1 - maxWidth * (x2 < x1 || -1);
-            fixAspectRatio();
-        }
-
-        if (abs(y2 - y1) > maxHeight) {
-            y2 = y1 - maxHeight * (y2 < y1 || -1);
-            fixAspectRatio(true);
-        }
-
-        selection = { x1: selX(min(x1, x2)), x2: selX(max(x1, x2)),
-            y1: selY(min(y1, y2)), y2: selY(max(y1, y2)),
-            width: abs(x2 - x1), height: abs(y2 - y1) };
-
-        update();
-
-        options.onSelectChange(img, getSelection());
-    }
-
-    function selectingMouseMove(event) {
-        x2 = /w|e|^$/.test(resize) || aspectRatio ? evX(event) : viewX(selection.x2);
-        y2 = /n|s|^$/.test(resize) || aspectRatio ? evY(event) : viewY(selection.y2);
-
-        doResize();
-
-        return false;
-
-    }
-
-    function doMove(newX1, newY1) {
-        x2 = (x1 = newX1) + selection.width;
-        y2 = (y1 = newY1) + selection.height;
-
-        $.extend(selection, { x1: selX(x1), y1: selY(y1), x2: selX(x2),
-            y2: selY(y2) });
-
-        update();
-
-        options.onSelectChange(img, getSelection());
-    }
-
-    function movingMouseMove(event) {
-        x1 = max(left, min(startX + evX(event), left + imgWidth - selection.width));
-        y1 = max(top, min(startY + evY(event), top + imgHeight - selection.height));
-
-        doMove(x1, y1);
-
-        event.preventDefault();
-
-        return false;
-    }
-
-    function startSelection() {
-        $(document).unbind('mousemove', startSelection);
-        adjust();
-
-        x2 = x1;
-        y2 = y1;
-
-        doResize();
-
-        resize = '';
-
-        if (!$outer.is(':visible'))
-            $box.add($outer).hide().fadeIn(options.fadeSpeed||0);
-
-        shown = true;
-
-        $(document).unbind('mouseup', cancelSelection)
-            .mousemove(selectingMouseMove).one('mouseup', docMouseUp);
-        $box.unbind('mousemove', areaMouseMove);
-
-        options.onSelectStart(img, getSelection());
-    }
-
-    function cancelSelection() {
-        $(document).unbind('mousemove', startSelection)
-            .unbind('mouseup', cancelSelection);
-        hide($box.add($outer));
-
-        setSelection(selX(x1), selY(y1), selX(x1), selY(y1));
-
-        if (!(this instanceof $.imgAreaSelect)) {
-            options.onSelectChange(img, getSelection());
-            options.onSelectEnd(img, getSelection());
-        }
-    }
-
-    function imgMouseDown(event) {
-        if (event.which != 1 || $outer.is(':animated')) return false;
-
-        adjust();
-        startX = x1 = evX(event);
-        startY = y1 = evY(event);
-
-        $(document).mousemove(startSelection).mouseup(cancelSelection);
-
-        return false;
-    }
-
-    function windowResize() {
-        doUpdate(false);
-    }
-
-    function imgLoad() {
-        imgLoaded = true;
-
-        setOptions(options = $.extend({
-            classPrefix: 'imgareaselect',
-            movable: true,
-            parent: 'body',
-            resizable: true,
-            resizeMargin: 10,
-            onInit: function () {},
-            onSelectStart: function () {},
-            onSelectChange: function () {},
-            onSelectEnd: function () {}
-        }, options));
-
-        $box.add($outer).css({ visibility: '' });
-
-        if (options.show) {
-            shown = true;
-            adjust();
-            update();
-            $box.add($outer).hide().fadeIn(options.fadeSpeed||0);
-        }
-
-        setTimeout(function () { options.onInit(img, getSelection()); }, 0);
-    }
-
-    var docKeyPress = function(event) {
-        var k = options.keys, d, t, key = event.keyCode;
-
-        d = !isNaN(k.alt) && (event.altKey || event.originalEvent.altKey) ? k.alt :
-            !isNaN(k.ctrl) && event.ctrlKey ? k.ctrl :
-            !isNaN(k.shift) && event.shiftKey ? k.shift :
-            !isNaN(k.arrows) ? k.arrows : 10;
-
-        if (k.arrows == 'resize' || (k.shift == 'resize' && event.shiftKey) ||
-            (k.ctrl == 'resize' && event.ctrlKey) ||
-            (k.alt == 'resize' && (event.altKey || event.originalEvent.altKey)))
-        {
-            switch (key) {
-            case 37:
-                d = -d;
-            case 39:
-                t = max(x1, x2);
-                x1 = min(x1, x2);
-                x2 = max(t + d, x1);
-                fixAspectRatio();
-                break;
-            case 38:
-                d = -d;
-            case 40:
-                t = max(y1, y2);
-                y1 = min(y1, y2);
-                y2 = max(t + d, y1);
-                fixAspectRatio(true);
-                break;
-            default:
-                return;
-            }
-
-            doResize();
-        }
-        else {
-            x1 = min(x1, x2);
-            y1 = min(y1, y2);
-
-            switch (key) {
-            case 37:
-                doMove(max(x1 - d, left), y1);
-                break;
-            case 38:
-                doMove(x1, max(y1 - d, top));
-                break;
-            case 39:
-                doMove(x1 + min(d, imgWidth - selX(x2)), y1);
-                break;
-            case 40:
-                doMove(x1, y1 + min(d, imgHeight - selY(y2)));
-                break;
-            default:
-                return;
-            }
-        }
-
-        return false;
-    };
-
-    function styleOptions($elem, props) {
-        for (var option in props)
-            if (options[option] !== undefined)
-                $elem.css(props[option], options[option]);
-    }
-
-    function setOptions(newOptions) {
-        if (newOptions.parent)
-            ($parent = $(newOptions.parent)).append($box.add($outer));
-
-        $.extend(options, newOptions);
-
-        adjust();
-
-        if (newOptions.handles != null) {
-            $handles.remove();
-            $handles = $([]);
-
-            i = newOptions.handles ? newOptions.handles == 'corners' ? 4 : 8 : 0;
-
-            while (i--)
-                $handles = $handles.add(div());
-
-            $handles.addClass(options.classPrefix + '-handle').css({
-                position: 'absolute',
-                fontSize: 0,
-                zIndex: zIndex + 1 || 1
-            });
-
-            if (!parseInt($handles.css('width')) >= 0)
-                $handles.width(5).height(5);
-
-            if (o = options.borderWidth)
-                $handles.css({ borderWidth: o, borderStyle: 'solid' });
-
-            styleOptions($handles, { borderColor1: 'border-color',
-                borderColor2: 'background-color',
-                borderOpacity: 'opacity' });
-        }
-
-        scaleX = options.imageWidth / imgWidth || 1;
-        scaleY = options.imageHeight / imgHeight || 1;
-
-        if (newOptions.x1 != null) {
-            setSelection(newOptions.x1, newOptions.y1, newOptions.x2,
-                newOptions.y2);
-            newOptions.show = !newOptions.hide;
-        }
-
-        if (newOptions.keys)
-            options.keys = $.extend({ shift: 1, ctrl: 'resize' },
-                newOptions.keys);
-
-        $outer.addClass(options.classPrefix + '-outer');
-        $area.addClass(options.classPrefix + '-selection');
-        for (i = 0; i++ < 4;)
-            $($border[i-1]).addClass(options.classPrefix + '-border' + i);
-
-        styleOptions($area, { selectionColor: 'background-color',
-            selectionOpacity: 'opacity' });
-        styleOptions($border, { borderOpacity: 'opacity',
-            borderWidth: 'border-width' });
-        styleOptions($outer, { outerColor: 'background-color',
-            outerOpacity: 'opacity' });
-        if (o = options.borderColor1)
-            $($border[0]).css({ borderStyle: 'solid', borderColor: o });
-        if (o = options.borderColor2)
-            $($border[1]).css({ borderStyle: 'dashed', borderColor: o });
-
-        $box.append($area.add($border).add($areaOpera)).append($handles);
-
-        if (msie) {
-            if (o = ($outer.css('filter')||'').match(/opacity=(\d+)/))
-                $outer.css('opacity', o[1]/100);
-            if (o = ($border.css('filter')||'').match(/opacity=(\d+)/))
-                $border.css('opacity', o[1]/100);
-        }
-
-        if (newOptions.hide)
-            hide($box.add($outer));
-        else if (newOptions.show && imgLoaded) {
-            shown = true;
-            $box.add($outer).fadeIn(options.fadeSpeed||0);
-            doUpdate();
-        }
-
-        aspectRatio = (d = (options.aspectRatio || '').split(/:/))[0] / d[1];
-
-        $img.add($outer).unbind('mousedown', imgMouseDown);
-
-        if (options.disable || options.enable === false) {
-            $box.unbind('mousemove', areaMouseMove).unbind('mousedown', areaMouseDown);
-            $(window).unbind('resize', windowResize);
-        }
-        else {
-            if (options.enable || options.disable === false) {
-                if (options.resizable || options.movable)
-                    $box.mousemove(areaMouseMove).mousedown(areaMouseDown);
-
-                $(window).resize(windowResize);
-            }
-
-            if (!options.persistent)
-                $img.add($outer).mousedown(imgMouseDown);
-        }
-
-        options.enable = options.disable = undefined;
-    }
-
-    this.remove = function () {
-        setOptions({ disable: true });
-        $box.add($outer).remove();
-    };
-
-    this.getOptions = function () { return options; };
-
-    this.setOptions = setOptions;
-
-    this.getSelection = getSelection;
-
-    this.setSelection = setSelection;
-
-    this.cancelSelection = cancelSelection;
-
-    this.update = doUpdate;
-
-    var msie = (/msie ([\w.]+)/i.exec(ua)||[])[1],
-        opera = /opera/i.test(ua),
-        safari = /webkit/i.test(ua) && !/chrome/i.test(ua);
-
-    $p = $img;
-
-    while ($p.length) {
-        zIndex = max(zIndex,
-            !isNaN($p.css('z-index')) ? $p.css('z-index') : zIndex);
-        if ($p.css('position') == 'fixed')
-            position = 'fixed';
-
-        $p = $p.parent(':not(body)');
-    }
-
-    zIndex = options.zIndex || zIndex;
-
-    if (msie)
-        $img.attr('unselectable', 'on');
-
-    $.imgAreaSelect.keyPress = msie || safari ? 'keydown' : 'keypress';
-
-    if (opera)
-
-        $areaOpera = div().css({ width: '100%', height: '100%',
-            position: 'absolute', zIndex: zIndex + 2 || 2 });
-
-    $box.add($outer).css({ visibility: 'hidden', position: position,
-        overflow: 'hidden', zIndex: zIndex || '0' });
-    $box.css({ zIndex: zIndex + 2 || 2 });
-    $area.add($border).css({ position: 'absolute', fontSize: 0 });
-
-    img.complete || img.readyState == 'complete' || !$img.is('img') ?
-        imgLoad() : $img.one('load', imgLoad);
-
-    if (!imgLoaded && msie && msie >= 7)
-        img.src = img.src;
-};
-
-$.fn.imgAreaSelect = function (options) {
-    options = options || {};
-
-    this.each(function () {
-        if ($(this).data('imgAreaSelect')) {
-            if (options.remove) {
-                $(this).data('imgAreaSelect').remove();
-                $(this).removeData('imgAreaSelect');
-            }
-            else
-                $(this).data('imgAreaSelect').setOptions(options);
-        }
-        else if (!options.remove) {
-            if (options.enable === undefined && options.disable === undefined)
-                options.enable = true;
-
-            $(this).data('imgAreaSelect', new $.imgAreaSelect(this, options));
-        }
-    });
-
-    if (options.instance)
-        return $(this).data('imgAreaSelect');
-
-    return this;
-};
-
-})(jQuery);
diff --git a/redakcja/static/js/lib/jquery/jquery.xmlns.js b/redakcja/static/js/lib/jquery/jquery.xmlns.js
deleted file mode 100644 (file)
index 96c5a55..0000000
+++ /dev/null
@@ -1,411 +0,0 @@
-//
-//  jquery.xmlns.js:  xml-namespace selector support for jQuery
-//
-//  This plugin modifies the jQuery tag and attribute selectors to
-//  support optional namespace specifiers as defined in CSS 3:
-//
-//    $("elem")      - matches 'elem' nodes in the default namespace
-//    $("|elem")     - matches 'elem' nodes that don't have a namespace
-//    $("NS|elem")   - matches 'elem' nodes in declared namespace 'NS'
-//    $("*|elem")    - matches 'elem' nodes in any namespace
-//    $("NS|*")      - matches any nodes in declared namespace 'NS'
-//
-//  A similar synax is also supported for attribute selectors, but note
-//  that the default namespace does *not* apply to attributes - a missing
-//  or empty namespace selector selects only attributes with no namespace.
-//
-//  In a slight break from the W3C standards, and with a nod to ease of
-//  implementation, the empty namespace URI is treated as equivalent to
-//  an unspecified namespace.  Plenty of browsers seem to make the same
-//  assumption...
-//
-//  Namespace declarations live in the $.xmlns object, which is a simple
-//  mapping from namespace ids to namespace URIs.  The default namespace
-//  is determined by the value associated with the empty string.
-//
-//    $.xmlns.D = "DAV:"
-//    $.xmlns.FOO = "http://www.foo.com/xmlns/foobar"
-//    $.xmlns[""] = "http://www.example.com/new/default/namespace/"
-//
-//  Unfortunately this is a global setting - I can't find a way to do
-//  query-object-specific namespaces since the jQuery selector machinery
-//  is stateless.  However, you can use the 'xmlns' function to push and
-//  pop namespace delcarations with ease:
-//
-//    $().xmlns({D:"DAV:"})     // pushes the DAV: namespace
-//    $().xmlns("DAV:")         // makes DAV: the default namespace
-//    $().xmlns(false)          // pops the namespace we just pushed
-//    $().xmlns(false)          // pops again, returning to defaults
-//
-//  To execute this as a kind of "transaction", pass a function as the
-//  second argument.  It will be executed in the context of the current
-//  jQuery object:
-//
-//    $().xmlns("DAV:",function() {
-//      //  The default namespace is DAV: within this function,
-//      //  but it is reset to the previous value on exit.
-//      return this.find("response").each(...);
-//    }).find("div")
-//
-//  If you pass a string as a function, it will be executed against the
-//  current jQuery object using find(); i.e. the following will find all
-//  "href" elements in the "DAV:" namespace:
-//
-//    $().xmlns("DAV:","href")
-//
-// 
-//  And finally, the legal stuff:
-//
-//    Copyright (c) 2009, Ryan Kelly.
-//    TAG and ATTR functions derived from jQuery's selector.js.
-//    Dual licensed under the MIT and GPL licenses.
-//    http://docs.jquery.com/License
-//
-
-(function($) {
-
-//  Some common default namespaces, that are treated specially by browsers.
-//
-var default_xmlns = {
-    "xml": "http://www.w3.org/XML/1998/namespace",
-    "xmlns": "http://www.w3.org/2000/xmlns/",
-    "html": "http://www.w3.org/1999/xhtml/"
-};
-
-//  A reverse mapping for common namespace prefixes.
-//
-var default_xmlns_rev = {}
-for(var k in default_xmlns) {
-    default_xmlns_rev[default_xmlns[k]] = k;
-}
-
-
-//  $.xmlns is a mapping from namespace identifiers to namespace URIs.
-//  The default default-namespace is "*", and we provide some additional
-//  defaults that are specified in the XML Namespaces standard.
-//
-$.extend({xmlns: $.extend({},default_xmlns,{"":"*"})});
-
-
-//  jQuery method to push/pop namespace declarations.
-//
-//  If a single argument is specified:
-//    * if it's a mapping, push those namespaces onto the stack
-//    * if it's a string, push that as the default namespace
-//    * if it evaluates to false, pop the latest namespace
-//
-//  If two arguments are specified, the second is executed "transactionally"
-//  using the namespace declarations found in the first.  It can be either a
-//  a selector string (in which case it is passed to this.find()) or a function
-//  (in which case it is called in the context of the current jQuery object).
-//  The given namespace mapping is automatically pushed before executing and
-//  popped afterwards.
-//
-var xmlns_stack = [];
-$.fn.extend({xmlns: function(nsmap,func) {
-    if(typeof nsmap == "string") {
-        nsmap = {"":nsmap};
-    }
-    if(nsmap) {
-        xmlns_stack.push($.xmlns);
-        $.xmlns = $.extend({},$.xmlns,nsmap);
-        if(func !== undefined) {
-            if(typeof func == "string") {
-                return this.find(func).xmlns(undefined)
-            } else {
-                var self = this;
-                try {
-                    self = func.call(this);
-                    if(!self) {
-                        self = this;
-                    }
-                } finally {
-                    self.xmlns(undefined);
-                }
-                return self
-            }
-        } else {
-            return this;
-        }
-    } else {
-        $.xmlns = (xmlns_stack ? xmlns_stack.pop() : {});
-        return this;
-    }
-}});
-
-
-//  Convert a namespace prefix into a namespace URI, based
-//  on the delcarations made in $.xmlns.
-//
-var getNamespaceURI = function(id) {
-    // No namespace id, use the default.
-    if(!id) {
-        return $.xmlns[""];
-    }
-    // Strip the pipe character from the specifier
-    id = id.substr(0,id.length-1);
-    // Certain special namespaces aren't mapped to a URI
-    if(id == "" || id == "*") {
-        return id;
-    }
-    var ns = $.xmlns[id];
-    if(typeof(ns) == "undefined") {
-        throw "Syntax error, undefined namespace prefix '" + id + "'";
-    }
-    return ns;
-};
-
-
-//  Update the regex used by $.expr to parse selector components for a
-//  particular type of selector (e.g. "TAG" or "ATTR").
-//
-//  This logic is taken straight from the jQuery/Sizzle sources.
-//
-var setExprMatchRegex = function(type,regex) {
-  $.expr.match[type] = new RegExp(regex.source + /(?![^\[]*\])(?![^\(]*\))/.source);
-  if($.expr.leftMatch) {
-      $.expr.leftMatch[type] = new RegExp(/(^(?:.|\r|\n)*?)/.source + $.expr.match[type].source.replace(/\\(\d+)/g, function(all, num){
-          return "\\" + (num - 0 + 1);
-      }));
-  }
-}
-
-
-
-//  Modify the TAG match regexp to include optional namespace selector.
-//  This is basically (namespace|)?(tagname).
-//
-setExprMatchRegex("TAG",/^((?:((?:[\w\u00c0-\uFFFF\*_-]*\|)?)((?:[\w\u00c0-\uFFFF\*_-]|\\.)+)))/);
-
-
-//  Perform some capability-testing.
-//
-var div = document.createElement("div");
-
-//  Sometimes getElementsByTagName("*") will return comment nodes,
-//  which we will have to remove from the results.
-//
-var gebtn_yields_comments = false;
-div.appendChild(document.createComment(""));
-if(div.getElementsByTagName("*").length > 0) {
-    gebtn_yields_comments = true;
-}
-
-//  Some browsers return node.localName in upper case, some in lower case.
-//
-var localname_is_uppercase = true;
-if(div.localName && div.localName == "div") {
-    localname_is_uppercase = false;
-}
-
-//  Allow the testing div to be garbage-collected.
-//
-div = null;
-
-
-//  Modify the TAG find function to account for a namespace selector.
-//
-$.expr.find.TAG = function(match,context,isXML) {
-    var ns = getNamespaceURI(match[2]);
-    var ln = match[3];
-    var res;
-    if(typeof context.getElementsByTagNameNS != "undefined") {
-        //  Easy case - we have getElementsByTagNameNS
-        res = context.getElementsByTagNameNS(ns,ln);
-    } else if(typeof context.selectNodes != "undefined") {
-        //  Use xpath if possible (not available on HTML DOM nodes in IE)
-        if(context.ownerDocument) {
-            context.ownerDocument.setProperty("SelectionLanguage","XPath");
-        } else {
-            context.setProperty("SelectionLanguage","XPath");
-        }
-        var predicate = "";
-        if(ns != "*") {
-            if(ln != "*") {
-                predicate="namespace-uri()='"+ns+"' and local-name()='"+ln+"'";
-            } else {
-                predicate="namespace-uri()='"+ns+"'";
-            }
-        } else {
-            if(ln != "*") {
-                predicate="local-name()='"+ln+"'";
-            }
-        }
-        if(predicate) {
-            res = context.selectNodes("descendant-or-self::*["+predicate+"]");
-        } else {
-            res = context.selectNodes("descendant-or-self::*");
-        }
-    } else {
-        //  Otherwise, we need to simulate using getElementsByTagName
-        res = context.getElementsByTagName(ln); 
-        if(gebtn_yields_comments && ln == "*") {
-            var tmp = [];
-            for(var i=0; res[i]; i++) {
-                if(res[i].nodeType == 1) {
-                    tmp.push(res[i]);
-                }
-            }
-            res = tmp;
-        }
-        if(res && ns != "*") {
-            var tmp = [];
-            for(var i=0; res[i]; i++) {
-               if(res[i].namespaceURI == ns || res[i].tagUrn == ns) {
-                   tmp.push(res[i]);
-               }
-            }
-            res = tmp;
-        }
-    }
-    return res;
-};
-
-
-//  Check whether a node is part of an XML document.
-//  Copied verbatim from jQuery sources, needed in TAG preFilter below.
-//
-var isXML = function(elem){
-    return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" ||
-            !!elem.ownerDocument && elem.ownerDocument.documentElement.nodeName !== "HTML";
-};
-
-
-//  Modify the TAG preFilter function to work with modified match regexp.
-//  This normalises case of the tag name if we're in a HTML document.
-//
-$.expr.preFilter.TAG = function(match, curLoop, inplace, result, not, isXML) {
-  var ln = match[3];
-  if(!isXML) {
-      if(localname_is_uppercase) {
-          ln = ln.toUpperCase();
-      } else {
-          ln = ln.toLowerCase();
-      }
-  }
-  return [match[0],getNamespaceURI(match[2]),ln];
-};
-
-
-//  Modify the TAG filter function to account for a namespace selector.
-//
-$.expr.filter.TAG = function(elem,match) {
-    var ns = match[1];
-    var ln = match[2];
-    var e_ns = elem.namespaceURI ? elem.namespaceURI : elem.tagUrn;
-    var e_ln = elem.localName ? elem.localName : elem.tagName;
-    if(ns == "*" || e_ns == ns || (ns == "" && !e_ns)) {
-        return ((ln == "*" && elem.nodeType == 1)  || e_ln == ln);
-    }
-    return false;
-};
-
-
-//  Modify the ATTR match regexp to extract a namespace selector.
-//  This is basically ([namespace|])(attrname)(op)(quote)(pattern)(quote)
-//
-setExprMatchRegex("ATTR",/\[\s*((?:((?:[\w\u00c0-\uFFFF\*_-]*\|)?)((?:[\w\u00c0-\uFFFF_-]|\\.)+)))\s*(?:(\S?=)\s*(['"]*)(.*?)\5|)\s*\]/);
-
-
-//  Modify the ATTR preFilter function to account for new regexp match groups,
-//  and normalise the namespace URI.
-//
-$.expr.preFilter.ATTR = function(match, curLoop, inplace, result, not, isXML) {
-    var name = match[3].replace(/\\/g, "");
-    if(!isXML && $.expr.attrMap[name]) {
-        match[3] = $.expr.attrMap[name];
-    }
-    if( match[4] == "~=" ) {
-        match[6] = " " + match[6] + " ";
-    }
-    if(!match[2] || match[2] == "|") {
-        match[2] = "";
-    } else {
-        match[2] = getNamespaceURI(match[2]);
-    }
-    return match;
-};
-
-
-//  Modify the ATTR filter function to account for namespace selector.
-//  Unfortunately this means factoring out the attribute-checking code
-//  into a separate function, since it might be called multiple times.
-//
-var filter_attr = function(result,type,check) {
-    var value = result + "";
-    return result == null ?
-                type === "!=" :
-                type === "=" ?
-                value === check :
-                type === "*=" ?
-                value.indexOf(check) >= 0 :
-                type === "~=" ?
-                (" " + value + " ").indexOf(check) >= 0 :
-                !check ?
-                value && result !== false :
-                type === "!=" ?
-                value != check :
-                type === "^=" ?
-                value.indexOf(check) === 0 :
-                type === "$=" ?
-                value.substr(value.length - check.length) === check :
-                type === "|=" ?
-                value === check || value.substr(0,check.length+1)===check+"-" :
-                false;
-}
-
-
-$.expr.filter.ATTR = function(elem, match) {
-    var ns = match[2];
-    var name = match[3];
-    var type = match[4];
-    var check = match[6];
-    var result;
-    //  No namespace, just use ordinary attribute lookup.
-    if(ns == "") {
-        result = $.expr.attrHandle[name] ?
-                     $.expr.attrHandle[name](elem) :
-                     elem[name] != null ?
-                         elem[name] :
-                         elem.getAttribute(name);
-        return filter_attr(result,type,check);
-    }
-    //  Directly use getAttributeNS if applicable and available
-    if(ns != "*" && typeof elem.getAttributeNS != "undefined") {
-        return filter_attr(elem.getAttributeNS(ns,name),type,check);
-    }
-    //  Need to iterate over all attributes, either because we couldn't
-    //  look it up or because we need to match all namespaces.
-    var attrs = elem.attributes;
-    for(var i=0; attrs[i]; i++) {
-        var ln = attrs[i].localName;
-        if(!ln) {
-            ln = attrs[i].nodeName
-            var idx = ln.indexOf(":");
-            if(idx >= 0) {
-                ln = ln.substr(idx+1);
-            }
-        }
-        if(ln == name) {
-            result = attrs[i].nodeValue;
-            if(ns == "*" || attrs[i].namespaceURI == ns) {
-                if(filter_attr(result,type,check)) {
-                    return true;
-                }
-            }
-            if(attrs[i].namespaceURI === "" && attrs[i].prefix) {
-                if(attrs[i].prefix == default_xmlns_rev[ns]) {
-                    if(filter_attr(result,type,check)) {
-                        return true;
-                    }
-                }
-            }
-        }
-    }
-    return false;
-};
-
-
-})(jQuery);
-
diff --git a/redakcja/static/js/slugify.js b/redakcja/static/js/slugify.js
deleted file mode 100644 (file)
index 9e17ba7..0000000
+++ /dev/null
@@ -1,126 +0,0 @@
-(function() {
-    var ALL_DOWNCODE_MAPS = new Array()
-
-    ALL_DOWNCODE_MAPS[0] = {
-        // LATIN_MAP
-        'À': 'A', 'Á': 'A', 'Â': 'A', 'Ã': 'A', 'Ä': 'A', 'Å': 'A', 'Æ': 'AE', 'Ç':
-        'C', 'È': 'E', 'É': 'E', 'Ê': 'E', 'Ë': 'E', 'Ì': 'I', 'Í': 'I', 'Î': 'I',
-        'Ï': 'I', 'Ð': 'D', 'Ñ': 'N', 'Ò': 'O', 'Ó': 'O', 'Ô': 'O', 'Õ': 'O', 'Ö':
-        'O', 'Ő': 'O', 'Ø': 'O', 'Ù': 'U', 'Ú': 'U', 'Û': 'U', 'Ü': 'U', 'Ű': 'U',
-        'Ý': 'Y', 'Þ': 'TH', 'ß': 'ss', 'à':'a', 'á':'a', 'â': 'a', 'ã': 'a', 'ä':
-        'a', 'å': 'a', 'æ': 'ae', 'ç': 'c', 'è': 'e', 'é': 'e', 'ê': 'e', 'ë': 'e',
-        'ì': 'i', 'í': 'i', 'î': 'i', 'ï': 'i', 'ð': 'd', 'ñ': 'n', 'ò': 'o', 'ó':
-        'o', 'ô': 'o', 'õ': 'o', 'ö': 'o', 'ő': 'o', 'ø': 'o', 'ù': 'u', 'ú': 'u',
-        'û': 'u', 'ü': 'u', 'ű': 'u', 'ý': 'y', 'þ': 'th', 'ÿ': 'y'
-    }
-    ALL_DOWNCODE_MAPS[1] = {
-        // LATIN_SYMBOLS_MAP
-        '©':'(c)'
-    }
-    ALL_DOWNCODE_MAPS[2] = {
-        // GREEK_MAP
-        'α':'a', 'β':'b', 'γ':'g', 'δ':'d', 'ε':'e', 'ζ':'z', 'η':'h', 'θ':'8',
-        'ι':'i', 'κ':'k', 'λ':'l', 'μ':'m', 'ν':'n', 'ξ':'3', 'ο':'o', 'π':'p',
-        'ρ':'r', 'σ':'s', 'τ':'t', 'υ':'y', 'φ':'f', 'χ':'x', 'ψ':'ps', 'ω':'w',
-        'ά':'a', 'έ':'e', 'ί':'i', 'ό':'o', 'ύ':'y', 'ή':'h', 'ώ':'w', 'ς':'s',
-        'ϊ':'i', 'ΰ':'y', 'ϋ':'y', 'ΐ':'i',
-        'Α':'A', 'Β':'B', 'Γ':'G', 'Δ':'D', 'Ε':'E', 'Ζ':'Z', 'Η':'H', 'Θ':'8',
-        'Ι':'I', 'Κ':'K', 'Λ':'L', 'Μ':'M', 'Ν':'N', 'Ξ':'3', 'Ο':'O', 'Π':'P',
-        'Ρ':'R', 'Σ':'S', 'Τ':'T', 'Υ':'Y', 'Φ':'F', 'Χ':'X', 'Ψ':'PS', 'Ω':'W',
-        'Ά':'A', 'Έ':'E', 'Ί':'I', 'Ό':'O', 'Ύ':'Y', 'Ή':'H', 'Ώ':'W', 'Ϊ':'I',
-        'Ϋ':'Y'
-    }
-    ALL_DOWNCODE_MAPS[3] = {
-        // TURKISH_MAP
-        'ş':'s', 'Ş':'S', 'ı':'i', 'İ':'I', 'ç':'c', 'Ç':'C', 'ü':'u', 'Ü':'U',
-        'ö':'o', 'Ö':'O', 'ğ':'g', 'Ğ':'G'
-    }
-    ALL_DOWNCODE_MAPS[4] = {
-        // RUSSIAN_MAP
-        'а':'a', 'б':'b', 'в':'v', 'г':'g', 'д':'d', 'е':'e', 'ё':'yo', 'ж':'zh',
-        'з':'z', 'и':'i', 'й':'j', 'к':'k', 'л':'l', 'м':'m', 'н':'n', 'о':'o',
-        'п':'p', 'р':'r', 'с':'s', 'т':'t', 'у':'u', 'ф':'f', 'х':'h', 'ц':'c',
-        'ч':'ch', 'ш':'sh', 'щ':'sh', 'ъ':'', 'ы':'y', 'ь':'', 'э':'e', 'ю':'yu',
-        'я':'ya',
-        'А':'A', 'Б':'B', 'В':'V', 'Г':'G', 'Д':'D', 'Е':'E', 'Ё':'Yo', 'Ж':'Zh',
-        'З':'Z', 'И':'I', 'Й':'J', 'К':'K', 'Л':'L', 'М':'M', 'Н':'N', 'О':'O',
-        'П':'P', 'Р':'R', 'С':'S', 'Т':'T', 'У':'U', 'Ф':'F', 'Х':'H', 'Ц':'C',
-        'Ч':'Ch', 'Ш':'Sh', 'Щ':'Sh', 'Ъ':'', 'Ы':'Y', 'Ь':'', 'Э':'E', 'Ю':'Yu',
-        'Я':'Ya'
-    }
-    ALL_DOWNCODE_MAPS[5] = {
-        // UKRAINIAN_MAP
-        'Є':'Ye', 'І':'I', 'Ї':'Yi', 'Ґ':'G', 'є':'ye', 'і':'i', 'ї':'yi', 'ґ':'g'
-    }
-    ALL_DOWNCODE_MAPS[6] = {
-        // CZECH_MAP
-        'č':'c', 'ď':'d', 'ě':'e', 'ň': 'n', 'ř':'r', 'š':'s', 'ť':'t', 'ů':'u',
-        'ž':'z', 'Č':'C', 'Ď':'D', 'Ě':'E', 'Ň': 'N', 'Ř':'R', 'Š':'S', 'Ť':'T',
-        'Ů':'U', 'Ž':'Z'
-    }
-
-    ALL_DOWNCODE_MAPS[7] = {
-        // POLISH_MAP
-        'ą':'a', 'ć':'c', 'ę':'e', 'ł':'l', 'ń':'n', 'ó':'o', 'ś':'s', 'ź':'z',
-        'ż':'z', 'Ą':'A', 'Ć':'C', 'Ę':'e', 'Ł':'L', 'Ń':'N', 'Ó':'o', 'Ś':'S',
-        'Ź':'Z', 'Ż':'Z'
-    }
-
-    ALL_DOWNCODE_MAPS[8] = {
-        // LATVIAN_MAP
-        'ā':'a', 'č':'c', 'ē':'e', 'ģ':'g', 'ī':'i', 'ķ':'k', 'ļ':'l', 'ņ':'n',
-        'š':'s', 'ū':'u', 'ž':'z', 'Ā':'A', 'Č':'C', 'Ē':'E', 'Ģ':'G', 'Ī':'i',
-        'Ķ':'k', 'Ļ':'L', 'Ņ':'N', 'Š':'S', 'Ū':'u', 'Ž':'Z'
-    }
-
-    var Downcoder = new Object();
-
-    Downcoder.Initialize = function() {
-        if (Downcoder.map) // already made
-            return ;
-        Downcoder.map ={}
-        Downcoder.chars = '';
-        for(var i in ALL_DOWNCODE_MAPS) {
-            var lookup = ALL_DOWNCODE_MAPS[i]
-            for (var c in lookup) {
-                Downcoder.map[c] = lookup[c];
-                Downcoder.chars += c;
-            }
-         }
-        Downcoder.regex = new RegExp('[' + Downcoder.chars + ']|[^' + Downcoder.chars + ']+','g');
-    }
-
-    downcode = function(slug) {
-        Downcoder.Initialize();
-        var downcoded =""
-        var pieces = slug.match(Downcoder.regex);
-        if(pieces) {
-            for (var i = 0 ; i < pieces.length ; i++) {
-                if (pieces[i].length == 1) {
-                    var mapped = Downcoder.map[pieces[i]];
-                    if (mapped != null) {
-                        downcoded+=mapped;
-                        continue;
-                    }
-                }
-                downcoded+=pieces[i];
-            }
-        } else {
-            downcoded = slug;
-        }
-        return downcoded;
-    }
-
-    slugify = function(s) {
-        s = downcode(s);
-        // if downcode doesn't hit, the char will be stripped here
-        s = s.replace(/[^-\w\s]/g, '');  // remove unneeded chars
-        s = s.replace(/^\s+|\s+$/g, ''); // trim leading/trailing spaces
-        s = s.replace(/[-\s]+/g, '-');   // convert spaces to hyphens
-        s = s.toLowerCase();             // convert to lowercase
-
-        return s;
-    }
-})();
-
-
diff --git a/redakcja/static/js/wiki/base.js b/redakcja/static/js/wiki/base.js
deleted file mode 100644 (file)
index ad2ee3a..0000000
+++ /dev/null
@@ -1,339 +0,0 @@
-(function($)
-{
-       var noop = function() { };
-
-       $.wiki = {
-               perspectives: {},
-               cls: {},
-               state: {
-                       "version": 1,
-                       "perspectives": {
-                               "ScanGalleryPerspective": {
-                                       "show": true,
-                                       "page": undefined
-                               },
-                               "CodeMirrorPerspective": {}
-                               /*
-                               "VisualPerspective": {},
-                               "HistoryPerspective": {},
-                               "SummaryPerspective": {}
-                               */
-                       }
-               }
-       };
-
-       $.wiki.loadConfig = function() {
-               if(!window.localStorage)
-                       return;
-
-               try {
-                       var value = window.localStorage.getItem(CurrentDocument.id) || "{}";
-                       var config = JSON.parse(value);
-
-                       if (config.version == $.wiki.state.version) {
-                               $.wiki.state.perspectives = $.extend($.wiki.state.perspectives, config.perspectives);
-                       }
-               } catch(e) {
-                       console.log("Failed to load config, using default.");
-               }
-
-               console.log("Loaded:", $.wiki.state, $.wiki.state.version);
-       };
-
-       $(window).bind('unload', function() {
-               if(window.localStorage)
-                       window.localStorage.setItem(CurrentDocument.id, JSON.stringify($.wiki.state));
-       })
-
-
-       $.wiki.activePerspective = function() {
-               return this.perspectives[$("#tabs li.active").attr('id')];
-       };
-
-       $.wiki.exitContext = function() {
-               var ap = this.activePerspective();
-               if(ap) ap.onExit();
-               return ap;
-       };
-
-       $.wiki.enterContext = function(ap) {
-               if(ap) ap.onEnter();
-       };
-
-       $.wiki.isDirty = function() {
-               var ap = this.activePerspective();
-               return (!!CurrentDocument && CurrentDocument.has_local_changes) || ap.dirty();
-       };
-
-       $.wiki.newTab = function(doc, title, klass) {
-               var base_id = 'id' + Math.floor(Math.random()* 5000000000);
-               var id = (''+klass)+'_' + base_id;
-               var $tab = $('<li id="'+id+'" data-ui-related="'+base_id+'" data-ui-jsclass="'+klass+'" >'
-                               + title + '<img src="'+STATIC_URL+'icons/close.png" class="tabclose"></li>');
-               var $view = $('<div class="editor '+klass+'" id="'+base_id+'"> </div>');
-
-               this.perspectives[id] = new $.wiki[klass]({
-                       doc: doc,
-                       id: id,
-                       base_id: base_id,
-               });
-
-               $('#tabs').append($tab);
-               $view.hide().appendTo('#editor');
-               return {
-                       tab: $tab[0],
-                       view: $view[0],
-               };
-       };
-
-       $.wiki.initTab = function(options) {
-               var klass = $(options.tab).attr('data-ui-jsclass');
-
-               return new $.wiki[klass]({
-                       doc: options.doc,
-                       id: $(options.tab).attr('id'),
-                       callback: function() {
-                               $.wiki.perspectives[this.perspective_id] = this;
-                               if(options.callback)
-                                       options.callback.call(this);
-                       }
-               });
-       };
-
-       $.wiki.perspectiveForTab = function(tab) { // element or id
-               return this.perspectives[ $(tab).attr('id')];
-       }
-
-       $.wiki.switchToTab = function(tab){
-               var self = this;
-               var $tab = $(tab);
-
-               if($tab.length != 1)
-                       $tab = $(DEFAULT_PERSPECTIVE);
-
-               var $old = $tab.closest('.tabs').find('.active');
-
-               $old.each(function(){
-                       $(this).removeClass('active');
-                       self.perspectives[$(this).attr('id')].onExit();
-                       $('#' + $(this).attr('data-ui-related')).hide();
-               });
-
-               /* show new */
-               $tab.addClass('active');
-               $('#' + $tab.attr('data-ui-related')).show();
-
-               console.log($tab);
-               console.log($.wiki.perspectives);
-
-               $.wiki.perspectives[$tab.attr('id')].onEnter();
-       };
-
-       /*
-        * Basic perspective.
-        */
-       $.wiki.Perspective = function(options) {
-               if(!options) return;
-
-               this.doc = options.doc;
-               if (options.id) {
-                       this.perspective_id = options.id;
-               }
-               else {
-                       this.perspective_id = '';
-               }
-
-               if(options.callback)
-                       options.callback.call(this);
-       };
-
-       $.wiki.Perspective.prototype.config = function() {
-               return $.wiki.state.perspectives[this.perspective_id];
-       }
-
-       $.wiki.Perspective.prototype.toString = function() {
-               return this.perspective_id;
-       };
-
-       $.wiki.Perspective.prototype.dirty = function() {
-               return true;
-       };
-
-       $.wiki.Perspective.prototype.onEnter = function () {
-               // called when perspective in initialized
-               if (!this.noupdate_hash_onenter) {
-                       document.location.hash = '#' + this.perspective_id;
-               }
-       };
-
-       $.wiki.Perspective.prototype.onExit = function () {
-               // called when user switches to another perspective
-               if (!this.noupdate_hash_onenter) {
-                       document.location.hash = '';
-               }
-       };
-
-       $.wiki.Perspective.prototype.destroy = function() {
-               // pass
-       };
-
-       $.wiki.Perspective.prototype.freezeState = function () {
-               // free UI state (don't store data here)
-       };
-
-       $.wiki.Perspective.prototype.unfreezeState = function (frozenState) {
-               // restore UI state
-       };
-
-       /*
-        * Stub rendering (used in generating history)
-        */
-       $.wiki.renderStub = function(params)
-       {
-               params = $.extend({ 'filters': {} }, params);
-               var $elem = params.stub.clone();
-               $elem.removeClass('row-stub');
-               params.container.append($elem);
-
-               $('*[data-stub-value]', $elem).each(function() {
-                       var $this = $(this);
-                       var field = $this.attr('data-stub-value');
-
-                       var value = params.data[field];
-
-                       if(params.filters[field])
-                               value = params.filters[field](value);
-
-                       if(value === null || value === undefined) return;
-
-                       if(!$this.attr('data-stub-target')) {
-                               $this.text(value);
-                       }
-                       else {
-                               $this.attr($this.attr('data-stub-target'), value);
-                               $this.removeAttr('data-stub-target');
-                               $this.removeAttr('data-stub-value');
-                       }
-               });
-
-               $elem.show();
-               return $elem;
-       };
-
-       /*
-        * Dialogs
-        */
-       function GenericDialog(element) {
-               if(!element) return;
-
-               var self = this;
-
-               self.$elem = $(element);
-
-               if(!self.$elem.attr('data-ui-initialized')) {
-                       console.log("Initializing dialog", this);
-                       self.initialize();
-                       self.$elem.attr('data-ui-initialized', true);
-               }
-
-               self.show();
-       };
-
-       GenericDialog.prototype = {
-
-               /*
-               * Steps to follow when the dialog in first loaded on page.
-               */
-               initialize: function(){
-                       var self = this;
-
-                       /* bind buttons */
-                       $('button[data-ui-action]', self.$elem).click(function(event) {
-                               event.preventDefault();
-
-                               var action = $(this).attr('data-ui-action');
-                               console.log("Button pressed, action: ", action);
-
-                               try {
-                                       self[action + "Action"].call(self);
-                               } catch(e) {
-                                       console.log("Action failed:", e);
-                                       // always hide on cancel
-                                       if(action == 'cancel')
-                                               self.hide();
-                               }
-                       });
-               },
-
-               /*
-                * Prepare dialog for user. Clear any unnessary data.
-               */
-               show: function() {
-                       $.blockUI({
-                               message: this.$elem,
-                               css: {
-                                       'top': '25%',
-                                       'left': '25%',
-                                       'width': '50%'
-                               }
-                       });
-               },
-
-               hide: function(){
-                       $.unblockUI();
-               },
-
-               cancelAction: function() {
-                       this.hide();
-               },
-
-               doneAction: function() {
-                       this.hide();
-               },
-
-               clearForm: function() {
-                       $("*[data-ui-error-for]", this.$elem).text('');
-               },
-
-               reportErrors: function(errors) {
-                       var global = $("*[data-ui-error-for='__all__']", this.$elem);
-                       var unassigned = [];
-
-            $("*[data-ui-error-for]", this.$elem).text('');
-                       for (var field_name in errors)
-                       {
-                               var span = $("*[data-ui-error-for='"+field_name+"']", this.$elem);
-
-                               if(!span.length) {
-                                       unassigned.push(field_name);
-                                       continue;
-                               }
-
-                               span.text(errors[field_name].join(' '));
-                       }
-
-                       if(unassigned.length > 0)
-                               global.text( global.text() + 'W formularzu wystąpiły błędy');
-               }
-       };
-
-       $.wiki.cls.GenericDialog = GenericDialog;
-
-       $.wiki.showDialog = function(selector, options) {
-               var elem = $(selector);
-
-               if(elem.length != 1) {
-                       console.log("Failed to show dialog:", selector, elem);
-                       return false;
-               }
-
-               try {
-                   var klass = elem.attr('data-ui-jsclass');
-                       return new $.wiki.cls[klass](elem, options);
-               } catch(e) {
-                       console.log("Failed to show dialog", selector, klass, e);
-                       return false;
-               }
-       };
-
-})(jQuery);
diff --git a/redakcja/static/js/wiki/dialog_pubmark.js b/redakcja/static/js/wiki/dialog_pubmark.js
deleted file mode 100755 (executable)
index 902a737..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Dialog for marking document for publishing
- *
- */
-(function($){
-
-    function PubmarkDialog(element, options){
-        if (!options.revision  && options.revision != 0)
-            throw "PubmarkDialog needs a revision number.";
-
-        this.ctx = $.wiki.exitContext();
-        this.clearForm();
-
-        /* fill out hidden fields */
-        this.$form = $('form', element);
-
-        $("input[name='pubmark-id']", this.$form).val(CurrentDocument.id);
-        $("input[name='pubmark-revision']", this.$form).val(options.revision);
-
-        $.wiki.cls.GenericDialog.call(this, element);
-    };
-
-    PubmarkDialog.prototype = $.extend(new $.wiki.cls.GenericDialog(), {
-        cancelAction: function(){
-            $.wiki.enterContext(this.ctx);
-            this.hide();
-        },
-
-        saveAction: function(){
-            var self = this;
-
-            self.$elem.block({
-                message: "Oznaczanie wersji",
-                fadeIn: 0,
-            });
-
-            CurrentDocument.pubmark({
-                form: self.$form,
-                success: function(doc, changed, info){
-                    self.$elem.block({
-                        message: info,
-                        timeout: 2000,
-                        fadeOut: 0,
-                        onUnblock: function(){
-                            self.hide();
-                            $.wiki.enterContext(self.ctx);
-                        }
-                    });
-                },
-                failure: function(doc, info){
-                    console.log("Failure", info);
-                    self.reportErrors(info);
-                    self.$elem.unblock();
-                }
-            });
-        }
-    });
-
-    /* make it global */
-    $.wiki.cls.PubmarkDialog = PubmarkDialog;
-})(jQuery);
diff --git a/redakcja/static/js/wiki/dialog_revert.js b/redakcja/static/js/wiki/dialog_revert.js
deleted file mode 100644 (file)
index 4d550f9..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Dialog for reverting document on the server
- *
- */
-(function($) {
-
-    function RevertDialog(element, options) {
-        this.ctx = $.wiki.exitContext();
-        this.clearForm();
-
-        /* fill out hidden fields */
-        this.$form = $('form', element);
-
-        $("input[name='textrevert-revision']", this.$form).val(options.revision);
-
-        $.wiki.cls.GenericDialog.call(this, element);
-    };
-
-    RevertDialog.prototype = new $.wiki.cls.GenericDialog();
-
-    RevertDialog.prototype.cancelAction = function() {
-        $.wiki.enterContext(this.ctx);
-        this.hide();
-    };
-
-    RevertDialog.prototype.revertAction = function() {
-            var self = this;
-
-            self.$elem.block({
-                message: "Przywracanie...",
-                fadeIn: 0,
-            });
-            $.wiki.blocking = self.$elem;
-
-            try {
-
-                CurrentDocument.revertToVersion({
-                    form: self.$form,
-                    success: function(e, msg) {
-                        self.$elem.block({
-                            message: msg,
-                            timeout: 2000,
-                            fadeOut: 0,
-                            onUnblock: function() {
-                                self.hide();
-                                $.wiki.enterContext(self.ctx);
-                            }
-                        });
-                    },
-                    'failure': function(e, info) {
-                        console.log("Failure", info);
-                        self.reportErrors(info);
-                        self.$elem.unblock();
-                    }
-                });
-
-            } catch(e) {
-                console.log('Exception:', e)
-                self.$elem.unblock();
-            }
-    }; /* end of revert dialog */
-
-    /* make it global */
-    $.wiki.cls.RevertDialog = RevertDialog;
-})(jQuery);
diff --git a/redakcja/static/js/wiki/dialog_save.js b/redakcja/static/js/wiki/dialog_save.js
deleted file mode 100644 (file)
index 903c0e1..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Dialog for saving document to the server
- *
- */
-(function($) {
-
-       function SaveDialog(element) {
-               this.ctx = $.wiki.exitContext();
-               this.clearForm();
-
-               /* fill out hidden fields */
-               this.$form = $('form', element);
-
-               $("input[name='textsave-parent_revision']", this.$form).val(CurrentDocument.revision);
-
-               $.wiki.cls.GenericDialog.call(this, element);
-       };
-
-       SaveDialog.prototype = new $.wiki.cls.GenericDialog();
-
-       SaveDialog.prototype.cancelAction = function() {
-               $.wiki.enterContext(this.ctx);
-               this.hide();
-       };
-
-       SaveDialog.prototype.saveAction = function() {
-                       var self = this;
-
-                       self.$elem.block({
-                               message: "Zapisywanie... <br/><button id='save-hide'>ukryj</button>",
-                               fadeIn: 0,
-                       });
-            $.wiki.blocking = self.$elem;
-
-                       try {
-
-                               CurrentDocument.save({
-                                       form: self.$form,
-                                       success: function(doc, changed, info){
-                                               self.$elem.block({
-                                                       message: info,
-                                                       timeout: 2000,
-                                                       fadeOut: 0,
-                                                       onUnblock: function() {
-                                                               self.hide();
-                                                               $.wiki.enterContext(self.ctx);
-                                                       }
-                                               });
-                                       },
-                                       failure: function(doc, info) {
-                                               console.log("Failure", info);
-                                               self.reportErrors(info);
-                                               self.$elem.unblock();
-                                       }
-                               });
-                       } catch(e) {
-                               console.log('Exception:', e)
-                               self.$elem.unblock();
-                       }
-       }; /* end of save dialog */
-
-       /* make it global */
-       $.wiki.cls.SaveDialog = SaveDialog;
-})(jQuery);
diff --git a/redakcja/static/js/wiki/loader.js b/redakcja/static/js/wiki/loader.js
deleted file mode 100644 (file)
index 1223992..0000000
+++ /dev/null
@@ -1,189 +0,0 @@
-if (!window.console) {
-    window.console = {
-        log: function(){
-        }
-    }
-}
-
-var DEFAULT_PERSPECTIVE = "#VisualPerspective";
-
-$(function()
-{
-       var tabs = $('ol#tabs li');
-       var gallery = null;
-       CurrentDocument = new $.wikiapi.WikiDocument("document-meta");
-
-       $.blockUI.defaults.baseZ = 10000;
-
-    function initialize()
-       {
-        var splitter = $('#splitter'),
-            editors = $('#editor .editor'),
-            vsplitbar = $('.vsplitbar'),
-            sidebar = $('#sidebar'),
-            dragLayer = $('#drag-layer'),
-            vsplitbarWidth = vsplitbar.outerWidth(),
-            isHolding = false;
-
-        // Moves panes so that left border of the vsplitbar lands x pixels from the left border of the splitter
-        function setSplitbarAt(x) {
-            var right = splitterWidth - x;
-            editors.each(function() {
-                this.style.right = right + 'px';
-            });
-            vsplitbar[0].style.right = sidebar[0].style.width = (right - vsplitbarWidth) + 'px';
-        };
-
-               $(document).keydown(function(event) {
-                       console.log("Received key:", event);
-               });
-
-               /* The save button */
-        $('#save-button').click(function(event){
-            event.preventDefault();
-                       $.wiki.showDialog('#save_dialog');
-        });
-
-               $('.editor').hide();
-
-               /*
-                * TABS
-                */
-        $('.tabs li').live('click', function(event, callback) {
-            event.preventDefault();
-                       $.wiki.switchToTab(this);
-        });
-
-               $('#tabs li > .tabclose').live('click', function(event, callback) {
-                       var $tab = $(this).parent();
-
-                       if($tab.is('.active'))
-                               $.wiki.switchToTab(DEFAULT_PERSPECTIVE);
-
-                       var p = $.wiki.perspectiveForTab($tab);
-                       p.destroy();
-
-                       return false;
-        });
-
-
-        $(window).resize(function(){
-            $('iframe').height($(window).height() - $('#tabs').outerHeight() - $('#source-editor .toolbar').outerHeight());
-            splitterWidth = splitter.width();
-        });
-
-        $(window).resize();
-
-        vsplitbar.toggle(
-                       function() {
-                               $.wiki.state.perspectives.ScanGalleryPerspective.show = true;
-                               setSplitbarAt(splitterWidth - (480 + vsplitbarWidth));
-                               $('.vsplitbar').addClass('active');
-                               $(window).resize();
-                               $.wiki.perspectiveForTab('#tabs-right .active').onEnter();
-                       },
-                       function() {
-                           var active_right = $.wiki.perspectiveForTab('#tabs-right .active');
-                               $.wiki.state.perspectives.ScanGalleryPerspective.show = false;
-                               $(".vsplitbar-title").html("&uarr;&nbsp;" + active_right.vsplitbar + "&nbsp;&uarr;");
-                               setSplitbarAt(splitterWidth - vsplitbarWidth);
-                               $('.vsplitbar').removeClass('active');
-                               $(window).resize();
-                               active_right.onExit();
-                       }
-               );
-
-
-        /* Splitbar dragging support */
-        vsplitbar
-            .mousedown(function(e) {
-                e.preventDefault();
-                isHolding = true;
-            })
-            .mousemove(function(e) {
-                if(isHolding) {
-                    dragLayer.show(); // We don't show it up until now so that we don't lose single click events on vsplitbar
-                }
-            });
-        dragLayer.mousemove(function(e) {
-            setSplitbarAt(e.clientX - vsplitbarWidth/2);
-        });
-        $('body').mouseup(function(e) {
-            dragLayer.hide();
-            isHolding = false;
-        });
-
-
-               if($.wiki.state.perspectives.ScanGalleryPerspective.show){
-            $('.vsplitbar').trigger('click');
-            $(".vsplitbar-title").html("&darr;&nbsp;GALERIA&nbsp;&darr;");
-        } else {
-            $(".vsplitbar-title").html("&uarr;&nbsp;GALERIA&nbsp;&uarr;");
-        }
-        window.onbeforeunload = function(e) {
-            if($.wiki.isDirty()) {
-                               e.returnValue = "Na stronie mogą być nie zapisane zmiany.";
-                               return "Na stronie mogą być nie zapisane zmiany.";
-                       };
-        };
-
-               console.log("Fetching document's text");
-
-               $(document).bind('wlapi_document_changed', function(event, doc) {
-                       try {
-                               $('#document-revision').text(doc.revision);
-                       } catch(e) {
-                               console.log("Failed handler", e);
-                       }
-               });
-
-               CurrentDocument.fetch({
-                       success: function(){
-                               console.log("Fetch success");
-                               $('#loading-overlay').fadeOut();
-                               var active_tab = document.location.hash || DEFAULT_PERSPECTIVE;
-
-                               if(active_tab == "#ScanGalleryPerspective")
-                                       active_tab = DEFAULT_PERSPECTIVE;
-
-                               console.log("Initial tab is:", active_tab)
-                               $.wiki.switchToTab(active_tab);
-
-                /* every 5 minutes check for a newer version */
-                var revTimer = setInterval(function() {
-                        CurrentDocument.checkRevision({outdated: function(){
-                            $('#header').addClass('out-of-date');
-                            clearInterval(revTimer);
-                        }});
-                    }, 300000);
-                       },
-                       failure: function() {
-                               $('#loading-overlay').fadeOut();
-                               alert("FAILURE");
-                       }
-               });
-    }; /* end of initialize() */
-
-
-       /* Load configuration */
-       $.wiki.loadConfig();
-
-       var initAll = function(a, f) {
-               if (a.length == 0) return f();
-
-               $.wiki.initTab({
-                       tab: a.pop(),
-                       doc: CurrentDocument,
-                       callback: function(){
-                               initAll(a, f);
-                       }
-               });
-       };
-
-
-       /*
-        * Initialize all perspectives
-        */
-       initAll( $.makeArray($('.tabs li')), initialize);
-       console.log(location.hash);
-});
diff --git a/redakcja/static/js/wiki/loader_readonly.js b/redakcja/static/js/wiki/loader_readonly.js
deleted file mode 100644 (file)
index a859ec6..0000000
+++ /dev/null
@@ -1,93 +0,0 @@
-if (!window.console) {
-    window.console = {
-        log: function(){
-        }
-    }
-}
-
-
-DEFAULT_PERSPECTIVE = "#VisualPerspective";
-
-$(function()
-{
-       var tabs = $('ol#tabs li');
-       var gallery = null;
-
-       CurrentDocument = new $.wikiapi.WikiDocument("document-meta");
-       $.blockUI.defaults.baseZ = 10000;
-
-       function initialize()
-       {
-               $('.editor').hide();
-
-               /*
-                * TABS
-                */
-        $('#tabs li').live('click', function(event, callback) {
-            event.preventDefault();
-                       $.wiki.switchToTab(this);
-        });
-
-               $('#tabs li > .tabclose').live('click', function(event, callback) {
-                       var $tab = $(this).parent();
-
-                       if($tab.is('.active'))
-                               $.wiki.switchToTab(DEFAULT_PERSPECTIVE);
-
-                       var p = $.wiki.perspectiveForTab($tab);
-                       p.destroy();
-                       return false;
-        });
-
-        $(window).resize(function(){
-            $('iframe').height($(window).height() - $('#tabs').outerHeight() - $('#source-editor .toolbar').outerHeight());
-        });
-
-               $(document).bind('wlapi_document_changed', function(event, doc) {
-                       try {
-                               $('#document-revision').text(doc.revision);
-                       } catch(e) {
-                               console.log("Failed handler", e);
-                       }
-               });
-
-               CurrentDocument.fetch({
-                       success: function(){
-                               console.log("Fetch success");
-                               $('#loading-overlay').fadeOut();
-                               var active_tab = document.location.hash || DEFAULT_PERSPECTIVE;
-
-                               $(window).resize();
-
-                               console.log("Initial tab is:", active_tab)
-                               $.wiki.switchToTab(active_tab);
-                       },
-                       failure: function() {
-                               $('#loading-overlay').fadeOut();
-                               alert("FAILURE");
-                       }
-               });
-    }; /* end of initialize() */
-
-       /* Load configuration */
-       $.wiki.loadConfig();
-
-       var initAll = function(a, f) {
-               if (a.length == 0) return f();
-
-               $.wiki.initTab({
-                       tab: a.pop(),
-                       doc: CurrentDocument,
-                       callback: function(){
-                               initAll(a, f);
-                       }
-               });
-       };
-
-
-       /*
-        * Initialize all perspectives
-        */
-       initAll( $.makeArray($('ol#tabs li')), initialize);
-       console.log(location.hash);
-});
\ No newline at end of file
diff --git a/redakcja/static/js/wiki/toolbar.js b/redakcja/static/js/wiki/toolbar.js
deleted file mode 100644 (file)
index 3eafdae..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Toolbar plugin.
- */
-(function($) {
-
-    $.fn.toolbarize = function(options) {
-        var $toolbar = $(this);
-        var $container = $('.button_group_container', $toolbar);
-
-        $('.button_group button', $toolbar).click(function(event){
-            event.preventDefault();
-
-            var params = eval("(" + $(this).attr('data-ui-action-params') + ")");
-
-            scriptletCenter.callInteractive({
-                action: $(this).attr('data-ui-action'),
-                context: options.actionContext,
-                extra: params
-            });
-        });
-
-        $toolbar.children().filter('select').change(function(event){
-            var slug = $(this).val();
-            $container.scrollLeft(0);
-            $('*[data-group]').hide().filter('[data-group=' + slug + ']').show();
-        }).change();
-
-        $('button.next', $toolbar).click(function() {
-            var $current_group = $('.button_group:visible', $toolbar);
-            var scroll = $container.scrollLeft();
-
-            var $hidden = $("li", $current_group).filter(function() {
-                return ($(this).position().left + $(this).outerWidth()) > $container.width();
-            }).first();
-
-            if($hidden.length > 0) {
-                scroll = $hidden.position().left + scroll + $hidden.outerWidth() - $container.width() + 1;
-                $container.scrollLeft(scroll);
-            };
-        });
-
-        $('button.prev', $toolbar).click(function() {
-            var $current_group = $('.button_group:visible', $toolbar);
-            var scroll = $container.scrollLeft();
-
-            /* first not visible element is: */
-            var $hidden = $("li", $current_group).filter(function() {
-                return $(this).position().left < 0;
-            }).last();
-
-            if( $hidden.length > 0)
-            {
-                scroll = scroll + $hidden.position().left;
-                $container.scrollLeft(scroll);
-            };
-        });
-
-    };
-
-})(jQuery);
\ No newline at end of file
diff --git a/redakcja/static/js/wiki/view_annotations.js b/redakcja/static/js/wiki/view_annotations.js
deleted file mode 100644 (file)
index b55effd..0000000
+++ /dev/null
@@ -1,180 +0,0 @@
-(function($){
-
-    /*
-     * Perspective
-     */
-    function AnnotationsPerspective(options){
-        var old_callback = options.callback || function() { };
-
-        this.noupdate_hash_onenter = true;
-        this.vsplitbar = 'PRZYPISY';
-
-        options.callback = function(){
-            var self = this;
-
-            this.$element = $("#side-annotations");
-            this.$error = $('.error-message', this.$element);
-            this.$annos = $('.annotations-list', this.$element);
-            this.$spinner = $('.spinner', this.$element);
-            this.$refresh = $('.refresh', this.$element);
-
-            this.$refresh.click(function() {
-                var $this = $(this);
-
-                self.$refresh.removeClass('active');
-                $this.addClass('active');
-                var atype = $this.attr('data-tag');
-
-                self.$annos.hide();
-                self.$error.hide();
-                self.$spinner.show(100, function(){
-                    self.refresh(self, atype);
-                });
-            });
-
-            old_callback.call(this);
-        };
-
-        $.wiki.Perspective.call(this, options);
-    }
-
-    AnnotationsPerspective.prototype = new $.wiki.Perspective();
-
-    AnnotationsPerspective.prototype.updateAnnotationIds = function(self){
-        self.annotationToAnchor = {};
-        $('#html-view').find('.annotation-inline-box').each(
-            function(i, annoBox) {
-                var $annoBox = $(annoBox);
-                var $anchor = $("a[name|=anchor]", $annoBox);
-                var htmlContent = $('span', $annoBox).html();
-                // TBD: perhaps use a hash of htmlContent as key
-                self.annotationToAnchor[htmlContent] = $anchor.attr('name');
-                });
-    };
-
-    AnnotationsPerspective.prototype.goToAnnotation = function(self, srcNode){
-        var content = $(srcNode).html();
-        content = content.replace(/&gt;/g, '>').replace(/&lt;/g, '<').replace(/&amp;/g, '&');
-        xml2html({
-            xml: '<root>'+content+'</root>',
-            success: function(txt) {
-                content = $(txt).html();
-                },
-            error: function(text) {
-                $.unblockUI();
-                self.$error.html(text);
-                self.$spinner.hide();
-                self.$error.show();
-            }
-        });
-
-        var anchor = self.annotationToAnchor[content];
-        if (anchor != undefined) {
-            var $htmlView = $("#html-view");
-            var top = $htmlView.offset().top + 
-                $("[name=" + anchor + "]", $htmlView).offset().top - 
-                $htmlView.children().eq(0).offset().top;
-
-            $htmlView.animate({scrollTop: top}, 250);
-        }
-    };
-
-    AnnotationsPerspective.prototype.refresh = function(self, atype) {
-        var xml;
-
-        var persp = $.wiki.activePerspective();
-        if (persp == 'CodeMirrorPerspective') {
-            xml = $.wiki.perspectives[persp].codemirror.getCode();
-        }
-        else if (persp == 'VisualPerspective') {
-            html2text({
-                element: $('#html-view').find('div').get(0),
-                success: function(text){
-                    xml = text;
-                },
-                error: function(text){
-                    self.$error.html('<p>Wystąpił błąd:</p><pre>' + text + '</pre>');
-                    self.$spinner.hide();
-                    self.$error.show();
-                }
-            });
-            self.updateAnnotationIds(self);
-        }
-        else {
-            xml = this.doc.text;
-        }
-
-        var parser = new DOMParser();
-        var serializer = new XMLSerializer();
-        var doc = parser.parseFromString(xml, 'text/xml');
-        var error = $('parsererror', doc);
-
-        if (error.length > 0) {
-            self.$error.html('Błąd parsowania XML.');
-            self.$spinner.hide();
-            self.$error.show();
-        }
-        else {
-            self.$annos.html('');
-            var anno_list = [];
-            var annos = $(atype, doc);
-            var counter = annos.length;
-            var atype_rx = atype.replace(/,/g, '|');
-            var ann_expr = new RegExp("^<("+atype_rx+")[^>]*>|</("+atype_rx+")>$", "g");
-
-            if (annos.length == 0)
-            {
-                self.$annos.html('Nie ma żadnych przypisów');
-                self.$spinner.hide();
-                self.$annos.show();
-            }
-            annos.each(function (i, elem) {
-                var xml_text = serializer.serializeToString(elem).replace(ann_expr, "");
-                xml2html({
-                    xml: "<akap>" + xml_text + "</akap>",
-                    success: function(xml_text){
-                        return function(elem){
-                            elem.sortby = $(elem).text().trim();
-                            $(elem).append("<div class='src'>"+ xml_text.replace(/&/g, "&amp;").replace(/</g, "&lt;") +"</div>");
-                            anno_list.push(elem);
-                            $(".src", elem).click(function() { self.goToAnnotation(self, this); });
-                            counter--;
-
-                            if (!counter) {
-                                anno_list.sort(function(a, b){return a.sortby.localeCompare(b.sortby);});
-                                for (i in anno_list)
-                                    self.$annos.append(anno_list[i]);
-                                self.$spinner.hide();
-                                self.$annos.show();
-                            }
-
-                        }
-                    }(xml_text),
-                    error: function(text) {
-                        $.unblockUI();
-                        self.$error.html(text);
-                        self.$spinner.hide();
-                        self.$error.show();
-                    }
-                });
-            });
-        }
-    };
-
-
-    AnnotationsPerspective.prototype.onEnter = function(){
-        $.wiki.Perspective.prototype.onEnter.call(this);
-
-        $('.vsplitbar').not('.active').trigger('click');
-        $(".vsplitbar-title").html("&darr;&nbsp;PRZYPISY&nbsp;&darr;");
-        this.$refresh.filter('.active').trigger('click');
-
-    };
-
-        AnnotationsPerspective.prototype.onExit = function(success, failure) {
-
-        };
-
-    $.wiki.AnnotationsPerspective = AnnotationsPerspective;
-
-})(jQuery);
diff --git a/redakcja/static/js/wiki/view_column_diff.js b/redakcja/static/js/wiki/view_column_diff.js
deleted file mode 100644 (file)
index 33ffb6a..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-(function($){
-
-       function DiffPerspective(options) {
-               var old_callback = options.callback || function() {};
-               var self = this;
-
-        options.callback = function(){
-                       self.base_id = options.base_id;
-                       old_callback.call(this);
-               };
-
-               $.wiki.Perspective.call(this, options);
-    };
-
-    DiffPerspective.prototype = new $.wiki.Perspective();
-
-    DiffPerspective.prototype.freezeState = function(){
-        // must
-    };
-
-       DiffPerspective.prototype.destroy = function() {
-        $.wiki.switchToTab('#HistoryPerspective');
-               $('#' + this.base_id).remove();
-               $('#' + this.perspective_id).remove();
-       };
-
-       DiffPerspective.prototype.onEnter = function(success, failure){
-               $.wiki.Perspective.prototype.onEnter.call(this);
-               console.log("Entered diff view");
-       };
-
-       $.wiki.DiffPerspective = DiffPerspective;
-
-})(jQuery);
-
diff --git a/redakcja/static/js/wiki/view_editor_source.js b/redakcja/static/js/wiki/view_editor_source.js
deleted file mode 100644 (file)
index 547456f..0000000
+++ /dev/null
@@ -1,109 +0,0 @@
-/* COMMENT */
-(function($) {
-
-       function CodeMirrorPerspective(options)
-       {
-               var old_callback = options.callback;
-        options.callback = function(){
-                       var self = this;
-
-                       this.codemirror = CodeMirror.fromTextArea('codemirror_placeholder', {
-                               parserfile: 'parsexml.js',
-                               path: STATIC_URL + "js/lib/codemirror-0.8/",
-                               stylesheet: STATIC_URL + "css/xmlcolors_20100906.css",
-                               parserConfig: {
-                                       useHTMLKludges: false
-                               },
-                               iframeClass: 'xml-iframe',
-                               textWrapping: true,
-                               lineNumbers: true,
-                               width: "100%",
-                               height: "100%",
-                               tabMode: 'default',
-                               indentUnit: 0,
-                               readOnly: CurrentDocument.readonly || false,
-                               initCallback: function(){
-
-                                       self.codemirror.grabKeys(function(event) {
-                                               if (event.button) {
-                                                       $(event.button).trigger('click');
-                                                       event.button = null;
-                                               }
-                                       }, function(keycode, event) {
-                                               if(!event.altKey)
-                                                       return false;
-
-                                               var c = String.fromCharCode(keycode).toLowerCase();
-                                               var button = $("#source-editor button[data-ui-accesskey='"+c+"']");
-                                               if(button.length == 0)
-                                                       return false;
-
-                                               /* it doesn't matter which button we pick - all do the same */
-                                               event.button = button[0];
-                                               return true;
-                                       });
-
-                                       $('#source-editor .toolbar').toolbarize({
-                                           actionContext: self.codemirror
-                                       });
-
-                                       console.log("Initialized CodeMirror");
-
-                                       // textarea is no longer needed
-                                       $('codemirror_placeholder').remove();
-
-                                       old_callback.call(self);
-                               }
-                       });
-               };
-
-               $.wiki.Perspective.call(this, options);
-       };
-
-
-       CodeMirrorPerspective.prototype = new $.wiki.Perspective();
-
-       CodeMirrorPerspective.prototype.freezeState = function() {
-               this.config().position = this.codemirror.win.scrollY || 0;
-       };
-
-       CodeMirrorPerspective.prototype.unfreezeState = function () {
-               this.codemirror.win.scroll(0, this.config().position || 0);
-       };
-
-       CodeMirrorPerspective.prototype.onEnter = function(success, failure) {
-               $.wiki.Perspective.prototype.onEnter.call(this);
-
-               console.log('Entering', this.doc);
-               this.codemirror.setCode(this.doc.text);
-
-               /* fix line numbers bar */
-               var $nums = $('.CodeMirror-line-numbers');
-           var barWidth = $nums.width();
-
-               $(this.codemirror.frame.contentDocument.body).css('padding-left', barWidth);
-               // $nums.css('left', -barWidth);
-
-               $(window).resize();
-               this.unfreezeState(this._uistate);
-
-               if(success) success();
-       }
-
-       CodeMirrorPerspective.prototype.onExit = function(success, failure) {
-               this.freezeState();
-
-               $.wiki.Perspective.prototype.onExit.call(this);
-               console.log('Exiting', this.doc);
-               this.doc.setText(this.codemirror.getCode());
-
-        if ($('.vsplitbar').hasClass('active') && $('#SearchPerspective').hasClass('active')) {
-            $.wiki.switchToTab('#ScanGalleryPerspective');
-        }
-
-               if(success) success();
-       }
-
-       $.wiki.CodeMirrorPerspective = CodeMirrorPerspective;
-
-})(jQuery);
diff --git a/redakcja/static/js/wiki/view_editor_wysiwyg.js b/redakcja/static/js/wiki/view_editor_wysiwyg.js
deleted file mode 100644 (file)
index 3ec4f70..0000000
+++ /dev/null
@@ -1,676 +0,0 @@
-(function($){
-
-    /* Show theme to the user */
-    function selectTheme(themeId){
-        var selection = window.getSelection();
-        selection.removeAllRanges();
-
-        var range = document.createRange();
-        var s = $(".motyw[theme-class='" + themeId + "']")[0];
-        var e = $(".end[theme-class='" + themeId + "']")[0];
-
-        if (s && e) {
-            range.setStartAfter(s);
-            range.setEndBefore(e);
-            selection.addRange(range);
-        }
-    };
-
-    /* Verify insertion port for annotation or theme */
-    function verifyTagInsertPoint(node){
-        if (node.nodeType == 3) { // Text Node
-            node = node.parentNode;
-        }
-
-        if (node.nodeType != 1) {
-            return false;
-        }
-
-        node = $(node);
-        var xtype = node.attr('x-node');
-
-        if (!xtype || (xtype.search(':') >= 0) ||
-        xtype == 'motyw' ||
-        xtype == 'begin' ||
-        xtype == 'end') {
-            return false;
-        }
-
-        // don't allow themes inside annotations
-        if (node.closest('[x-node="pe"]').length > 0)
-            return false;
-
-        return true;
-    }
-
-    /* Convert HTML fragment to plaintext */
-    var ANNOT_FORBIDDEN = ['pt', 'pa', 'pr', 'pe', 'begin', 'end', 'motyw'];
-
-    function html2plainText(fragment){
-        var text = "";
-
-        $(fragment.childNodes).each(function(){
-            if (this.nodeType == 3) // textNode
-                text += this.nodeValue;
-            else {
-                if (this.nodeType == 1 &&
-                        $.inArray($(this).attr('x-node'), ANNOT_FORBIDDEN) == -1) {
-                    text += html2plainText(this);
-                }
-            };
-        });
-
-        return text;
-    }
-
-
-    /* Insert annotation using current selection */
-    function addAnnotation(){
-        var selection = window.getSelection();
-        var n = selection.rangeCount;
-
-        if (n == 0) {
-            window.alert("Nie zaznaczono żadnego obszaru");
-            return false;
-        }
-
-        // for now allow only 1 range
-        if (n > 1) {
-            window.alert("Zaznacz jeden obszar");
-            return false;
-        }
-
-        // remember the selected range
-        var range = selection.getRangeAt(0);
-
-        if (!verifyTagInsertPoint(range.endContainer)) {
-            window.alert("Nie można wstawić w to miejsce przypisu.");
-            return false;
-        }
-
-        // BUG #273 - selected text can contain themes, which should be omitted from
-        // defining term
-        var text = html2plainText(range.cloneContents());
-        var tag = $('<span></span>');
-        range.collapse(false);
-        range.insertNode(tag[0]);
-
-        xml2html({
-            xml: '<pe><slowo_obce>' + text + '</slowo_obce> --- </pe>',
-            success: function(text){
-                var t = $(text);
-                tag.replaceWith(t);
-                openForEdit(t);
-            },
-            error: function(){
-                tag.remove();
-                alert('Błąd przy dodawaniu przypisu:' + errors);
-            }
-        })
-    }
-
-
-    /* Insert theme using current selection */
-
-    function addTheme(){
-        var selection = window.getSelection();
-        var n = selection.rangeCount;
-
-        if (n == 0) {
-            window.alert("Nie zaznaczono żadnego obszaru");
-            return false;
-        }
-
-        // for now allow only 1 range
-        if (n > 1) {
-            window.alert("Zaznacz jeden obszar.");
-            return false;
-        }
-
-
-        // remember the selected range
-        var range = selection.getRangeAt(0);
-
-
-        if ($(range.startContainer).is('.html-editarea') ||
-        $(range.endContainer).is('.html-editarea')) {
-            window.alert("Motywy można oznaczać tylko na tekście nie otwartym do edycji. \n Zamknij edytowany fragment i spróbuj ponownie.");
-            return false;
-        }
-
-        // verify if the start/end points make even sense -
-        // they must be inside a x-node (otherwise they will be discarded)
-        // and the x-node must be a main text
-        if (!verifyTagInsertPoint(range.startContainer)) {
-            window.alert("Motyw nie może się zaczynać w tym miejscu.");
-            return false;
-        }
-
-        if (!verifyTagInsertPoint(range.endContainer)) {
-            window.alert("Motyw nie może się kończyć w tym miejscu.");
-            return false;
-        }
-
-        var date = (new Date()).getTime();
-        var random = Math.floor(4000000000 * Math.random());
-        var id = ('' + date) + '-' + ('' + random);
-
-        var createPoint = function(container, offset) {
-            var offsetBetweenCommas = function(text, offset) {
-                if(text.length < 2 || offset < 1 || offset > text.length)
-                    return false;
-                return text[offset-1] === ',' && text[offset] === ',';
-            }
-            var point = document.createRange();
-            offset = offsetBetweenCommas(container.textContent, offset) ? offset - 1 : offset;
-            point.setStart(container, offset);
-            return point;
-        }
-        
-        var spoint = createPoint(range.startContainer, range.startOffset);
-        var epoint = createPoint(range.endContainer, range.endOffset);
-               
-        var mtag, btag, etag, errors;
-
-        // insert theme-ref
-
-        xml2html({
-            xml: '<end id="e' + id + '" />',
-            success: function(text){
-                etag = $('<span></span>');
-                epoint.insertNode(etag[0]);
-                etag.replaceWith(text);
-                xml2html({
-                    xml: '<motyw id="m' + id + '"></motyw>',
-                    success: function(text){
-                        mtag = $('<span></span>');
-                        spoint.insertNode(mtag[0]);
-                        mtag.replaceWith(text);
-                        xml2html({
-                            xml: '<begin id="b' + id + '" />',
-                            success: function(text){
-                                btag = $('<span></span>');
-                                spoint.insertNode(btag[0])
-                                btag.replaceWith(text);
-                                selection.removeAllRanges();
-                                openForEdit($('.motyw[theme-class=' + id + ']'));
-                            }
-                        });
-                    }
-                });
-            }
-        });
-    }
-
-    function addSymbol() {
-        if($('div.html-editarea textarea')[0]) {
-            var specialCharsContainer = $("<div id='specialCharsContainer'><a href='#' id='specialCharsClose'>Zamknij</a><table id='tableSpecialChars' style='width: 600px;'></table></div>");
-                        
-            var specialChars = [' ', 'Ą','ą','Ć','ć','Ę','ę','Ł','ł','Ń','ń','Ó','ó','Ś','ś','Ż','ż','Ź','ź','Á','á','À','à',
-            'Â','â','Ä','ä','Å','å','Ā','ā','Ă','ă','Ã','ã',
-            'Æ','æ','Ç','ç','Č','č','Ċ','ċ','Ď','ď','É','é','È','è',
-            'Ê','ê','Ë','ë','Ē','ē','Ě','ě','Ġ','ġ','Ħ','ħ','Í','í','Î','î',
-            'Ī','ī','Ĭ','ĭ','Ľ','ľ','Ñ','ñ','Ň','ň','Ó','ó','Ö','ö',
-            'Ô','ô','Ō','ō','Ǒ','ǒ','Œ','œ','Ø','ø','Ř','ř','Š',
-            'š','Ş','ş','Ť','ť','Ţ','ţ','Ű','ű','Ú','ú','Ù','ù',
-            'Ü','ü','Ů','ů','Ū','ū','Û','û','Ŭ','ŭ',
-            'Ý','ý','Ž','ž','ß','Ð','ð','Þ','þ','А','а','Б',
-            'б','В','в','Г','г','Д','д','Е','е','Ё','ё','Ж',
-            'ж','З','з','И','и','Й','й','К','к','Л','л','М',
-            'м','Н','н','О','о','П','п','Р','р','С','с',
-            'Т','т','У','у','Ф','ф','Х','х','Ц','ц','Ч',
-            'ч','Ш','ш','Щ','щ','Ъ','ъ','Ы','ы','Ь','ь','Э',
-            'э','Ю','ю','Я','я','ѓ','є','і','ї','ј','љ','њ',
-            'Ґ','ґ','Α','α','Β','β','Γ','γ','Δ','δ','Ε','ε',
-            'Ζ','ζ','Η','η','Θ','θ','Ι','ι','Κ','κ','Λ','λ','Μ',
-            'μ','Ν','ν','Ξ','ξ','Ο','ο','Π','π','Ρ','ρ','Σ','ς','σ',
-            'Τ','τ','Υ','υ','Φ','φ','Χ','χ','Ψ','ψ','Ω','ω','–',
-            '—','¡','¿','$','¢','£','€','©','®','°','¹','²','³',
-            '¼','½','¾','†','§','‰','•','←','↑','→','↓',
-            '„','”','„”','«','»','«»','»«','’','[',']','~','|','−','·',
-            '×','÷','≈','≠','±','≤','≥','∈'];
-            var tableContent = "<tr>";
-            
-            for(var i in specialChars) {
-                if(i % 14 == 0 && i > 0) {
-                    tableContent += "</tr><tr>";
-                }              
-                tableContent += "<td><input type='button' class='specialBtn' value='"+specialChars[i]+"'/></td>";              
-            }
-            
-            tableContent += "</tr>";                                   
-            $("#content").append(specialCharsContainer);
-            
-            
-             // localStorage for recently used characters - reading
-             if (typeof(localStorage) != 'undefined') {
-                 if (localStorage.getItem("recentSymbols")) {
-                     var recent = localStorage.getItem("recentSymbols");
-                     var recentArray = recent.split(";");
-                     var recentRow = "";
-                     for(var i in recentArray.reverse()) {
-                        recentRow += "<td><input type='button' class='specialBtn recentSymbol' value='"+recentArray[i]+"'/></td>";              
-                     }
-                     recentRow = "<tr>" + recentRow + "</tr>";                              
-                 }
-             }            
-            $("#tableSpecialChars").append(recentRow);
-            $("#tableSpecialChars").append(tableContent);
-            
-            /* events */
-            
-            $('.specialBtn').click(function(){
-                var editArea = $('div.html-editarea textarea')[0];
-                var insertVal = $(this).val();
-                
-                // if we want to surround text with quotes
-                // not sure if just check if value has length == 2
-                
-                if (insertVal.length == 2) {
-                    var startTag = insertVal[0];
-                    var endTag = insertVal[1];
-                               var textAreaOpened = editArea;                                                  
-                               //IE support
-                               if (document.selection) {
-                                   textAreaOpened.focus();
-                                   sel = document.selection.createRange();
-                                   sel.text = startTag + sel.text + endTag;
-                               }
-                               //MOZILLA/NETSCAPE support
-                               else if (textAreaOpened.selectionStart || textAreaOpened.selectionStart == '0') {
-                                   var startPos = textAreaOpened.selectionStart;
-                                   var endPos = textAreaOpened.selectionEnd;
-                                   textAreaOpened.value = textAreaOpened.value.substring(0, startPos)
-                                         + startTag + textAreaOpened.value.substring(startPos, endPos) + endTag + textAreaOpened.value.substring(endPos, textAreaOpened.value.length);
-                               }                
-                } else {
-                    // if we just want to insert single symbol
-                    insertAtCaret(editArea, insertVal);
-                }
-                
-                // localStorage for recently used characters - saving
-                if (typeof(localStorage) != 'undefined') {
-                    if (localStorage.getItem("recentSymbols")) {
-                        var recent = localStorage.getItem("recentSymbols");
-                        var recentArray = recent.split(";");
-                        var valIndex = $.inArray(insertVal, recentArray);
-                        //alert(valIndex);
-                        if(valIndex == -1) {
-                            // value not present in array yet
-                            if(recentArray.length > 13){
-                                recentArray.shift();
-                                recentArray.push(insertVal);
-                            } else {
-                                recentArray.push(insertVal);
-                            }
-                        } else  {
-                            // value already in the array
-                            for(var i = valIndex; i < recentArray.length; i++){
-                                recentArray[i] = recentArray[i+1];
-                            }
-                            recentArray[recentArray.length-1] = insertVal;
-                        }
-                        localStorage.setItem("recentSymbols", recentArray.join(";"));
-                    } else {
-                        localStorage.setItem("recentSymbols", insertVal);
-                    }
-                }
-                $(specialCharsContainer).remove();
-            });         
-            $('#specialCharsClose').click(function(){
-                $(specialCharsContainer).remove();
-            });                   
-            
-        } else {
-            window.alert('Najedź na fragment tekstu, wybierz "Edytuj" i ustaw kursor na miejscu gdzie chcesz wstawić symbol.');
-        }
-    }
-
-    function insertAtCaret(txtarea,text) { 
-        /* http://www.scottklarr.com/topic/425/how-to-insert-text-into-a-textarea-where-the-cursor-is/ */
-        var scrollPos = txtarea.scrollTop; 
-        var strPos = 0; 
-        var backStart = 0;
-        var br = ((txtarea.selectionStart || txtarea.selectionStart == '0') ? "ff" : (document.selection ? "ie" : false ) );
-        if (br == "ie") { 
-            txtarea.focus();
-            var range = document.selection.createRange(); 
-            range.moveStart ('character', -txtarea.value.length); 
-            strPos = backStart = range.text.length; 
-        } else if (br == "ff") {
-            strPos = txtarea.selectionStart; 
-            backStart = txtarea.selectionEnd;
-        }
-        var front = (txtarea.value).substring(0,strPos); 
-        var back = (txtarea.value).substring(backStart,txtarea.value.length); 
-        txtarea.value=front+text+back; 
-        strPos = strPos + text.length; 
-        if (br == "ie") { 
-            txtarea.focus(); 
-            var range = document.selection.createRange(); 
-            range.moveStart ('character', -txtarea.value.length); 
-            range.moveStart ('character', strPos); 
-            range.moveEnd ('character', 0); 
-            range.select(); 
-        } else if (br == "ff") { 
-            txtarea.selectionStart = strPos; 
-            txtarea.selectionEnd = strPos; 
-            txtarea.focus(); 
-        } 
-        txtarea.scrollTop = scrollPos; 
-    } 
-
-    /* open edition window for selected fragment */
-    function openForEdit($origin){
-        var $box = null
-
-        // annotations overlay their sub box - not their own box //
-        if ($origin.is(".annotation-inline-box")) {
-            $box = $("*[x-annotation-box]", $origin);
-        }
-        else {
-            $box = $origin;
-        }
-        var x = $box[0].offsetLeft;
-        var y = $box[0].offsetTop;        
-        
-        var w = $box.outerWidth();
-        var h = $box.innerHeight();
-
-        if ($origin.is(".annotation-inline-box")) {
-            w = Math.max(w, 400);
-            h = Math.max(h, 60);
-            if($('.htmlview div').offset().left + $('.htmlview div').width() > ($('.vsplitbar').offset().left - 480)){
-                x = -(Math.max($origin.offset().left, $origin.width())); 
-            } else {
-                x = 100;
-            }
-        }
-
-        // start edition on this node
-        var $overlay = $('<div class="html-editarea"><button class="accept-button">Zapisz</button><button class="delete-button">Usuń</button><button class="tytul-button akap-edit-button">tytuł dzieła</button><button class="wyroznienie-button akap-edit-button">wyróżnienie</button><button class="slowo-button akap-edit-button">słowo obce</button><button class="znak-button akap-edit-button">znak spec.</button><textarea></textarea></div>').css({
-            position: 'absolute',
-            height: h,
-            left: x,
-            top: y,
-            width: w
-        }).appendTo($box[0].offsetParent || $box.parent()).show();
-        
-
-        if ($origin.is('.motyw')) {
-           $('.akap-edit-button').remove();
-            withThemes(function(canonThemes){
-                $('textarea', $overlay).autocomplete(canonThemes, {
-                    autoFill: true,
-                    multiple: true,
-                    selectFirst: true,
-                    highlight: false
-                });
-            })
-        }
-
-        if ($origin.is('.motyw')){
-            $('.delete-button', $overlay).click(function(){
-                if (window.confirm("Czy jesteś pewien, że chcesz usunąć ten motyw ?")) {
-                    $('[theme-class=' + $origin.attr('theme-class') + ']').remove();
-                    $overlay.remove();
-                    $(document).unbind('click.blur-overlay');
-                    return false;
-                };
-            });
-        }
-        else if($box.is('*[x-annotation-box]')) {
-            $('.delete-button', $overlay).click(function(){
-                if (window.confirm("Czy jesteś pewien, że chcesz usunąć ten przypis?")) {
-                    $origin.remove();
-                    $overlay.remove();
-                    $(document).unbind('click.blur-overlay');
-                    return false;
-                };
-            });
-        }
-        else {
-            $('.delete-button', $overlay).html("Anuluj");
-            $('.delete-button', $overlay).click(function(){
-                if (window.confirm("Czy jesteś pewien, że chcesz anulować zmiany?")) {
-                    $overlay.remove();
-                    $(document).unbind('click.blur-overlay');
-                    return false;
-                };
-            });
-        }
-
-
-        var serializer = new XMLSerializer();
-
-        html2text({
-            element: $box[0],
-            stripOuter: true,
-            success: function(text){
-                $('textarea', $overlay).val($.trim(text));
-
-                setTimeout(function(){
-                    $('textarea', $overlay).elastic().focus();
-                }, 50);
-
-                function save(argument){
-                    var nodeName = $box.attr('x-node') || 'pe';
-                    var insertedText = $('textarea', $overlay).val();
-
-                    if ($origin.is('.motyw')) {
-                        insertedText = insertedText.replace(/,\s*$/, '');
-                    }
-
-                    xml2html({
-                        xml: '<' + nodeName + '>' + insertedText + '</' + nodeName + '>',
-                        success: function(element){
-                            if (nodeName == 'out-of-flow-text') {
-                                $(element).children().insertAfter($origin);
-                                $origin.remove()
-                            }
-                            else {
-                                $origin.html($(element).html());
-                            }
-                            $overlay.remove();
-                        },
-                        error: function(text){
-                            alert('Błąd! ' + text);
-                        }
-                    })
-                    
-                    var msg = $("<div class='saveNotify'><p>Pamiętaj, żeby zapisać swoje zmiany.</p></div>");
-                    $("#base").prepend(msg);
-                    $('#base .saveNotify').fadeOut(3000, function(){
-                        $(this).remove(); 
-                    });
-                }
-
-               $('.akap-edit-button', $overlay).click(function(){
-                       var textAreaOpened = $('textarea', $overlay)[0];
-                       var startTag = "";
-                       var endTag = "";
-                       var buttonName = this.innerHTML;
-
-                       if(buttonName == "słowo obce") {
-                               startTag = "<slowo_obce>";
-                               endTag = "</slowo_obce>";
-                       } else if (buttonName == "wyróżnienie") {
-                               startTag = "<wyroznienie>";
-                               endTag = "</wyroznienie>";
-                       } else if (buttonName == "tytuł dzieła") {
-                               startTag = "<tytul_dziela>";
-                               endTag = "</tytul_dziela>";
-                       } else if(buttonName == "znak spec."){
-                           addSymbol();
-                           return false;
-                       }
-                       
-                       var myField = textAreaOpened;                   
-                        
-                       //IE support
-                       if (document.selection) {
-                           textAreaOpened.focus();
-                           sel = document.selection.createRange();
-                           sel.text = startTag + sel.text + endTag;
-                       }
-                       //MOZILLA/NETSCAPE support
-                       else if (textAreaOpened.selectionStart || textAreaOpened.selectionStart == '0') {
-                           var startPos = textAreaOpened.selectionStart;
-                           var endPos = textAreaOpened.selectionEnd;
-                           textAreaOpened.value = textAreaOpened.value.substring(0, startPos)
-                                 + startTag + textAreaOpened.value.substring(startPos, endPos) + endTag + textAreaOpened.value.substring(endPos, textAreaOpened.value.length);
-                       }
-               });
-
-                $('.accept-button', $overlay).click(function(){
-                    save();
-                    $(document).unbind('click.blur-overlay');
-                });
-
-                $(document).bind('click.blur-overlay', function(event){
-                    if ($(event.target).closest('.html-editarea, #specialCharsContainer').length > 0) {
-                        return;
-                    }
-                    save();
-                    $(document).unbind('click.blur-overlay');
-                });
-
-            },
-            error: function(text){
-                alert('Błąd! ' + text);
-            }
-        });
-    }
-
-    function VisualPerspective(options){
-
-        var old_callback = options.callback;
-
-        options.callback = function(){
-            var element = $("#html-view");
-            var button = $('<button class="edit-button">Edytuj</button>');
-
-            if (!CurrentDocument.readonly) {
-                $('#html-view').bind('mousemove', function(event){
-                    var editable = $(event.target).closest('*[x-editable]');
-                    $('.active', element).not(editable).removeClass('active').children('.edit-button').remove();
-
-                    if (!editable.hasClass('active')) {
-                        editable.addClass('active').append(button);
-                    }
-                    if (editable.is('.annotation-inline-box')) {
-                        $('*[x-annotation-box]', editable).css({
-                            position: 'absolute',
-                            left: event.clientX - editable.offset().left + 5,
-                            top: event.clientY - editable.offset().top + 5
-                        }).show();
-                    }
-                    else {
-                        $('*[x-annotation-box]').hide();
-                    }
-                });
-
-                $('#insert-annotation-button').click(function(){
-                    addAnnotation();
-                    return false;
-                });
-
-                $('#insert-theme-button').click(function(){
-                    addTheme();
-                    return false;
-                });            
-
-                $('.edit-button').live('click', function(event){
-                    event.preventDefault();
-                    openForEdit($(this).parent());
-                });
-
-            }
-
-            $('.motyw').live('click', function(){
-                selectTheme($(this).attr('theme-class'));
-            });
-
-            old_callback.call(this);
-        };
-
-        $.wiki.Perspective.call(this, options);
-    };
-
-    VisualPerspective.prototype = new $.wiki.Perspective();
-
-    VisualPerspective.prototype.freezeState = function(){
-
-    };
-
-    VisualPerspective.prototype.onEnter = function(success, failure){
-        $.wiki.Perspective.prototype.onEnter.call(this);
-
-        $.blockUI({
-            message: 'Uaktualnianie widoku...'
-        });
-
-        function _finalize(callback){
-            $.unblockUI();
-            if (callback)
-                callback();
-        }
-
-        xml2html({
-            xml: this.doc.text,
-            success: function(element){
-                var htmlView = $('#html-view');
-                htmlView.html(element);
-                htmlView.find('*[x-node]').dblclick(function(e) {
-                    if($(e.target).is('textarea'))
-                        return;
-                    var selection = window.getSelection();
-                    selection.collapseToStart();
-                    selection.modify('extend', 'forward', 'word');
-                    e.stopPropagation();
-                });
-                _finalize(success);
-            },
-            error: function(text, source){
-                err = '<p class="error">Wystąpił błąd:</p><p>'+text+'</p>';
-                if (source)
-                    err += '<pre>'+source.replace(/&/g, '&amp;').replace(/</g, '&lt;')+'</pre>'
-                $('#html-view').html(err);
-                _finalize(failure);
-            }
-        });
-    };
-
-    VisualPerspective.prototype.onExit = function(success, failure){
-        var self = this;
-
-        $.blockUI({
-            message: 'Zapisywanie widoku...'
-        });
-
-        function _finalize(callback){
-            $.unblockUI();
-            if (callback)
-                callback();
-        }
-
-        if ($('#html-view .error').length > 0)
-            return _finalize(failure);
-
-        html2text({
-            element: $('#html-view').get(0),
-            stripOuter: true,
-            success: function(text){
-                self.doc.setText(text);
-                _finalize(success);
-            },
-            error: function(text){
-                $('#source-editor').html('<p>Wystąpił błąd:</p><pre>' + text + '</pre>');
-                _finalize(failure);
-            }
-        });
-    };
-
-    $.wiki.VisualPerspective = VisualPerspective;
-
-})(jQuery);
diff --git a/redakcja/static/js/wiki/view_gallery.js b/redakcja/static/js/wiki/view_gallery.js
deleted file mode 100644 (file)
index 65a716a..0000000
+++ /dev/null
@@ -1,259 +0,0 @@
-(function($){
-
-    function normalizeNumber(pageNumber, pageCount){
-        // Page number should be >= 1, <= pageCount; 0 if pageCount = 0
-        var pageNumber = parseInt(pageNumber, 10);
-
-        if (!pageCount)
-            return 0;
-
-        if (!pageNumber ||
-                isNaN(pageNumber) ||
-                pageNumber == Infinity ||
-                pageNumber == -Infinity ||
-                pageNumber < 1)
-            return 1;
-
-        if (pageNumber > pageCount)
-            return pageCount;
-
-        return pageNumber;
-    }
-
-    function bounds(galleryWidth, galleryHeight, imageWidth, imageHeight){
-        return {
-            maxX: 0,
-            maxY: 0,
-            minX: galleryWidth - imageWidth,
-            minY: galleryHeight - imageHeight
-        }
-    };
-
-    function normalizePosition(x, y, galleryWidth, galleryHeight, imageWidth, imageHeight){
-        var b = bounds(galleryWidth, galleryHeight, imageWidth, imageHeight);
-        return {
-            x: Math.min(b.maxX, Math.max(b.minX, x)),
-            y: Math.min(b.maxY, Math.max(b.minY, y))
-        }
-    };
-
-    function fixImageSize(){
-
-    }
-
-    /*
-     * Perspective
-     */
-    function ScanGalleryPerspective(options){
-        var old_callback = options.callback || function() { };
-
-               this.noupdate_hash_onenter = true;
-        this.vsplitbar = 'GALERIA';
-
-        options.callback = function(){
-            var self = this;
-
-            this.dimensions = {};
-            this.zoomFactor = 1;
-           if (this.config().page == undefined)
-               this.config().page = CurrentDocument.galleryStart;
-            this.$element = $("#side-gallery");
-            this.$numberInput = $('.page-number', this.$element);
-
-            // ...
-            var origin = {};
-            var imageOrigin = {};
-
-            this.$image = $('.gallery-image img', this.$element).attr('unselectable', 'on');
-
-            // button handlers
-            this.$numberInput.change(function(event){
-                event.preventDefault();
-                self.setPage($(this).val());
-            });
-                    
-           $('.start-page', this.$element).click(function(){
-               self.setPage(CurrentDocument.galleryStart);
-           });
-
-            $('.previous-page', this.$element).click(function(){
-                self.setPage(parseInt(self.$numberInput.val(),10) - 1);
-            });
-
-            $('.next-page', this.$element).click(function(){
-                self.setPage(parseInt(self.$numberInput.val(),10) + 1);
-            });
-
-            $('.zoom-in', this.$element).click(function(){
-                self.alterZoom(0.2);
-            });
-
-            $('.zoom-out', this.$element).click(function(){
-                self.alterZoom((-0.2));
-            });
-
-            $(window).resize(function(){
-                self.dimensions.galleryWidth = self.$image.parent().width();
-                self.dimensions.galleryHeight = self.$image.parent().height();
-            });
-
-            this.$image.load(function(){
-                console.log("Image loaded.")
-                self._resizeImage();
-            }).bind('mousedown', function() {
-                               self.imageMoveStart.apply(self, arguments);
-                       });
-
-
-
-                       old_callback.call(this);
-        };
-
-        $.wiki.Perspective.call(this, options);
-    };
-
-    ScanGalleryPerspective.prototype = new $.wiki.Perspective();
-
-    ScanGalleryPerspective.prototype._resizeImage = function(){
-        var $img = this.$image;
-
-        $img.css({
-            width: '',
-            height: ''
-        });
-
-        this.dimensions = {
-            width: $img.width() * this.zoomFactor,
-            height: $img.height() * this.zoomFactor,
-            originWidth: $img.width(),
-            originHeight: $img.height(),
-                   galleryWidth: $img.parent().width(),
-            galleryHeight: $img.parent().height()
-        };
-
-        if (!(this.dimensions.width && this.dimensions.height)) {
-            setTimeout(function(){
-                $img.load();
-            }, 100);
-        }
-
-        var position = normalizePosition($img.position().left, $img.position().top, this.dimensions.galleryWidth, this.dimensions.galleryHeight, this.dimensions.width, this.dimensions.height);
-
-        $img.css({
-            left: position.x,
-            top: position.y,
-            width: $img.width() * this.zoomFactor,
-            height: $img.height() * this.zoomFactor
-        });
-    };
-
-    ScanGalleryPerspective.prototype.setPage = function(newPage){
-        newPage = normalizeNumber(newPage, this.doc.galleryImages.length);
-        this.$numberInput.val(newPage);
-               this.config().page = newPage;
-        $('.gallery-image img', this.$element).attr('src', this.doc.galleryImages[newPage - 1]);
-    };
-
-    ScanGalleryPerspective.prototype.alterZoom = function(delta){
-        var zoomFactor = this.zoomFactor + delta;
-        if (zoomFactor < 0.2)
-            zoomFactor = 0.2;
-        if (zoomFactor > 2)
-            zoomFactor = 2;
-        this.setZoom(zoomFactor);
-    };
-
-    ScanGalleryPerspective.prototype.setZoom = function(factor){
-        this.zoomFactor = factor;
-
-        this.dimensions.width = this.dimensions.originWidth * this.zoomFactor;
-        this.dimensions.height = this.dimensions.originHeight * this.zoomFactor;
-
-        // var position = normalizePosition(this.$image.position().left, this.$image.position().top, this.dimensions.galleryWidth, this.dimensions.galleryHeight, this.dimensions.width, this.dimensions.height);
-
-               this._resizeImage();
-    };
-
-       /*
-        * Movement
-        */
-       ScanGalleryPerspective.prototype.imageMoved = function(event){
-               event.preventDefault();
-
-               // origin is where the drag started
-               // imageOrigin is where the drag started on the image
-
-               var newX = event.clientX - this.origin.x + this.imageOrigin.left;
-               var newY = event.clientY - this.origin.y + this.imageOrigin.top;
-
-               var position = normalizePosition(newX, newY, this.dimensions.galleryWidth, this.dimensions.galleryHeight, this.dimensions.width, this.dimensions.height);
-
-               this.$image.css({
-                       left: position.x,
-                       top: position.y,
-               });
-
-               return false;
-       };
-
-       ScanGalleryPerspective.prototype.imageMoveStart = function(event){
-               event.preventDefault();
-
-               var self = this;
-
-               this.origin = {
-                       x: event.clientX,
-                       y: event.clientY
-               };
-
-               this.imageOrigin = self.$image.position();
-               $(document).bind('mousemove.gallery', function(){
-                       self.imageMoved.apply(self, arguments);
-               }).bind('mouseup.gallery', function() {
-                       self.imageMoveStop.apply(self, arguments);
-               });
-
-               return false;
-       };
-
-       ScanGalleryPerspective.prototype.imageMoveStop = function(event){
-               $(document).unbind('mousemove.gallery').unbind('mouseup.gallery');
-       };
-
-    /*
-     * Loading gallery
-     */
-    ScanGalleryPerspective.prototype.onEnter = function(success, failure){
-        var self = this;
-
-        $.wiki.Perspective.prototype.onEnter.call(this);
-
-        $('.vsplitbar').not('.active').trigger('click');
-        $(".vsplitbar-title").html("&darr;&nbsp;GALERIA&nbsp;&darr;");        
-        
-        this.doc.refreshGallery({
-            success: function(doc, data){
-                self.$image.show();
-                               console.log("gconfig:", self.config().page );
-                               self.setPage( self.config().page );
-                $('#imagesCount').html("/" + doc.galleryImages.length);
-
-                $('.error_message', self.$element).hide();
-                if(success) success();
-            },
-            failure: function(doc, message){
-                self.$image.hide();
-                $('.error_message', self.$element).show().html(message);
-                if(failure) failure();
-            }
-        });
-
-    };
-
-       ScanGalleryPerspective.prototype.onExit = function(success, failure) {
-
-       };
-
-    $.wiki.ScanGalleryPerspective = ScanGalleryPerspective;
-
-})(jQuery);
diff --git a/redakcja/static/js/wiki/view_history.js b/redakcja/static/js/wiki/view_history.js
deleted file mode 100644 (file)
index 85adca0..0000000
+++ /dev/null
@@ -1,204 +0,0 @@
-(function($){
-
-    function HistoryPerspective(options) {
-               var old_callback = options.callback || function() {};
-
-               options.callback = function() {
-                       var self = this;
-            if (CurrentDocument.diff) {
-                rev_from = CurrentDocument.diff[0];
-                rev_to = CurrentDocument.diff[1];
-                this.doc.fetchDiff({
-                    from: rev_from,
-                    to: rev_to,
-                    success: function(doc, data){
-                        var result = $.wiki.newTab(doc, ''+rev_from +' -> ' + rev_to, 'DiffPerspective');
-
-                        $(result.view).html(data);
-                        $.wiki.switchToTab(result.tab);
-                    }
-                });
-            }
-
-                       // first time page is rendered
-               $('#make-diff-button').click(function() {
-                               self.makeDiff();
-                       });
-
-                       $('#pubmark-changeset-button').click(function() {
-                               self.showPubmarkForm();
-                       });
-
-               $('#doc-revert-button').click(function() {
-                   self.revertDialog();
-               });
-
-                       $('#open-preview-button').click(function(event) {
-                               var selected = $('#changes-list .entry.selected');
-
-                               if (selected.length != 1) {
-                           window.alert("Wybierz dokładnie *jedną* wersję.");
-                       return;
-                       }
-
-                               var version = parseInt($("*[data-stub-value='version']", selected[0]).text());
-                               window.open($(this).attr('data-basehref') + "?revision=" + version);
-
-                               event.preventDefault();
-                       });
-
-               $('#changes-list .entry').live('click', function(){
-               var $this = $(this);
-
-               var selected_count = $("#changes-list .entry.selected").length;
-
-               if ($this.hasClass('selected')) {
-                       $this.removeClass('selected');
-                       selected_count -= 1;
-               }
-               else {
-                   if (selected_count  < 2) {
-                       $this.addClass('selected');
-                       selected_count += 1;
-                   };
-               };
-
-               $('#history-view-editor .toolbar button').attr('disabled', 'disabled').
-                   filter('*[data-enabled-when~=' + selected_count + '], *[data-enabled-when~=*]').
-                   attr('disabled', null);
-               });
-
-           $('#changes-list span.tag').live('click', function(event){
-                   return false;
-               });
-
-               old_callback.call(this);
-               }
-
-               $.wiki.Perspective.call(this, options);
-    };
-
-    HistoryPerspective.prototype = new $.wiki.Perspective();
-
-    HistoryPerspective.prototype.freezeState = function(){
-        // must
-    };
-
-    HistoryPerspective.prototype.onEnter = function(success, failure){
-        $.wiki.Perspective.prototype.onEnter.call(this);
-
-        $.blockUI({
-            message: 'Odświeżanie historii...'
-        });
-
-        function _finalize(s){
-            $.unblockUI();
-
-            if (s) {
-                if (success)
-                    success();
-            }
-            else {
-                if (failure)
-                    failure();
-            }
-        }
-
-        function _failure(doc, message){
-            $('#history-view .message-box').html('Nie udało się odświeżyć historii:' + message).show();
-            _finalize(false);
-        };
-
-        function _success(doc, data){
-            $('#history-view .message-box').hide();
-            var changes_list = $('#changes-list');
-            var $stub = $('#history-view .row-stub');
-            changes_list.html('');
-
-                       var tags = $('select#id_addtag-tag option');
-
-            $.each(data, function(){
-                $.wiki.renderStub({
-                                       container: changes_list,
-                                       stub: $stub,
-                                       data: this,
-                                       filters: {
-//                                             tag: function(value) {
-//                                                     return tags.filter("*[value='"+value+"']").text();
-//                                             }
-//                        description: function(value) {
-//                                                 return value.replace('\n', ');
-//                                             }
-                                       }
-                               });
-            });
-
-            _finalize(true);
-        };
-
-        return this.doc.fetchHistory({
-            success: _success,
-            failure: _failure
-        });
-    };
-
-       HistoryPerspective.prototype.showPubmarkForm = function(){
-               var selected = $('#changes-list .entry.selected');
-
-               if (selected.length != 1) {
-            window.alert("Musisz zaznaczyć dokładnie jedną wersję.");
-            return;
-        }
-
-               var version = parseInt($("*[data-stub-value='version']", selected[0]).text());
-               $.wiki.showDialog('#pubmark_dialog', {'revision': version});
-       };
-
-       HistoryPerspective.prototype.makeDiff = function() {
-        var changelist = $('#changes-list');
-        var selected = $('.entry.selected', changelist);
-
-        if (selected.length != 2) {
-            window.alert("Musisz zaznaczyć dokładnie dwie wersje do porównania.");
-            return;
-        }
-
-        $.blockUI({
-            message: 'Wczytywanie porównania...'
-        });
-
-               var rev_from = $("*[data-stub-value='version']", selected[1]).text();
-               var rev_to =  $("*[data-stub-value='version']", selected[0]).text();
-
-        return this.doc.fetchDiff({
-            from: rev_from,
-                       to: rev_to,
-            success: function(doc, data){
-                var result = $.wiki.newTab(doc, ''+rev_from +' -> ' + rev_to, 'DiffPerspective');
-
-                               $(result.view).html(data);
-                               $.wiki.switchToTab(result.tab);
-                               $.unblockUI();
-            },
-            failure: function(doc){
-                $.unblockUI();
-            }
-        });
-    };
-
-    HistoryPerspective.prototype.revertDialog = function(){
-        var self = this;
-        var selected = $('#changes-list .entry.selected');
-
-        if (selected.length != 1) {
-            window.alert("Musisz zaznaczyć dokładnie jedną wersję.");
-            return;
-        }
-
-        var version = parseInt($("*[data-stub-value='version']", selected[0]).text());
-        $.wiki.showDialog('#revert_dialog', {revision: version});
-    };
-
-    $.wiki.HistoryPerspective = HistoryPerspective;
-
-})(jQuery);
diff --git a/redakcja/static/js/wiki/view_search.js b/redakcja/static/js/wiki/view_search.js
deleted file mode 100644 (file)
index b49671c..0000000
+++ /dev/null
@@ -1,117 +0,0 @@
-(function($){
-
-    /*
-     * Perspective
-     */
-    function SearchPerspective(options){
-        var old_callback = options.callback || function() { };
-
-        this.noupdate_hash_onenter = true;
-        this.vsplitbar = 'ZNAJDŹ&nbsp;I&nbsp;ZAMIEŃ';
-
-        options.callback = function(){
-            var self = this;
-
-            this.editor = null;
-            this.$element = $("#side-search");
-            this.$searchInput = $('#search-input', this.$element);
-            this.$replaceInput = $('#replace-input', this.$element);
-            this.$searchButton = $('#search-button', this.$element);
-            this.$replaceButton = $('#replace-button', this.$element);
-
-            this.$replaceButton.attr("disabled","disabled");
-            this.options = Array();
-
-            // handlers
-            this.$searchInput.change(function(event){
-                self.searchCursor = null;
-            });
-            this.$replaceInput.change(function(event){
-                self.searchCursor = null;
-            });
-
-            $("#side-search input:checkbox").each(function() {
-                self.options[this.id] = this.checked;
-            }).change(function(){
-                self.options[this.id] = this.checked;
-                self.searchCursor = null;
-            });
-
-            this.$searchButton.click(function(){
-                if (!self.search())
-                    alert('Brak wyników.');
-            });
-
-            this.$replaceButton.click(function(){
-                self.replace();
-            });
-
-            old_callback.call(this);
-        };
-
-        $.wiki.Perspective.call(this, options);
-    };
-
-    SearchPerspective.prototype = new $.wiki.Perspective();
-
-    SearchPerspective.prototype.search = function(){
-        var self = this;
-        var query = self.$searchInput.val();
-
-        if (!self.editor)
-            self.editor = $.wiki.perspectiveForTab('#CodeMirrorPerspective').codemirror
-
-        if (!self.searchCursor) {
-            self.searchCursor = self.editor.getSearchCursor(
-                self.$searchInput.val(), 
-                self.options['search-from-cursor'], 
-                !self.options['search-case-sensitive']
-            );
-        }
-        if (self.searchCursor.findNext()) {
-            self.searchCursor.select();
-            self.$replaceButton.removeAttr("disabled");
-            return true;
-        }
-        else {
-            self.searchCursor = null;
-            this.$replaceButton.attr("disabled","disabled");
-            return false;
-        }
-    };
-
-    SearchPerspective.prototype.replace = function(){
-        var self = this;
-        var query = self.$replaceInput.val();
-
-        if (!self.searchCursor) {
-            self.search();
-        }
-        else {}
-        self.searchCursor.select();
-        self.searchCursor.replace(query);
-        if(self.search() && self.options['replace-all']) {
-            self.replace();
-        }
-    };
-
-    SearchPerspective.prototype.onEnter = function(success, failure){
-        var self = this;
-
-        $.wiki.Perspective.prototype.onEnter.call(this);
-        self.$searchCursor = null;
-
-        $('.vsplitbar').not('.active').trigger('click');
-        $(".vsplitbar-title").html("&darr;&nbsp;ZNAJDŹ&nbsp;I&nbsp;ZAMIEŃ&nbsp;&darr;");        
-        
-        if ($.wiki.activePerspective() != 'CodeMirrorPerspective')
-            $.wiki.switchToTab('#CodeMirrorPerspective');
-    };
-
-    SearchPerspective.prototype.onExit = function(success, failure) {
-
-    };
-
-    $.wiki.SearchPerspective = SearchPerspective;
-
-})(jQuery);
diff --git a/redakcja/static/js/wiki/view_summary.js b/redakcja/static/js/wiki/view_summary.js
deleted file mode 100644 (file)
index 099a0e8..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-(function($){
-
-       function SummaryPerspective(options) {
-               var old_callback = options.callback || function() {};
-
-               options.callback = function() {
-                       var self = this;
-
-                       // first time page is rendered
-               $('#summary-cover-refresh').click(function() {
-                               self.refreshCover();
-                       });
-
-               old_callback.call(this);
-               }
-
-               $.wiki.Perspective.call(this, options);
-    };
-
-    SummaryPerspective.prototype = new $.wiki.Perspective();
-
-       SummaryPerspective.prototype.refreshCover = function() {
-               $('#summary-cover-refresh').attr('disabled', 'disabled');
-               this.doc.refreshCover({
-                       success: function(text) {
-                               $('#summary-cover').attr('src', text);
-                       $('#summary-cover-refresh').removeAttr('disabled');
-                       }
-               });
-       };
-
-    SummaryPerspective.prototype.showCharCount = function() {
-        var cc;
-        try {
-            cc = this.doc.getLength();
-            $('#charcount_untagged').hide();
-        }
-        catch (e) {
-            $('#charcount_untagged').show();
-            cc = this.doc.text.replace(/\s{2,}/g, ' ').length;
-        }
-        $('#charcount').html(cc);
-        $('#charcount_pages').html((Math.round(cc/18)/100).toLocaleString());
-    }
-
-    SummaryPerspective.prototype.freezeState = function(){
-        // must
-    };
-
-       SummaryPerspective.prototype.onEnter = function(success, failure){
-               $.wiki.Perspective.prototype.onEnter.call(this);
-               
-               this.showCharCount();
-
-               console.log("Entered summery view");
-       };
-
-       $.wiki.SummaryPerspective = SummaryPerspective;
-
-})(jQuery);
-
diff --git a/redakcja/static/js/wiki/wikiapi.js b/redakcja/static/js/wiki/wikiapi.js
deleted file mode 100644 (file)
index 8df3ef5..0000000
+++ /dev/null
@@ -1,434 +0,0 @@
-(function($) {
-       $.wikiapi = {};
-       var noop = function() {
-       };
-       var noops = {
-               success: noop,
-               failure: noop
-       };
-       /*
-        * Return absolute reverse path of given named view. (at least he have it
-        * hard-coded in one place)
-        *
-        * TODO: think of a way, not to hard-code it here ;)
-        *
-        */
-       function reverse() {
-               var vname = arguments[0];
-               var base_path = "/editor";
-
-               if (vname == "ajax_document_text") {
-                       var path = "/text/" + arguments[1] + '/';
-
-               if (arguments[2] !== undefined)
-                               path += arguments[2] + '/';
-
-                       return base_path + path;
-               }
-
-        if (vname == "ajax_document_revert") {
-            return base_path + "/revert/" + arguments[1] + '/';
-        }
-
-
-               if (vname == "ajax_document_history") {
-
-                       return base_path + "/history/" + arguments[1] + '/';
-               }
-
-               if (vname == "ajax_document_gallery") {
-
-                       return base_path + "/gallery/" + arguments[1] + '/';
-               }
-
-               if (vname == "ajax_document_diff")
-                       return base_path + "/diff/" + arguments[1] + '/';
-
-        if (vname == "ajax_document_rev")
-            return base_path + "/rev/" + arguments[1] + '/';
-
-               if (vname == "ajax_document_pubmark")
-                       return base_path + "/pubmark/" + arguments[1] + '/';
-
-               if (vname == "ajax_cover_preview")
-                       return "/cover/preview/";
-
-               console.log("Couldn't reverse match:", vname);
-               return "/404.html";
-       };
-
-       /*
-        * Document Abstraction
-        */
-       function WikiDocument(element_id) {
-               var meta = $('#' + element_id);
-               this.id = meta.attr('data-chunk-id');
-
-               this.revision = $("*[data-key='revision']", meta).text();
-               this.readonly = !!$("*[data-key='readonly']", meta).text();
-
-               this.galleryLink = $("*[data-key='gallery']", meta).text();
-        this.galleryStart = parseInt($("*[data-key='gallery-start']", meta).text());
-
-        var diff = $("*[data-key='diff']", meta).text();
-        if (diff) {
-            diff = diff.split(',');
-            if (diff.length == 2 && diff[0] < diff[1])
-                this.diff = diff;
-            else if (diff.length == 1) {
-                diff = parseInt(diff);
-                if (diff != NaN)
-                    this.diff = [diff - 1, diff];
-            }
-        }
-
-               this.galleryImages = [];
-               this.text = null;
-               this.has_local_changes = false;
-               this._lock = -1;
-               this._context_lock = -1;
-               this._lock_count = 0;
-       };
-
-       WikiDocument.prototype.triggerDocumentChanged = function() {
-               $(document).trigger('wlapi_document_changed', this);
-       };
-       /*
-        * Fetch text of this document.
-        */
-       WikiDocument.prototype.fetch = function(params) {
-               params = $.extend({}, noops, params);
-               var self = this;
-               $.ajax({
-                       method: "GET",
-                       url: reverse("ajax_document_text", self.id),
-                       data: {"revision": self.revision},
-                       dataType: 'json',
-                       success: function(data) {
-                               var changed = false;
-
-                               if (self.text === null || self.revision !== data.revision) {
-                                       self.text = data.text;
-                                       self.revision = data.revision;
-                                       self.gallery = data.gallery;
-                                       changed = true;
-                                       self.triggerDocumentChanged();
-                               };
-
-                               self.has_local_changes = false;
-                               params['success'](self, changed);
-                       },
-                       error: function() {
-                               params['failure'](self, "Nie udało się wczytać treści dokumentu.");
-                       }
-               });
-       };
-       /*
-        * Fetch history of this document.
-        *
-        * from - First revision to fetch (default = 0) upto - Last revision to
-        * fetch (default = tip)
-        *
-        */
-       WikiDocument.prototype.fetchHistory = function(params) {
-               /* this doesn't modify anything, so no locks */
-               params = $.extend({}, noops, params);
-               var self = this;
-               $.ajax({
-                       method: "GET",
-                       url: reverse("ajax_document_history", self.id),
-                       dataType: 'json',
-                       data: {
-                               "from": params['from'],
-                               "upto": params['upto']
-                       },
-                       success: function(data) {
-                               params['success'](self, data);
-                       },
-                       error: function() {
-                               params['failure'](self, "Nie udało się wczytać historii dokumentu.");
-                       }
-               });
-       };
-       WikiDocument.prototype.fetchDiff = function(params) {
-               /* this doesn't modify anything, so no locks */
-               var self = this;
-               params = $.extend({
-                       'from': self.revision,
-                       'to': self.revision
-               }, noops, params);
-               $.ajax({
-                       method: "GET",
-                       url: reverse("ajax_document_diff", self.id),
-                       dataType: 'html',
-                       data: {
-                               "from": params['from'],
-                               "to": params['to']
-                       },
-                       success: function(data) {
-                               params['success'](self, data);
-                       },
-                       error: function() {
-                               params['failure'](self, "Nie udało się wczytać porównania wersji.");
-                       }
-               });
-       };
-
-    WikiDocument.prototype.checkRevision = function(params) {
-        /* this doesn't modify anything, so no locks */
-        var self = this;
-        $.ajax({
-            method: "GET",
-            url: reverse("ajax_document_rev", self.id),
-            dataType: 'text',
-            success: function(data) {
-                if (data == '') {
-                    if (params.error)
-                        params.error();
-                }
-                else if (data != self.revision)
-                    params.outdated();
-            }
-        });
-    };
-
-       /*
-        * Fetch gallery
-        */
-       WikiDocument.prototype.refreshGallery = function(params) {
-               params = $.extend({}, noops, params);
-               var self = this;
-               $.ajax({
-                       method: "GET",
-                       url: reverse("ajax_document_gallery", self.galleryLink),
-                       dataType: 'json',
-                       // data: {},
-                       success: function(data) {
-                               self.galleryImages = data;
-                               params['success'](self, data);
-                       },
-                       error: function(xhr) {
-                switch (xhr.status) {
-                    case 403:
-                        var msg = 'Galerie dostępne tylko dla zalogowanych użytkowników.';
-                        break;
-                    case 404:
-                        var msg = "Nie znaleziono galerii o nazwie: '" + self.galleryLink + "'.";
-                    default:
-                        var msg = "Nie udało się wczytać galerii o nazwie: '" + self.galleryLink + "'.";
-                }
-                               self.galleryImages = [];
-                               params['failure'](self, "<p>" + msg + "</p>");
-                       }
-               });
-       };
-
-       /*
-        * Set document's text
-        */
-       WikiDocument.prototype.setText = function(text) {
-               return this.setDocumentProperty('text', text);
-       };
-
-       /*
-        * Set document's gallery link
-        */
-       WikiDocument.prototype.setGalleryLink = function(gallery) {
-               return this.setDocumentProperty('galleryLink', gallery);
-       };
-
-       /*
-        * Set document's property
-        */
-       WikiDocument.prototype.setDocumentProperty = function(property, value) {
-               if(this[property] !== value) {
-                       this[property] = value;
-                       this.has_local_changes = true;
-               }
-       };
-
-       /*
-        * Save text back to the server
-        */
-       WikiDocument.prototype.save = function(params) {
-               params = $.extend({}, noops, params);
-               var self = this;
-
-               if (!self.has_local_changes) {
-                       console.log("Abort: no changes.");
-                       return params['success'](self, false, "Nie ma zmian do zapisania.");
-               };
-
-               // Serialize form to dictionary
-               var data = {};
-               $.each(params['form'].serializeArray(), function() {
-                       data[this.name] = this.value;
-               });
-
-               data['textsave-text'] = self.text;
-
-               $.ajax({
-                       url: reverse("ajax_document_text", self.id),
-                       type: "POST",
-                       dataType: "json",
-                       data: data,
-                       success: function(data) {
-                               var changed = false;
-
-                $('#header').removeClass('saving');
-
-                               if (data.text) {
-                                       self.text = data.text;
-                                       self.revision = data.revision;
-                                       self.gallery = data.gallery;
-                                       changed = true;
-                                       self.triggerDocumentChanged();
-                               };
-
-                               params['success'](self, changed, ((changed && "Udało się zapisać :)") || "Twoja wersja i serwera jest identyczna"));
-                       },
-                       error: function(xhr) {
-                if ($('#header').hasClass('saving')) {
-                    $('#header').removeClass('saving');
-                    $.blockUI({
-                        message: "<p>Nie udało się zapisać zmian. <br/><button onclick='$.unblockUI()'>OK</button></p>"
-                    })
-                }
-                else {
-                    try {
-                        params['failure'](self, $.parseJSON(xhr.responseText));
-                    }
-                    catch (e) {
-                        params['failure'](self, {
-                            "__message": "<p>Nie udało się zapisać - błąd serwera.</p>"
-                        });
-                    };
-                }
-
-                       }
-               });
-
-        $('#save-hide').click(function(){
-            $('#header').addClass('saving');
-            $.unblockUI();
-            $.wiki.blocking.unblock();
-        });
-       }; /* end of save() */
-
-    WikiDocument.prototype.revertToVersion = function(params) {
-        var self = this;
-        params = $.extend({}, noops, params);
-
-        if (params.revision >= this.revision) {
-            params.failure(self, 'Proszę wybrać rewizję starszą niż aktualna.');
-            return;
-        }
-
-        // Serialize form to dictionary
-        var data = {};
-        $.each(params['form'].serializeArray(), function() {
-            data[this.name] = this.value;
-        });
-
-        $.ajax({
-            url: reverse("ajax_document_revert", self.id),
-            type: "POST",
-            dataType: "json",
-            data: data,
-            success: function(data) {
-                if (data.text) {
-                    self.text = data.text;
-                    self.revision = data.revision;
-                    self.gallery = data.gallery;
-                    self.triggerDocumentChanged();
-
-                    params.success(self, "Udało się przywrócić wersję :)");
-                }
-                else {
-                    params.failure(self, "Przywracana wersja identyczna z aktualną. Anulowano przywracanie.");
-                }
-            },
-            error: function(xhr) {
-                params.failure(self, "Nie udało się przywrócić wersji - błąd serwera.");
-            }
-        });
-    };
-
-       WikiDocument.prototype.pubmark = function(params) {
-               params = $.extend({}, noops, params);
-               var self = this;
-               var data = {
-                       "pubmark-id": self.id,
-               };
-
-               /* unpack form */
-               $.each(params.form.serializeArray(), function() {
-                       data[this.name] = this.value;
-               });
-
-               $.ajax({
-                       url: reverse("ajax_document_pubmark", self.id),
-                       type: "POST",
-                       dataType: "json",
-                       data: data,
-                       success: function(data) {
-                               params.success(self, data.message);
-                       },
-                       error: function(xhr) {
-                               if (xhr.status == 403 || xhr.status == 401) {
-                                       params.failure(self, {
-                                               "__all__": ["Nie masz uprawnień lub nie jesteś zalogowany."]
-                                       });
-                               }
-                               else {
-                                       try {
-                                               params.failure(self, $.parseJSON(xhr.responseText));
-                                       }
-                                       catch (e) {
-                                               params.failure(self, {
-                                                       "__all__": ["Nie udało się - błąd serwera."]
-                                               });
-                                       };
-                               };
-                       }
-               });
-       };
-
-       WikiDocument.prototype.refreshCover = function(params) {
-        var self = this;
-               var data = {
-                       xml: self.text // TODO: send just DC
-               };
-        $.ajax({
-            url: reverse("ajax_cover_preview"),
-            type: "POST",
-            data: data,
-            success: function(data) {
-                params.success(data);
-            },
-            error: function(xhr) {
-                // params.failure("Nie udało się odświeżyć okładki - błąd serwera.");
-            }
-        });
-       };
-
-
-    WikiDocument.prototype.getLength = function(params) {
-        var xml = this.text.replace(/\/(\s+)/g, '<br />$1');
-        var parser = new DOMParser();
-        var doc = parser.parseFromString(xml, 'text/xml');
-        var error = $('parsererror', doc);
-
-        if (error.length > 0) {
-            throw "Not an XML document.";
-        }
-        $.xmlns["rdf"] = "http://www.w3.org/1999/02/22-rdf-syntax-ns#"; 
-        $('rdf|RDF, motyw, pa, pe, pr, pt', doc).remove();
-        var text = $(doc).text();
-        text = $.trim(text.replace(/\s{2,}/g, ' '));
-        return text.length;
-    }
-
-
-       $.wikiapi.WikiDocument = WikiDocument;
-})(jQuery);
diff --git a/redakcja/static/js/wiki/xslt.js b/redakcja/static/js/wiki/xslt.js
deleted file mode 100644 (file)
index ab90e0c..0000000
+++ /dev/null
@@ -1,393 +0,0 @@
-/*
- *
- * XSLT STUFF
- *
- */
-function createXSLT(xsl) {
-    var p = new XSLTProcessor();
-    p.importStylesheet(xsl);
-    return p;
-}
-
-var xml2htmlStylesheet = null;
-
-// Wykonuje block z załadowanymi arkuszami stylów
-function withStylesheets(code_block, onError)
-{
-    if (!xml2htmlStylesheet) {
-       $.blockUI({message: 'Ładowanie arkuszy stylów...'});
-       $.ajax({
-               url: STATIC_URL + 'xsl/wl2html_client.xsl?20171106',
-               dataType: 'xml',
-               timeout: 10000,
-               success: function(data) {
-               xml2htmlStylesheet = createXSLT(data);
-                $.unblockUI();
-                               code_block();
-
-            },
-                       error: onError
-        })
-    }
-       else {
-               code_block();
-       }
-}
-
-
-// Wykonuje block z załadowanymi kanonicznymi motywami
-function withThemes(code_block, onError)
-{
-    if (typeof withThemes.canon == 'undefined') {
-        $.ajax({
-            url: '/editor/themes',
-            dataType: 'text',
-            success: function(data) {
-                withThemes.canon = data.split('\n');
-                code_block(withThemes.canon);
-            },
-            error: function() {
-                withThemes.canon = null;
-                code_block(withThemes.canon);
-            }
-        })
-    }
-    else {
-        code_block(withThemes.canon);
-    }
-}
-
-
-function xml2html(options) {
-    withStylesheets(function() {
-        var xml = options.xml.replace(/\/(\s+)/g, '<br />$1');
-        xml = xml.replace(/([^a-zA-Z0-9ąćęłńóśźżĄĆĘŁŃÓŚŹŻ\s<>«»\\*_!,:;?&%."'=#()\/-]+)/g, '<alien>$1</alien>');
-        var parser = new DOMParser();
-        var serializer = new XMLSerializer();
-        var doc = parser.parseFromString(xml, 'text/xml');
-        var error = $('parsererror', doc);
-
-        if (error.length == 0) {
-            doc = xml2htmlStylesheet.transformToFragment(doc, document);
-            console.log(doc.firstChild);
-
-        if(doc.firstChild === null) {
-            options.error("Błąd w przetwarzaniu XML.");
-                return;
-            }
-
-            error = $('parsererror', doc);
-        }
-
-        if (error.length > 0 && options.error) {
-            source = $('sourcetext', doc);
-            source_text = source.text();
-            source.text('');
-            options.error(error.text(), source_text);
-        } else {
-            options.success(doc.childNodes);
-
-            withThemes(function(canonThemes) {
-                if (canonThemes != null) {
-                    $('.theme-text-list').addClass('canon').each(function(){
-                        var themes = $(this).html().split(',');
-                        for (i in themes) {
-                            themes[i] = $.trim(themes[i]);
-                            if (canonThemes.indexOf(themes[i]) == -1)
-                                themes[i] = '<span x-pass-thru="true" class="noncanon">' + themes[i] + "</span>"
-                        }
-                        $(this).html(themes.join(', '));
-                    });
-                }
-            });
-        }
-    }, function() { options.error && options.error('Nie udało się załadować XSLT'); });
-}
-
-/* USEFULL CONSTANTS */
-const ELEMENT_NODE                                      = 1;
-const ATTRIBUTE_NODE                 = 2;
-const TEXT_NODE                      = 3;
-const CDATA_SECTION_NODE             = 4;
-const ENTITY_REFERENCE_NODE          = 5;
-const ENTITY_NODE                    = 6;
-const PROCESSING_INSTRUCTION_NODE    = 7;
-const COMMENT_NODE                   = 8;
-const DOCUMENT_NODE                  = 9;
-const DOCUMENT_TYPE_NODE             = 10;
-const DOCUMENT_FRAGMENT_NODE         = 11;
-const NOTATION_NODE                  = 12;
-const XATTR_RE = /^x-attr-name-(.*)$/;
-
-const ELEM_START = 1;
-const ELEM_END = 2;
-const NS_END = 3;
-
-const NAMESPACES = {
-       // namespaces not listed here will be assigned random names
-       "http://www.w3.org/1999/02/22-rdf-syntax-ns#": "rdf",
-       "http://purl.org/dc/elements/1.1/": "dc",
-       "http://www.w3.org/XML/1998/namespace": "xml"
-};
-
-function HTMLSerializer() {
-       // empty constructor
-}
-
-
-
-HTMLSerializer.prototype._prepare = function() {
-       this.stack = [];
-
-       // XML namespace is implicit
-       this.nsMap = {"http://www.w3.org/XML/1998/namespace": "xml"};
-
-       this.result = "";
-       this.nsCounter = 1;
-}
-
-HTMLSerializer.prototype._pushElement = function(element) {
-       this.stack.push({
-               "type": ELEM_START,
-               "node": element
-       });
-}
-
-HTMLSerializer.prototype._pushChildren = function(element) {
-       for(var i = element.childNodes.length-1; i >= 0; i--)
-               this._pushElement(element.childNodes.item(i));
-}
-
-HTMLSerializer.prototype._pushTagEnd = function(tagName) {
-       this.stack.push({
-               "type": ELEM_END,
-               "tagName": tagName
-       });
-}
-
-HTMLSerializer.prototype._verseBefore = function(node) {
-    /* true if previous element is a previous verse of a stanza */
-    var parent = node.parentNode;
-    if (!parent || !parent.hasAttribute('x-node') || parent.getAttribute('x-node') != 'strofa')
-        return false;
-
-       var prev = node.previousSibling;
-
-       while((prev !== null) && (prev.nodeType != ELEMENT_NODE)) {
-               prev = prev.previousSibling;
-       }
-
-       return (prev !== null) && prev.hasAttribute('x-verse');
-}
-
-HTMLSerializer.prototype._nodeIgnored = function(node) {
-    return node.getAttribute('x-node') == 'wers';
-}
-
-HTMLSerializer.prototype._ignoredWithWhitespace = function(node) {
-    while (node.nodeType == ELEMENT_NODE && this._nodeIgnored(node) && node.childNodes.length > 0)
-        node = node.childNodes[0];
-    if (node.nodeType == TEXT_NODE)
-        return node.nodeValue.match(/^\s/)
-    else return false;
-}
-
-
-HTMLSerializer.prototype.serialize = function(rootElement, stripOuter)
-{
-       var self = this;
-       self._prepare();
-
-       if(!stripOuter)
-               self._pushElement(rootElement);
-       else
-               self._pushChildren(rootElement);
-
-    var text_buffer = '';
-
-       while(self.stack.length > 0) {
-               var token = self.stack.pop();
-
-        if(token.type === ELEM_END) {
-            self.result += text_buffer;
-            text_buffer = '';
-            if (token.tagName != '')
-                self.result += "</" + token.tagName + ">";
-            continue;
-        };
-
-               if(token.type === NS_END) {
-                       self._unassignNamespace(token.namespace);
-                       continue;
-               }
-
-
-               switch(token.node.nodeType) {
-                       case ELEMENT_NODE:
-                               if(token.node.hasAttribute('x-pass-thru')
-                                || token.node.hasAttribute('data-pass-thru')) {
-                                       self._pushChildren(token.node);
-                                       break;
-                               }
-
-                               if(!token.node.hasAttribute('x-node'))
-                                       break;
-
-                               var xnode = token.node.getAttribute('x-node');
-
-                               if(xnode === 'out-of-flow-text') {
-                                       self._pushChildren(token.node);
-                                       break;
-                               }
-
-                if(token.node.hasAttribute('x-verse') && self._verseBefore(token.node)) {
-                    self.result += '/';
-                    // add whitespace if there's none
-                    if (!(text_buffer.match(/^\s/) || self._ignoredWithWhitespace(token.node)))
-                        self.result += ' ';
-                }
-
-                self.result += text_buffer;
-                text_buffer = '';
-                               self._serializeElement(token.node);
-                               break;
-                       case TEXT_NODE:
-                               self.result += text_buffer;
-                               text_buffer = token.node.nodeValue.replace(/&/g, '&amp;').replace(/</g, '&lt;');
-                               break;
-            case COMMENT_NODE:
-                self.result += text_buffer;
-                text_buffer = '';
-                self.result += '<!--' + token.node.nodeValue + '-->';
-                break;
-               };
-       };
-    self.result += text_buffer;
-
-       return this.result;
-}
-
-/*
- * TODO: this doesn't support prefix redefinitions
- */
-HTMLSerializer.prototype._unassignNamespace = function(nsData) {
-       this.nsMap[nsData.uri] = undefined;
-};
-
-HTMLSerializer.prototype._assignNamespace = function(uri) {
-       if(uri === null) {
-               // default namespace
-               return ({"prefix": "", "uri": "", "fresh": false});
-       }
-
-       if(this.nsMap[uri] === undefined) {
-               // this prefix hasn't been defined yet in current context
-               var prefix = NAMESPACES[uri];
-
-               if (prefix === undefined) { // not predefined
-                       prefix = "ns" + this.nsCounter;
-                       this.nsCounter += 1;
-               }
-
-               this.nsMap[uri] = prefix;
-               return ({
-                       "prefix": prefix,
-                       "uri": uri,
-                       "fresh": true
-               });
-       }
-
-       return ({"prefix": this.nsMap[uri], "uri": uri, "fresh": false});
-};
-
-HTMLSerializer.prototype._join = function(prefix, name) {
-       if(!!prefix)
-               return prefix + ":" + name;
-       return name;
-};
-
-HTMLSerializer.prototype._rjoin = function(prefix, name) {
-       if(!!name)
-               return prefix + ":" + name;
-       return prefix;
-};
-
-HTMLSerializer.prototype._serializeElement = function(node) {
-    var self = this;
-
-    if (self._nodeIgnored(node)) {
-        self._pushTagEnd('');
-        self._pushChildren(node);
-    }
-    else {
-       var ns = node.getAttribute('x-ns');
-       var nsPrefix = null;
-       var newNamespaces = [];
-
-       var nsData = self._assignNamespace(node.getAttribute('x-ns'));
-
-       if(nsData.fresh) {
-               newNamespaces.push(nsData);
-               self.stack.push({
-                       "type": NS_END,
-                       "namespace": nsData
-               });
-       }
-
-       var tagName = self._join(nsData.prefix, node.getAttribute('x-node'));
-
-       /* retrieve attributes */
-       var attributeIDs = [];
-       for (var i = 0; i < node.attributes.length; i++) {
-               var attr = node.attributes.item(i);
-
-               // check if name starts with "x-attr-name"
-               var m = attr.name.match(XATTR_RE);
-               if (m !== null)
-                       attributeIDs.push(m[1]);
-       };
-
-       /* print out */
-
-       self.result += '<' + tagName;
-
-       $.each(attributeIDs, function() {
-               var nsData = self._assignNamespace(node.getAttribute('x-attr-ns-'+this));
-
-               if(nsData.fresh) {
-                       newNamespaces.push(nsData);
-                       self.stack.push({
-                               "type": NS_END,
-                               "namespace": nsData
-                       });
-               };
-
-               self.result += ' ' + self._join(nsData.prefix, node.getAttribute('x-attr-name-'+this));
-               self.result += '="'+node.getAttribute('x-attr-value-'+this) +'"';
-       });
-
-       /* print new namespace declarations */
-       $.each(newNamespaces, function() {
-               self.result += " " + self._rjoin("xmlns", this.prefix);
-               self.result += '="' + this.uri + '"';
-       });
-
-       if (node.childNodes.length > 0) {
-               self.result += ">";
-               self._pushTagEnd(tagName);
-               self._pushChildren(node);
-       }
-       else {
-               self.result += "/>";
-       };
-    }
-};
-
-function html2text(params) {
-       try {
-               var s = new HTMLSerializer();
-               params.success( s.serialize(params.element, params.stripOuter) );
-       } catch(e) {
-               params.error("Nie udało się zserializować tekstu:" + e)
-       }
-}
diff --git a/redakcja/static/js/wiki_img/base.js b/redakcja/static/js/wiki_img/base.js
deleted file mode 100644 (file)
index ffe5a01..0000000
+++ /dev/null
@@ -1,329 +0,0 @@
-(function($)
-{
-       var noop = function() { };
-
-       $.wiki = {
-               perspectives: {},
-               cls: {},
-               state: {
-                       "version": 1,
-                       "perspectives": {
-                "CodeMirrorPerspective": {}
-                       }
-               }
-       };
-
-       $.wiki.loadConfig = function() {
-               if(!window.localStorage)
-                       return;
-
-               try {
-                       var value = window.localStorage.getItem(CurrentDocument.id) || "{}";
-                       var config = JSON.parse(value);
-
-                       if (config.version == $.wiki.state.version) {
-                               $.wiki.state.perspectives = $.extend($.wiki.state.perspectives, config.perspectives);
-                       }
-               } catch(e) {
-                       console.log("Failed to load config, using default.");
-               }
-
-               console.log("Loaded:", $.wiki.state, $.wiki.state.version);
-       };
-
-       $(window).bind('unload', function() {
-               if(window.localStorage)
-                       window.localStorage.setItem(CurrentDocument.id, JSON.stringify($.wiki.state));
-       })
-
-
-       $.wiki.activePerspective = function() {
-               return this.perspectives[$("#tabs li.active").attr('id')];
-       };
-
-       $.wiki.exitContext = function() {
-               var ap = this.activePerspective();
-               if(ap) ap.onExit();
-               return ap;
-       };
-
-       $.wiki.enterContext = function(ap) {
-               if(ap) ap.onEnter();
-       };
-
-       $.wiki.isDirty = function() {
-               var ap = this.activePerspective();
-               return (!!CurrentDocument && CurrentDocument.has_local_changes) || ap.dirty();
-       };
-
-       $.wiki.newTab = function(doc, title, klass) {
-               var base_id = 'id' + Math.floor(Math.random()* 5000000000);
-               var id = (''+klass)+'_' + base_id;
-               var $tab = $('<li id="'+id+'" data-ui-related="'+base_id+'" data-ui-jsclass="'+klass+'" >'
-                               + title + '<img src="'+STATIC_URL+'icons/close.png" class="tabclose"></li>');
-               var $view = $('<div class="editor '+klass+'" id="'+base_id+'"> </div>');
-
-               this.perspectives[id] = new $.wiki[klass]({
-                       doc: doc,
-                       id: id,
-                       base_id: base_id,
-               });
-
-               $('#tabs').append($tab);
-               $view.hide().appendTo('#editor');
-               return {
-                       tab: $tab[0],
-                       view: $view[0],
-               };
-       };
-
-       $.wiki.initTab = function(options) {
-               var klass = $(options.tab).attr('data-ui-jsclass');
-
-               return new $.wiki[klass]({
-                       doc: options.doc,
-                       id: $(options.tab).attr('id'),
-                       callback: function() {
-                               $.wiki.perspectives[this.perspective_id] = this;
-                               if(options.callback)
-                                       options.callback.call(this);
-                       }
-               });
-       };
-
-       $.wiki.perspectiveForTab = function(tab) { // element or id
-               return this.perspectives[ $(tab).attr('id')];
-       }
-
-       $.wiki.switchToTab = function(tab){
-               var self = this;
-               var $tab = $(tab);
-
-               if($tab.length != 1)
-                       $tab = $(DEFAULT_PERSPECTIVE);
-
-               var $old = $tab.closest('.tabs').find('.active');
-
-               $old.each(function(){
-                       $(this).removeClass('active');
-                       self.perspectives[$(this).attr('id')].onExit();
-                       $('#' + $(this).attr('data-ui-related')).hide();
-               });
-
-               /* show new */
-               $tab.addClass('active');
-               $('#' + $tab.attr('data-ui-related')).show();
-
-               console.log($tab);
-               console.log($.wiki.perspectives);
-
-               $.wiki.perspectives[$tab.attr('id')].onEnter();
-       };
-
-       /*
-        * Basic perspective.
-        */
-       $.wiki.Perspective = function(options) {
-               if(!options) return;
-
-               this.doc = options.doc;
-               if (options.id) {
-                       this.perspective_id = options.id;
-               }
-               else {
-                       this.perspective_id = '';
-               }
-
-               if(options.callback)
-                       options.callback.call(this);
-       };
-
-       $.wiki.Perspective.prototype.config = function() {
-               return $.wiki.state.perspectives[this.perspective_id];
-       }
-
-       $.wiki.Perspective.prototype.toString = function() {
-               return this.perspective_id;
-       };
-
-       $.wiki.Perspective.prototype.dirty = function() {
-               return true;
-       };
-
-       $.wiki.Perspective.prototype.onEnter = function () {
-               // called when perspective in initialized
-               if (!this.noupdate_hash_onenter) {
-                       document.location.hash = '#' + this.perspective_id;
-               }
-       };
-
-       $.wiki.Perspective.prototype.onExit = function () {
-               // called when user switches to another perspective
-               if (!this.noupdate_hash_onenter) {
-                       document.location.hash = '';
-               }
-       };
-
-       $.wiki.Perspective.prototype.destroy = function() {
-               // pass
-       };
-
-       $.wiki.Perspective.prototype.freezeState = function () {
-               // free UI state (don't store data here)
-       };
-
-       $.wiki.Perspective.prototype.unfreezeState = function (frozenState) {
-               // restore UI state
-       };
-
-       /*
-        * Stub rendering (used in generating history)
-        */
-       $.wiki.renderStub = function(params)
-       {
-               params = $.extend({ 'filters': {} }, params);
-               var $elem = params.stub.clone();
-               $elem.removeClass('row-stub');
-               params.container.append($elem);
-
-               $('*[data-stub-value]', $elem).each(function() {
-                       var $this = $(this);
-                       var field = $this.attr('data-stub-value');
-
-                       var value = params.data[field];
-
-                       if(params.filters[field])
-                               value = params.filters[field](value);
-
-                       if(value === null || value === undefined) return;
-
-                       if(!$this.attr('data-stub-target')) {
-                               $this.text(value);
-                       }
-                       else {
-                               $this.attr($this.attr('data-stub-target'), value);
-                               $this.removeAttr('data-stub-target');
-                               $this.removeAttr('data-stub-value');
-                       }
-               });
-
-               $elem.show();
-               return $elem;
-       };
-
-       /*
-        * Dialogs
-        */
-       function GenericDialog(element) {
-               if(!element) return;
-
-               var self = this;
-
-               self.$elem = $(element);
-
-               if(!self.$elem.attr('data-ui-initialized')) {
-                       console.log("Initializing dialog", this);
-                       self.initialize();
-                       self.$elem.attr('data-ui-initialized', true);
-               }
-
-               self.show();
-       };
-
-       GenericDialog.prototype = {
-
-               /*
-               * Steps to follow when the dialog in first loaded on page.
-               */
-               initialize: function(){
-                       var self = this;
-
-                       /* bind buttons */
-                       $('button[data-ui-action]', self.$elem).click(function(event) {
-                               event.preventDefault();
-
-                               var action = $(this).attr('data-ui-action');
-                               console.log("Button pressed, action: ", action);
-
-                               try {
-                                       self[action + "Action"].call(self);
-                               } catch(e) {
-                                       console.log("Action failed:", e);
-                                       // always hide on cancel
-                                       if(action == 'cancel')
-                                               self.hide();
-                               }
-                       });
-               },
-
-               /*
-                * Prepare dialog for user. Clear any unnessary data.
-               */
-               show: function() {
-                       $.blockUI({
-                               message: this.$elem,
-                               css: {
-                                       'top': '25%',
-                                       'left': '25%',
-                                       'width': '50%'
-                               }
-                       });
-               },
-
-               hide: function(){
-                       $.unblockUI();
-               },
-
-               cancelAction: function() {
-                       this.hide();
-               },
-
-               doneAction: function() {
-                       this.hide();
-               },
-
-               clearForm: function() {
-                       $("*[data-ui-error-for]", this.$elem).text('');
-               },
-
-               reportErrors: function(errors) {
-                       var global = $("*[data-ui-error-for='__all__']", this.$elem);
-                       var unassigned = [];
-
-                       for (var field_name in errors)
-                       {
-                               var span = $("*[data-ui-error-for='"+field_name+"']", this.$elem);
-
-                               if(!span.length) {
-                                       unassigned.push(field_name);
-                                       continue;
-                               }
-
-                               span.text(errors[field_name].join(' '));
-                       }
-
-                       if(unassigned.length > 0)
-                               global.text( global.text() + 'W formularzu wystąpiły błędy');
-               }
-       };
-
-       $.wiki.cls.GenericDialog = GenericDialog;
-
-       $.wiki.showDialog = function(selector, options) {
-               var elem = $(selector);
-
-               if(elem.length != 1) {
-                       console.log("Failed to show dialog:", selector, elem);
-                       return false;
-               }
-
-               try {
-                   var klass = elem.attr('data-ui-jsclass');
-                       return new $.wiki.cls[klass](elem, options);
-               } catch(e) {
-                       console.log("Failed to show dialog", selector, klass, e);
-                       return false;
-               }
-       };
-
-})(jQuery);
diff --git a/redakcja/static/js/wiki_img/loader.js b/redakcja/static/js/wiki_img/loader.js
deleted file mode 100644 (file)
index c3fe03e..0000000
+++ /dev/null
@@ -1,137 +0,0 @@
-if (!window.console) {
-    window.console = {
-        log: function(){
-        }
-    }
-}
-
-DEFAULT_PERSPECTIVE = "#MotifsPerspective";
-
-$(function()
-{
-       var tabs = $('ol#tabs li');
-       var gallery = null;
-       CurrentDocument = new $.wikiapi.WikiDocument("document-meta");
-
-       $.blockUI.defaults.baseZ = 10000;
-
-    function initialize()
-       {
-               $(document).keydown(function(event) {
-                       console.log("Received key:", event);
-               });
-
-               /* The save button */
-        $('#save-button').click(function(event){
-            event.preventDefault();
-                       $.wiki.showDialog('#save_dialog');
-        });
-
-               $('.editor').hide();
-
-               /*
-                * TABS
-                */
-        $('.tabs li').live('click', function(event, callback) {
-            event.preventDefault();
-                       $.wiki.switchToTab(this);
-        });
-
-               $('#tabs li > .tabclose').live('click', function(event, callback) {
-                       var $tab = $(this).parent();
-
-                       if($tab.is('.active'))
-                               $.wiki.switchToTab(DEFAULT_PERSPECTIVE);
-
-                       var p = $.wiki.perspectiveForTab($tab);
-                       p.destroy();
-
-                       return false;
-        });
-
-
-        /*$(window).resize(function(){
-            $('iframe').height($(window).height() - $('#tabs').outerHeight() - $('#source-editor .toolbar').outerHeight());
-        });
-
-        $(window).resize();*/
-
-        /*$('.vsplitbar').toggle(
-                       function() {
-                               $.wiki.state.perspectives.ScanGalleryPerspective.show = true;
-                               $('#sidebar').show();
-                               $('.vsplitbar').css('right', 480).addClass('active');
-                               $('#editor .editor').css('right', 510);
-                               $(window).resize();
-                               $.wiki.perspectiveForTab('#tabs-right .active').onEnter();
-                       },
-                       function() {
-                           var active_right = $.wiki.perspectiveForTab('#tabs-right .active');
-                               $.wiki.state.perspectives.ScanGalleryPerspective.show = false;
-                               $('#sidebar').hide();
-                               $('.vsplitbar').css('right', 0).removeClass('active');
-                               $(".vsplitbar-title").html("&uarr;&nbsp;" + active_right.vsplitbar + "&nbsp;&uarr;");
-                               $('#editor .editor').css('right', 30);
-                               $(window).resize();
-                               active_right.onExit();
-                       }
-               );*/
-
-        window.onbeforeunload = function(e) {
-            if($.wiki.isDirty()) {
-                               e.returnValue = "Na stronie mogą być nie zapisane zmiany.";
-                               return "Na stronie mogą być nie zapisane zmiany.";
-                       };
-        };
-
-               console.log("Fetching document's text");
-
-               $(document).bind('wlapi_document_changed', function(event, doc) {
-                       try {
-                               $('#document-revision').text(doc.revision);
-                       } catch(e) {
-                               console.log("Failed handler", e);
-                       }
-               });
-
-               CurrentDocument.fetch({
-                       success: function(){
-                               console.log("Fetch success");
-                               $('#loading-overlay').fadeOut();
-                               var active_tab = document.location.hash || DEFAULT_PERSPECTIVE;
-
-                               console.log("Initial tab is:", active_tab)
-                               $.wiki.switchToTab(active_tab);
-                       },
-                       failure: function() {
-                               $('#loading-overlay').fadeOut();
-                               alert("FAILURE");
-                       }
-               });
-    }; /* end of initialize() */
-
-
-       /* Load configuration */
-       $.wiki.loadConfig();
-
-       var initAll = function(a, f) {
-               if (a.length == 0) return f();
-
-               $.wiki.initTab({
-                       tab: a.pop(),
-                       doc: CurrentDocument,
-                       callback: function(){
-                               initAll(a, f);
-                       }
-               });
-       };
-
-
-       /*
-        * Initialize all perspectives
-        */
-       initAll( $.makeArray($('.tabs li')), initialize);
-       console.log(location.hash);
-});
-
-
diff --git a/redakcja/static/js/wiki_img/loader_readonly.js b/redakcja/static/js/wiki_img/loader_readonly.js
deleted file mode 100755 (executable)
index 99e5ad0..0000000
+++ /dev/null
@@ -1,93 +0,0 @@
-if (!window.console) {
-    window.console = {
-        log: function(){
-        }
-    }
-}
-
-
-DEFAULT_PERSPECTIVE = "#MotifsPerspective";
-
-
-$(function()
-{
-       var tabs = $('ol#tabs li');
-       var gallery = null;
-
-       CurrentDocument = new $.wikiapi.WikiDocument("document-meta");
-       $.blockUI.defaults.baseZ = 10000;
-
-       function initialize()
-       {
-               $('.editor').hide();
-
-               /*
-                * TABS
-                */
-        $('#tabs li').live('click', function(event, callback) {
-            event.preventDefault();
-                       $.wiki.switchToTab(this);
-        });
-
-               $('#tabs li > .tabclose').live('click', function(event, callback) {
-                       var $tab = $(this).parent();
-
-                       if($tab.is('.active'))
-                               $.wiki.switchToTab(DEFAULT_PERSPECTIVE);
-
-                       var p = $.wiki.perspectiveForTab($tab);
-                       p.destroy();
-                       return false;
-        });
-/*
-        $(window).resize(function(){
-            $('iframe').height($(window).height() - $('#tabs').outerHeight() - $('#source-editor .toolbar').outerHeight());
-        });
-        */
-
-               $(document).bind('wlapi_document_changed', function(event, doc) {
-                       try {
-                               $('#document-revision').text(doc.revision);
-                       } catch(e) {
-                               console.log("Failed handler", e);
-                       }
-               });
-
-               CurrentDocument.fetch({
-                       success: function(){
-                               console.log("Fetch success");
-                               $('#loading-overlay').fadeOut();
-                               var active_tab = document.location.hash || DEFAULT_PERSPECTIVE;
-
-                               console.log("Initial tab is:", active_tab)
-                               $.wiki.switchToTab(active_tab);
-                       },
-                       failure: function() {
-                               $('#loading-overlay').fadeOut();
-                               alert("FAILURE");
-                       }
-               });
-    }; /* end of initialize() */
-
-       /* Load configuration */
-       $.wiki.loadConfig();
-
-       var initAll = function(a, f) {
-               if (a.length == 0) return f();
-
-               $.wiki.initTab({
-                       tab: a.pop(),
-                       doc: CurrentDocument,
-                       callback: function(){
-                               initAll(a, f);
-                       }
-               });
-       };
-
-
-       /*
-        * Initialize all perspectives
-        */
-       initAll( $.makeArray($('ol#tabs li')), initialize);
-       console.log(location.hash);
-});
diff --git a/redakcja/static/js/wiki_img/view_editor_motifs.js b/redakcja/static/js/wiki_img/view_editor_motifs.js
deleted file mode 100644 (file)
index ad60c22..0000000
+++ /dev/null
@@ -1,158 +0,0 @@
-(function($){
-
-    function MotifsPerspective(options){
-
-        var old_callback = options.callback;
-
-        options.callback = function(){
-            var self = this;
-
-            self.$tag_name = $('#motifs-editor .tag-name');
-            self.$toolbar = $('#motifs-editor .toolbar');
-            self.$scrolled = $('#motifs-editor .scrolled');
-            withThemes(function(canonThemes){
-                self.$tag_name.autocomplete(canonThemes, {
-                    autoFill: true,
-                    multiple: true,
-                    selectFirst: true,
-                    highlight: false
-                });
-            })
-
-            self.$objects_list = $('#motifs-editor .objects-list');
-
-            self.x1 = null;
-            self.x2 = null;
-            self.y1 = null;
-            self.y2 = null;
-
-            if (!CurrentDocument.readonly) {
-                self.ias = $('#motifs-editor img.area-selectable').imgAreaSelect({ handles: true, onSelectEnd: self._fillCoords(self), instance: true });
-                $('#motifs-editor .add').click(self._addObject(self));
-
-                $('.delete', self.$objects_list).live('click', function() {
-                    $(this).prev().trigger('click');
-                    if (window.confirm("Czy na pewno chcesz usunąć ten motyw?")) {
-                        $(this).prev().remove();
-                        $(this).remove();
-                        self._refreshLayout();
-                    }
-                    self._resetSelection();
-                    return false;
-                });
-            }
-
-            $('.image-object', self.$objects_list).live('click', function(){
-                $('.active', self.$objects_list).removeClass('active');
-                $(this).addClass('active');
-                var coords = $(this).data('coords');
-                if (coords) {
-                    self.ias.setSelection.apply(self.ias, coords);
-                    self.ias.setOptions({ show: true });
-                }
-                else {
-                    self._resetSelection();
-                }
-            });
-
-            old_callback.call(this);
-        };
-
-        $.wiki.Perspective.call(this, options);
-    };
-
-    MotifsPerspective.prototype = new $.wiki.Perspective();
-
-    MotifsPerspective.prototype.freezeState = function(){
-
-    };
-
-    MotifsPerspective.prototype._resetSelection = function() {
-        var self = this;
-        self.x1 = self.x2 = self.y1 = self.y2 = null;
-        self.ias.setOptions({ hide: true });
-    }
-
-    MotifsPerspective.prototype._refreshLayout = function() {
-        this.$scrolled.css({top: this.$toolbar.height()});
-    };
-
-    MotifsPerspective.prototype._push = function(name, x1, y1, x2, y2) {
-        var $e = $('<span class="image-object"></span>')
-        $e.text(name);
-        if (x1 !== null)
-            $e.data('coords', [x1, y1, x2, y2]);
-        this.$objects_list.append($e);
-        this.$objects_list.append('<span class="delete">(x) </span>');
-        this._refreshLayout();
-    }
-
-
-    MotifsPerspective.prototype._addObject = function(self) {
-        return function() {
-            outputs = [];
-            chunks = self.$tag_name.val().split(',');
-            for (i in chunks) {
-                item = chunks[i].trim();
-                if (item == '')
-                    continue;
-                outputs.push(item.trim());
-            }
-            output = outputs.join(', ');
-
-            self._push(output, self.x1, self.y1, self.x2, self.y2);
-            self._resetSelection();
-        }
-    }
-
-    MotifsPerspective.prototype._fillCoords = function(self) {
-        return function(img, selection) {
-            $('.active', self.$objects_list).removeClass('active');
-            if (selection.x1 != selection.x2 && selection.y1 != selection.y2) {
-                self.x1 = selection.x1;
-                self.x2 = selection.x2;
-                self.y1 = selection.y1;
-                self.y2 = selection.y2;
-            }
-            else {
-                self.x1 = self.x2 = self.y1 = self.y2 = null;
-            }
-        }
-    }
-
-    MotifsPerspective.prototype.onEnter = function(success, failure){
-        var self = this;
-        this.$objects_list.children().remove();
-
-        $.each(this.doc.getImageItems('theme'), function(i, e) {
-            self._push.apply(self, e);
-        });
-
-        if (this.x1 !== null)
-            this.ias.setOptions({enable: true, show: true});
-        else
-            this.ias.setOptions({enable: true});
-
-        $.wiki.Perspective.prototype.onEnter.call(this);
-
-    };
-
-    MotifsPerspective.prototype.onExit = function(success, failure){
-        var self = this;
-        var motifs = [];
-        this.$objects_list.children(".image-object").each(function(i, e) {
-            var args = $(e).data('coords');
-            if (!args)
-                args = [null, null, null, null];
-            args.unshift($(e).text());
-            motifs.push(args);
-        })
-        self.doc.setImageItems('theme', motifs);
-
-        this.ias.setOptions({disable: true, hide: true});
-
-    };
-
-    $.wiki.MotifsPerspective = MotifsPerspective;
-
-})(jQuery);
diff --git a/redakcja/static/js/wiki_img/view_editor_objects.js b/redakcja/static/js/wiki_img/view_editor_objects.js
deleted file mode 100644 (file)
index b0cf2a9..0000000
+++ /dev/null
@@ -1,149 +0,0 @@
-(function($){
-
-    function ObjectsPerspective(options){
-
-        var old_callback = options.callback;
-
-        options.callback = function(){
-            var self = this;
-
-            self.$tag_name = $('#objects-editor .tag-name');
-            self.$toolbar = $('#objects-editor .toolbar');
-            self.$scrolled = $('#objects-editor .scrolled');
-            self.$objects_list = $('#objects-editor .objects-list');
-
-            self.x1 = null;
-            self.x2 = null;
-            self.y1 = null;
-            self.y2 = null;
-
-            if (!CurrentDocument.readonly) {
-                self.ias = $('#objects-editor img.area-selectable').imgAreaSelect({ handles: true, onSelectEnd: self._fillCoords(self), instance: true });
-                $('#objects-editor .add').click(self._addObject(self));
-
-                $('.delete', self.$objects_list).live('click', function() {
-                    $(this).prev().trigger('click');
-                    if (window.confirm("Czy na pewno chcesz usunąć ten obiekt?")) {
-                        $(this).prev().remove();
-                        $(this).remove();
-                        self._refreshLayout();
-                    }
-                    self._resetSelection();
-                    return false;
-                });
-            }
-
-            $('.image-object', self.$objects_list).live('click', function(){
-                $('.active', self.$objects_list).removeClass('active');
-                $(this).addClass('active');
-                var coords = $(this).data('coords');
-                if (coords) {
-                    self.ias.setSelection.apply(self.ias, coords);
-                    self.ias.setOptions({ show: true });
-                }
-                else {
-                    self._resetSelection();
-                }
-            });
-
-            old_callback.call(this);
-        };
-
-        $.wiki.Perspective.call(this, options);
-    };
-
-    ObjectsPerspective.prototype = new $.wiki.Perspective();
-
-    ObjectsPerspective.prototype.freezeState = function(){
-
-    };
-
-    ObjectsPerspective.prototype._resetSelection = function() {
-        var self = this;
-        self.x1 = self.x2 = self.y1 = self.y2 = null;
-        self.ias.setOptions({ hide: true });
-    }
-
-    ObjectsPerspective.prototype._refreshLayout = function() {
-        this.$scrolled.css({top: this.$toolbar.height()});
-    };
-
-    ObjectsPerspective.prototype._push = function(name, x1, y1, x2, y2) {
-        var $e = $('<span class="image-object"></span>')
-        $e.text(name);
-        if (x1 !== null)
-            $e.data('coords', [x1, y1, x2, y2]);
-        this.$objects_list.append($e);
-        this.$objects_list.append('<span class="delete">(x) </span>');
-        this._refreshLayout();
-    }
-
-
-    ObjectsPerspective.prototype._addObject = function(self) {
-        return function() {
-            outputs = [];
-            chunks = self.$tag_name.val().split(',');
-            for (i in chunks) {
-                item = chunks[i].trim();
-                if (item == '')
-                    continue;
-                outputs.push(item.trim());
-            }
-            output = outputs.join(', ');
-
-            self._push(output, self.x1, self.y1, self.x2, self.y2);
-            self._resetSelection();
-        }
-    }
-
-    ObjectsPerspective.prototype._fillCoords = function(self) {
-        return function(img, selection) {
-            $('.active', self.$objects_list).removeClass('active');
-            if (selection.x1 != selection.x2 && selection.y1 != selection.y2) {
-                self.x1 = selection.x1;
-                self.x2 = selection.x2;
-                self.y1 = selection.y1;
-                self.y2 = selection.y2;
-            }
-            else {
-                self.x1 = self.x2 = self.y1 = self.y2 = null;
-            }
-        }
-    }
-
-    ObjectsPerspective.prototype.onEnter = function(success, failure){
-        var self = this;
-        this.$objects_list.children().remove();
-
-        $.each(this.doc.getImageItems('object'), function(i, e) {
-            self._push.apply(self, e);
-        });
-
-        if (this.x1 !== null)
-            this.ias.setOptions({enable: true, show: true});
-        else
-            this.ias.setOptions({enable: true});
-
-        $.wiki.Perspective.prototype.onEnter.call(this);
-
-    };
-
-    ObjectsPerspective.prototype.onExit = function(success, failure){
-        var self = this;
-        var objects = [];
-        this.$objects_list.children(".image-object").each(function(i, e) {
-            var args = $(e).data('coords');
-            if (!args)
-                args = [null, null, null, null];
-            args.unshift($(e).text());
-            objects.push(args);
-        })
-        self.doc.setImageItems('object', objects);
-
-        this.ias.setOptions({disable: true, hide: true});
-
-    };
-
-    $.wiki.ObjectsPerspective = ObjectsPerspective;
-
-})(jQuery);
diff --git a/redakcja/static/js/wiki_img/wikiapi.js b/redakcja/static/js/wiki_img/wikiapi.js
deleted file mode 100644 (file)
index 377e4f9..0000000
+++ /dev/null
@@ -1,418 +0,0 @@
-(function($) {
-       $.wikiapi = {};
-       var noop = function() {
-       };
-       var noops = {
-               success: noop,
-               failure: noop
-       };
-       /*
-        * Return absolute reverse path of given named view. (at least he have it
-        * hard-coded in one place)
-        *
-        * TODO: think of a way, not to hard-code it here ;)
-        *
-        */
-       function reverse() {
-               var vname = arguments[0];
-               var base_path = "/images";
-
-               if (vname == "ajax_document_text")
-                   return base_path + "/text/" + arguments[1] + "/";
-
-
-        if (vname == "ajax_document_revert") {
-            return base_path + "/revert/" + arguments[1] + '/';
-        }
-
-               if (vname == "ajax_document_history") {
-                       return base_path + "/history/" + arguments[1] + '/';
-               }
-
-               if (vname == "ajax_document_diff")
-                       return base_path + "/diff/" + arguments[1] + '/';
-
-               if (vname == "ajax_document_pubmark")
-                       return base_path + "/pubmark/" + arguments[1] + '/';
-
-               console.log("Couldn't reverse match:", vname);
-               return "/404.html";
-       };
-
-       /*
-        * Document Abstraction
-        */
-       function WikiDocument(element_id) {
-               var meta = $('#' + element_id);
-               this.id = meta.attr('data-object-id');
-
-               this.revision = $("*[data-key='revision']", meta).text();
-               this.readonly = !!$("*[data-key='readonly']", meta).text();
-
-        var diff = $("*[data-key='diff']", meta).text();
-        if (diff) {
-            diff = diff.split(',');
-            if (diff.length == 2 && diff[0] < diff[1])
-                this.diff = diff;
-            else if (diff.length == 1) {
-                diff = parseInt(diff);
-                if (diff != NaN)
-                    this.diff = [diff - 1, diff];
-            }
-        }
-
-               this.text = null;
-               this.has_local_changes = false;
-               this._lock = -1;
-               this._context_lock = -1;
-               this._lock_count = 0;
-       };
-
-       WikiDocument.prototype.triggerDocumentChanged = function() {
-               $(document).trigger('wlapi_document_changed', this);
-       };
-       /*
-        * Fetch text of this document.
-        */
-       WikiDocument.prototype.fetch = function(params) {
-               params = $.extend({}, noops, params);
-               var self = this;
-               $.ajax({
-                       method: "GET",
-                       url: reverse("ajax_document_text", self.id),
-                       data: {"revision": self.revision},
-                       dataType: 'json',
-                       success: function(data) {
-                               var changed = false;
-
-                               if (self.text === null || self.revision !== data.revision) {
-                                       self.text = data.text;
-                                       if (self.text === '') {
-                                           self.text = '<picture></picture>';
-                                       }
-                                       self.revision = data.revision;
-//                    self.commit = data.commit;
-                                       changed = true;
-                                       self.triggerDocumentChanged();
-                               };
-
-                               self.has_local_changes = false;
-                               params['success'](self, changed);
-                       },
-                       error: function() {
-                               params['failure'](self, "Nie udało się wczytać treści dokumentu.");
-                       }
-               });
-       };
-       /*
-        * Fetch history of this document.
-        *
-        * from - First revision to fetch (default = 0) upto - Last revision to
-        * fetch (default = tip)
-        *
-        */
-       WikiDocument.prototype.fetchHistory = function(params) {
-               /* this doesn't modify anything, so no locks */
-               params = $.extend({}, noops, params);
-               var self = this;
-               $.ajax({
-                       method: "GET",
-                       url: reverse("ajax_document_history", self.id),
-                       dataType: 'json',
-                       data: {
-                               "from": params['from'],
-                               "upto": params['upto']
-                       },
-                       success: function(data) {
-                               params['success'](self, data);
-                       },
-                       error: function() {
-                               params['failure'](self, "Nie udało się wczytać historii dokumentu.");
-                       }
-               });
-       };
-       WikiDocument.prototype.fetchDiff = function(params) {
-               /* this doesn't modify anything, so no locks */
-               var self = this;
-               params = $.extend({
-                       'from': self.revision,
-                       'to': self.revision
-               }, noops, params);
-               $.ajax({
-                       method: "GET",
-                       url: reverse("ajax_document_diff", self.id),
-                       dataType: 'html',
-                       data: {
-                               "from": params['from'],
-                               "to": params['to']
-                       },
-                       success: function(data) {
-                               params['success'](self, data);
-                       },
-                       error: function() {
-                               params['failure'](self, "Nie udało się wczytać porównania wersji.");
-                       }
-               });
-       };
-
-       /*
-        * Set document's text
-        */
-       WikiDocument.prototype.setText = function(text) {
-               this.text = text;
-               this.has_local_changes = true;
-       };
-
-       /*
-        * Save text back to the server
-        */
-       WikiDocument.prototype.save = function(params) {
-               params = $.extend({}, noops, params);
-               var self = this;
-
-               if (!self.has_local_changes) {
-                       console.log("Abort: no changes.");
-                       return params['success'](self, false, "Nie ma zmian do zapisania.");
-               };
-
-               // Serialize form to dictionary
-               var data = {};
-               $.each(params['form'].serializeArray(), function() {
-                       data[this.name] = this.value;
-               });
-
-               data['textsave-text'] = self.text;
-
-               $.ajax({
-                       url: reverse("ajax_document_text", self.id),
-                       type: "POST",
-                       dataType: "json",
-                       data: data,
-                       success: function(data) {
-                               var changed = false;
-
-                $('#header').removeClass('saving');
-
-                               if (data.text) {
-                                       self.text = data.text;
-                                       self.revision = data.revision;
-//                    self.commit = data.commit;
-                                       changed = true;
-                                       self.triggerDocumentChanged();
-                               };
-
-                               params['success'](self, changed, ((changed && "Udało się zapisać :)") || "Twoja wersja i serwera jest identyczna"));
-                       },
-                       error: function(xhr) {
-                if ($('#header').hasClass('saving')) {
-                    $('#header').removeClass('saving');
-                    $.blockUI({
-                        message: "<p>Nie udało się zapisać zmian. <br/><button onclick='$.unblockUI()'>OK</button></p>"
-                    })
-                }
-                else {
-                    try {
-                        params['failure'](self, $.parseJSON(xhr.responseText));
-                    }
-                    catch (e) {
-                        params['failure'](self, {
-                            "__message": "<p>Nie udało się zapisać - błąd serwera.</p>"
-                        });
-                    };
-                }
-
-                       }
-               });
-
-        $('#save-hide').click(function(){
-            $('#header').addClass('saving');
-            $.unblockUI();
-            $.wiki.blocking.unblock();
-        });
-       }; /* end of save() */
-
-    WikiDocument.prototype.revertToVersion = function(params) {
-        var self = this;
-        params = $.extend({}, noops, params);
-
-        if (params.revision >= this.revision) {
-            params.failure(self, 'Proszę wybrać rewizję starszą niż aktualna.');
-            return;
-        }
-
-        // Serialize form to dictionary
-        var data = {};
-        $.each(params['form'].serializeArray(), function() {
-            data[this.name] = this.value;
-        });
-
-        $.ajax({
-            url: reverse("ajax_document_revert", self.id),
-            type: "POST",
-            dataType: "json",
-            data: data,
-            success: function(data) {
-                if (data.text) {
-                    self.text = data.text;
-                    self.revision = data.revision;
-                    self.gallery = data.gallery;
-                    self.triggerDocumentChanged();
-
-                    params.success(self, "Udało się przywrócić wersję :)");
-                }
-                else {
-                    params.failure(self, "Przywracana wersja identyczna z aktualną. Anulowano przywracanie.");
-                }
-            },
-            error: function(xhr) {
-                params.failure(self, "Nie udało się przywrócić wersji - błąd serwera.");
-            }
-        });
-    };
-
-       WikiDocument.prototype.pubmark = function(params) {
-               params = $.extend({}, noops, params);
-               var self = this;
-               var data = {
-                       "pubmark-id": self.id,
-               };
-
-               /* unpack form */
-               $.each(params.form.serializeArray(), function() {
-                       data[this.name] = this.value;
-               });
-
-               $.ajax({
-                       url: reverse("ajax_document_pubmark", self.id),
-                       type: "POST",
-                       dataType: "json",
-                       data: data,
-                       success: function(data) {
-                               params.success(self, data.message);
-                       },
-                       error: function(xhr) {
-                               if (xhr.status == 403 || xhr.status == 401) {
-                                       params.failure(self, {
-                                               "__all__": ["Nie masz uprawnień lub nie jesteś zalogowany."]
-                                       });
-                               }
-                               else {
-                                       try {
-                                               params.failure(self, $.parseJSON(xhr.responseText));
-                                       }
-                                       catch (e) {
-                                               params.failure(self, {
-                                                       "__all__": ["Nie udało się - błąd serwera."]
-                                               });
-                                       };
-                               };
-                       }
-               });
-       };
-
-
-
-    WikiDocument.prototype.getImageItems = function(tag) {
-        var self = this;
-
-        var parser = new DOMParser();
-        var doc = parser.parseFromString(self.text, 'text/xml');
-        var error = $('parsererror', doc);
-
-        if (error.length != 0) {
-            return null;
-        }
-
-        var a = [];
-        $('sem[type="'+tag+'"]', doc).each(function(i, e) {
-            var $e = $(e);
-            var $div = $e.children().first()
-            var value = $e.attr(tag);
-            $e.find('div').each(function(i, div) {
-                var $div = $(div);
-                switch ($div.attr('type')) {
-                    case 'rect':
-                        a.push([
-                            value,
-                            $div.attr('x1'),
-                            $div.attr('y1'),
-                            $div.attr('x2'),
-                            $div.attr('y2')
-                        ]);
-                        break;
-                    case 'whole':
-                        a.push([
-                            value,
-                            null, null, null, null
-                        ]);
-                        break
-                }
-            });
-        });
-
-        return a;
-    }
-
-    WikiDocument.prototype.setImageItems = function(tag, items) {
-        var self = this;
-
-        var parser = new DOMParser();
-        var doc = parser.parseFromString(self.text, 'text/xml');
-        var serializer = new XMLSerializer();
-        var error = $('parsererror', doc);
-
-        if (error.length != 0) {
-            return null;
-        }
-
-        $('sem[type="'+tag+'"]', doc).remove();
-        $root = $(doc.firstChild);
-        $.each(items, function(i, e) {
-            var $sem = $(doc.createElement("sem"));
-            $sem.attr('type', tag);
-            $sem.attr(tag, e[0]);
-            $div = $(doc.createElement("div"));
-            if (e[1]) {
-                $div.attr('type', 'rect');
-                $div.attr('x1', e[1]);
-                $div.attr('y1', e[2]);
-                $div.attr('x2', e[3]);
-                $div.attr('y2', e[4]);
-            }
-            else {
-                $div.attr('type', 'whole');
-            }
-            $sem.append($div);
-            $root.append($sem);
-        });
-        self.setText(serializer.serializeToString(doc));
-    }
-
-
-       $.wikiapi.WikiDocument = WikiDocument;
-})(jQuery);
-
-
-
-// Wykonuje block z załadowanymi kanonicznymi motywami
-function withThemes(code_block, onError)
-{
-    if (typeof withThemes.canon == 'undefined') {
-        $.ajax({
-            url: '/editor/themes',
-            dataType: 'text',
-            success: function(data) {
-                withThemes.canon = data.split('\n');
-                code_block(withThemes.canon);
-            },
-            error: function() {
-                withThemes.canon = null;
-                code_block(withThemes.canon);
-            }
-        })
-    }
-    else {
-        code_block(withThemes.canon);
-    }
-}
-
diff --git a/redakcja/static/xsl/wl2html_client.xsl b/redakcja/static/xsl/wl2html_client.xsl
deleted file mode 100644 (file)
index 784acb7..0000000
+++ /dev/null
@@ -1,867 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<xsl:stylesheet version="1.0"    
-    xmlns="http://www.w3.org/1999/xhtml"    
-    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
-
-    <!--
-        Dokument ten opisuje jednoznaczne przekształcenie WLML 0.1 -> XHTML.
-    -->        
-    <xsl:output method="html" omit-xml-declaration="yes" encoding="utf-8" indent="no" />
-
-    <!--
-        Base tag for rendering a fragment of text
-    -->
-    <xsl:template match="chunk">        
-        <xsl:apply-templates select="child::node()">            
-            <xsl:with-param name="mixed" select="true()" />
-        </xsl:apply-templates>        
-    </xsl:template>
-    
-    <!--
-        Base tag for rendering the whole text 
-    -->
-
-    <xsl:template match="utwor">
-        <div>
-            <xsl:call-template name="standard-attributes" />
-            <xsl:apply-templates select="child::node()">
-                <xsl:with-param name="mixed" select="false()" />
-            </xsl:apply-templates>
-        </div>
-    </xsl:template>    
-    
-    <!-- 
-        Przekształcenia poszczególnych elementów zgodnie z:            
-        http://wiki.wolnepodreczniki.pl/Lektury:Sk%C5%82ad/Tagi_sk%C5%82adu
-    -->
-
-    <!-- TAGI MASTERÓW STYLÓW
-    
-        Tagi rozpoczynające i kończące tekst utworu lirycznego o standardowej szerokości łamu:
-    -->
-
-    <xsl:template match="opowiadanie|powiesc">
-        <xsl:param name="mixed" />
-        <div>
-            <xsl:call-template name="standard-attributes" />
-            <xsl:apply-templates select="child::node()">
-                <xsl:with-param name="mixed" select="false()" />
-            </xsl:apply-templates>
-        </div>
-    </xsl:template>
-
-    <xsl:template match="liryka_l|liryka_lp">
-        <xsl:param name="mixed" />
-        <div>
-            <xsl:call-template name="standard-attributes" />
-            <xsl:apply-templates select="child::node()">
-                <xsl:with-param name="mixed" select="false()" />
-            </xsl:apply-templates>
-        </div>
-    </xsl:template>
-
-    <xsl:template match="dramat_wierszowany_l|dramat_wierszowany_lp|dramat_wspolczesny">
-        <xsl:param name="mixed" />
-        <div>
-            <xsl:call-template name="standard-attributes" />
-            <xsl:apply-templates select="child::node()">
-                <xsl:with-param name="mixed" select="false()" />
-            </xsl:apply-templates>
-        </div>
-    </xsl:template>
-
-    <xsl:template match="wywiad">
-        <xsl:param name="mixed" />
-        <div>
-            <xsl:call-template name="standard-attributes" />
-            <xsl:apply-templates select="child::node()">
-                <xsl:with-param name="mixed" select="false()" />
-            </xsl:apply-templates>
-        </div>
-    </xsl:template>
-
-    <!-- 
-        *****************************
-        ELEMENTY POZA TEKSTEM GŁÓWNYM
-        *****************************
-    -->
-
-    <!--
-        Autor składanego utworu
-
-        Element strony tytułowej - lub odpowiadającej jej przestrzeni,
-        np. na stronie internetowej) :
-
-        <autor_utworu> imiona-itd.-autora-składanego-utworu </autor_utworu>
-    -->
-    <xsl:template match="autor_utworu">
-        <xsl:param name="mixed" />
-        <h2 x-editable="true">
-            <xsl:call-template name="standard-attributes" />
-            <xsl:call-template name="context-menu" />
-            <xsl:apply-templates select="child::node()">
-                <xsl:with-param name="mixed" select="true()" />
-            </xsl:apply-templates>
-        </h2>
-    </xsl:template>
-
-    <!-- 
-        Nazwa składanego utworu
-
-        Element strony tytułowej - lub odpowiadającej jej przestrzeni,
-        np. na stronie internetowej
-
-        <nazwa_utworu> tytuł-składanego-utworu </nazwa_utworu>
-    -->
-    <xsl:template match="nazwa_utworu">
-        <xsl:param name="mixed" />
-        <h1 x-editable="true">
-            <xsl:call-template name="standard-attributes" />
-            <xsl:call-template name="context-menu" />
-            <xsl:apply-templates select="child::node()">
-                <xsl:with-param name="mixed" select="true()" />
-            </xsl:apply-templates>
-        </h1>
-    </xsl:template>
-
-    <!--
-        Nazwa utworu nadrzędnego, w którego skład wchodzi dany utwór
-        <dzielo_nadrzedne> tytuł-dzieła-nadrzędnego </dzielo_nadrzedne>
-
-         Przykład:
-
-         <utwor>
-         <liryka_l>
-            <autor_utworu>Bruno Jasieński</autor_utworu>
-            <dzielo_nadrzedne>But w butonierce</dzielo_nadrzedne>
-            <nazwa_utworu>Deszcz</nazwa_utworu>
-            ....
-    -->
-    <xsl:template match="dzielo_nadrzedne">
-        <xsl:param name="mixed" />
-        <h2 x-editable="true">
-            <xsl:call-template name="standard-attributes" />
-            <xsl:call-template name="context-menu" />
-            <xsl:apply-templates select="child::node()">
-                <xsl:with-param name="mixed" select="true()" />
-            </xsl:apply-templates>
-        </h2>
-    </xsl:template>
-
-    <!--
-        Podtytuł, czyli wszystkie dopiski do tytułu
-
-        Element strony tytułowej - lub odpowiadającej jej przestrzeni.
-        <podtytul> podtytuł-składanego-utworu </podtytul>
-
-        Przykład:
-        <utwor>
-            <powiesc>
-            <autor_utworu>Daniel Defoe</autor_utworu>
-            <nazwa_utworu>Robinson Crusoe</nazwa_utworu>
-            <podtytul>Jego życia losy, doświadczenia i przypadki</podtytul>
-            ...
-    -->
-    <xsl:template match="podtytul">
-        <xsl:param name="mixed" />
-        <h3 x-editable="true">
-            <xsl:call-template name="standard-attributes" />
-            <xsl:call-template name="context-menu" />
-            <xsl:apply-templates select="child::node()">
-                <xsl:with-param name="mixed" select="true()" />
-            </xsl:apply-templates>
-        </h3>
-    </xsl:template>
-
-    <!--
-       Tagi obejmujące tekst noty poprzedzającej tekst główny (styl wieloakapitowy):
-
-        <nota><akap> tekst-noty </akap></nota> (styl wieloakapitowy)
-    -->
-
-    <xsl:template match="nota">
-        <xsl:param name="mixed" />
-        <div>
-            <xsl:call-template name="standard-attributes" />
-            <xsl:apply-templates select="child::node()">
-                <xsl:with-param name="mixed" select="false()" />
-            </xsl:apply-templates>
-        </div>
-    </xsl:template>
-
-    <xsl:template match="nota_red">
-        <xsl:param name="mixed" />
-        <div>
-            <xsl:call-template name="standard-attributes" />
-            <xsl:apply-templates select="child::node()">
-                <xsl:with-param name="mixed" select="false()" />
-            </xsl:apply-templates>
-        </div>
-    </xsl:template>
-
-    <!--
-        Tagi obejmujące tekst dedykacji (styl wieloakapitowy bądź wielostrofowy):
-        <dedykacja> tekst-dedykacji </dedykacja>
-    -->
-    <xsl:template match="dedykacja">
-        <xsl:param name="mixed" />
-        <div>
-            <xsl:call-template name="standard-attributes" />
-            <xsl:apply-templates select="child::node()">
-                <xsl:with-param name="mixed" select="false()" />
-            </xsl:apply-templates>
-        </div>
-    </xsl:template>
-
-    <!--
-        Tagi obejmujące tekst w ramce (styl wieloakapitowy bądź wielostrofowy):
-        <ramka> tekst-w-ramce </ramka>
-    -->
-    <xsl:template match="ramka">
-        <xsl:param name="mixed" />
-        <div>
-            <xsl:call-template name="standard-attributes" />
-            <xsl:apply-templates select="child::node()">
-                <xsl:with-param name="mixed" select="false()" />
-            </xsl:apply-templates>
-        </div>
-    </xsl:template>
-
-    <!--
-        Tagi obejmujące tekst motta (styl wieloakapitowy bądź wielostrofowy):
-        <motto> tekst-motta </motto>
-    -->
-    <xsl:template match="motto">
-        <xsl:param name="mixed" />
-        <div class="motto_container" data-pass-thru="true">
-        <div x-editable="true">
-            <xsl:call-template name="standard-attributes" />
-            <xsl:apply-templates select="child::node()">
-                <xsl:with-param name="mixed" select="true()" />
-            </xsl:apply-templates>            
-        </div>
-        <xsl:apply-templates select="following-sibling::*[1][self::motto_podpis]" mode="motto">
-            <xsl:with-param name="mixed" select="true()" />
-        </xsl:apply-templates>
-        </div>
-    </xsl:template>
-
-    <!--  
-        Catch stand-alone "motto_podpis" and make then render properly.
-        If not, editing will fail :(.
-        
-        TODO: Make "motto" & "motto_podpis" one block in edit mode (like strofa) 
-    -->
-    <xsl:template match="motto_podpis[not(preceding-sibling::*[1][self::motto])]">
-       <xsl:apply-templates select="." mode="motto">
-            <xsl:with-param name="mixed" select="true()" />
-        </xsl:apply-templates>
-    </xsl:template>        
-        
-    <xsl:template match="motto_podpis" mode="motto">
-        <xsl:param name="mixed" />
-        <p x-editable="true">
-            <xsl:call-template name="standard-attributes" />
-            <xsl:call-template name="context-menu" />
-            <xsl:apply-templates select="child::node()">
-                <xsl:with-param name="mixed" select="true()" />
-            </xsl:apply-templates>
-        </p>
-    </xsl:template>    
-    
-    <xsl:template match="motto_podpis" />
-    
-    
-
-    <!--
-        Tagi obejmujące listę osób poprzedzającą tekst dramatu
-          (zwykle składaną na osobnej stronie; to odmiana stylu listy):
-
-        <lista_osob> osoby </lista_osob>
-    -->
-    <xsl:template match="lista_osob">
-        <xsl:param name="mixed" />
-        <div>
-            <xsl:call-template name="standard-attributes" />
-            <xsl:apply-templates select="child::node()">
-                <xsl:with-param name="mixed" select="false()" />
-            </xsl:apply-templates>
-        </div>
-    </xsl:template>
-
-    <xsl:template match="naglowek_listy">
-        <xsl:param name="mixed" />
-        <p x-editable="true">
-            <xsl:call-template name="standard-attributes" />
-            <xsl:call-template name="context-menu" />
-            <xsl:apply-templates select="child::node()">
-                <xsl:with-param name="mixed" select="true()" />
-            </xsl:apply-templates>
-        </p>
-    </xsl:template>
-
-    <xsl:template match="lista_osoba">
-        <xsl:param name="mixed" />
-        <p x-editable="true">
-            <xsl:call-template name="standard-attributes" />
-            <xsl:call-template name="context-menu" />
-            <xsl:apply-templates select="child::node()">
-                <xsl:with-param name="mixed" select="true()" />
-            </xsl:apply-templates>
-        </p>
-    </xsl:template>
-
-    <!--  Tagi obejmujące inne komentarze wprowadzające
-        przed tekstem dramatu (składane razem z listą osób):
-
-        <miejsce_czas> komentarze-wprowadzające </miejsce_czas>
-    -->
-    <xsl:template match="miejsce_czas">
-        <xsl:param name="mixed" />
-        <p x-editable="true">
-            <xsl:call-template name="standard-attributes" />
-            <xsl:call-template name="context-menu" />
-            <xsl:apply-templates select="child::node()">
-                <xsl:with-param name="mixed" select="true()" />
-            </xsl:apply-templates>
-        </p>
-    </xsl:template>
-
-
-    <!--
-        ***************************
-        TAGI STYLÓW TEKSTU GŁÓWNEGO
-        ***************************
-    -->
-
-    <!--
-      Tagi nagłówka części/księgi:
-      <naglowek_czesc> nagłówek-części-lub-księgi </naglowek_czesc>
-    -->
-    <xsl:template match="naglowek_czesc">
-        <xsl:param name="mixed" />
-        <h2 x-editable="true">
-            <xsl:call-template name="standard-attributes" />
-            <xsl:call-template name="context-menu" />
-            <xsl:apply-templates select="child::node()">
-                <xsl:with-param name="mixed" select="true()" />
-            </xsl:apply-templates>
-        </h2>
-    </xsl:template>
-
-    <!--
-      Tagi tytułu rozdziału:
-      <naglowek_rozdzial> nr-i/lub-tytuł </naglowek_rozdzial>
-    -->
-    <xsl:template match="naglowek_rozdzial">
-        <xsl:param name="mixed" />
-        <h3 x-editable="true">
-            <xsl:call-template name="standard-attributes" />
-            <xsl:call-template name="context-menu" />
-            <xsl:apply-templates select="child::node()">
-                <xsl:with-param name="mixed" select="true()" />
-            </xsl:apply-templates>
-        </h3>
-    </xsl:template>
-
-    <!--
-      Tagi tytułu podrozdziału:
-      <naglowek_podrozdzial> nr-i/lub-podtytuł </naglowek_podrozdzial>
-    -->
-    <xsl:template match="naglowek_podrozdzial">
-        <xsl:param name="mixed" />
-        <h4 x-editable="true">
-            <xsl:call-template name="standard-attributes" />
-            <xsl:call-template name="context-menu" />
-            <xsl:apply-templates select="child::node()">
-                <xsl:with-param name="mixed" select="true()" />
-            </xsl:apply-templates>
-        </h4>
-    </xsl:template>
-
-    <!--
-       Tagi sródtytułu:
-       <srodtytul> śródtytuł </srodtytul>
-
-       Tagi nagłówków aktów:
-       <naglowek_akt> nagłówek-aktu </naglowek_akt>
-    -->
-
-    <xsl:template match="srodtytul">
-        <xsl:param name="mixed" />
-        <h2 x-editable="true">
-            <xsl:call-template name="standard-attributes" />
-            <xsl:call-template name="context-menu" />
-            <xsl:apply-templates select="child::node()">
-                <xsl:with-param name="mixed" select="true()" />
-            </xsl:apply-templates>
-        </h2>
-    </xsl:template>
-
-    <xsl:template match="naglowek_akt">
-        <xsl:param name="mixed" />
-        <h2 x-editable="true">
-            <xsl:call-template name="standard-attributes" />
-            <xsl:call-template name="context-menu" />
-            <xsl:apply-templates select="child::node()">
-                <xsl:with-param name="mixed" select="true()" />
-            </xsl:apply-templates>
-        </h2>
-    </xsl:template>
-
-    <!--
-      Tagi nagłówków scen:
-      <naglowek_scena> nagłówek-sceny </naglowek_scena>
-    -->
-
-    <xsl:template match="naglowek_scena">
-        <xsl:param name="mixed" />
-        <h3 x-editable="true">
-            <xsl:call-template name="standard-attributes" />
-            <xsl:call-template name="context-menu" />
-            <xsl:apply-templates select="child::node()">
-                <xsl:with-param name="mixed" select="true()" />
-            </xsl:apply-templates>
-        </h3>
-    </xsl:template>
-    
-    <xsl:template match="naglowek_osoba">
-        <xsl:param name="mixed" />
-        <h4 x-editable="true">
-            <xsl:call-template name="standard-attributes" />
-            <xsl:call-template name="context-menu" />
-            <xsl:apply-templates select="child::node()">
-                <xsl:with-param name="mixed" select="true()" />
-            </xsl:apply-templates>
-        </h4>
-    </xsl:template>
-
-    <!--
-       ************************
-       Bloki w tekście głównym
-       ************************
-    -->
-    
-    <xsl:template match="dlugi_cytat">
-        <xsl:param name="mixed" />
-        <div>
-            <xsl:call-template name="standard-attributes" />
-            <xsl:apply-templates select="child::node()">
-                <xsl:with-param name="mixed" select="false()" />
-            </xsl:apply-templates>
-        </div>
-    </xsl:template>
-    
-    <xsl:template match="poezja_cyt">
-        <xsl:param name="mixed" />
-        <div>
-            <xsl:call-template name="standard-attributes" />
-            <xsl:apply-templates select="child::node()">
-                <xsl:with-param name="mixed" select="false()" />
-            </xsl:apply-templates>
-        </div>
-    </xsl:template>
-
-    <xsl:template match="kwestia">
-        <xsl:param name="mixed" />
-        <div>
-            <xsl:call-template name="standard-attributes" />
-            <xsl:apply-templates select="child::node()">
-                <xsl:with-param name="mixed" select="false()" />
-            </xsl:apply-templates>
-        </div>
-    </xsl:template>
-
-    <xsl:template match="didaskalia">
-        <xsl:param name="mixed" />
-        <div x-editable="true">
-            <xsl:call-template name="standard-attributes" />
-            <xsl:apply-templates select="child::node()">
-                <xsl:with-param name="mixed" select="true()" />
-            </xsl:apply-templates>
-        </div>
-    </xsl:template>
-
-    <xsl:template match="wywiad_pyt">
-        <xsl:param name="mixed" />
-        <div>
-            <xsl:call-template name="standard-attributes" />
-            <xsl:apply-templates select="child::node()">
-                <xsl:with-param name="mixed" select="false()" />
-            </xsl:apply-templates>
-        </div>
-    </xsl:template>
-
-    <xsl:template match="wywiad_odp">
-        <xsl:param name="mixed" />
-        <div>
-            <xsl:call-template name="standard-attributes" />
-            <xsl:apply-templates select="child::node()">
-                <xsl:with-param name="mixed" select="false()" />
-            </xsl:apply-templates>
-        </div>
-    </xsl:template>
-
-    <!--
-        ***********************************
-        Style akapitowe oraz strofy i wersy
-        ***********************************
-    -->
-
-    <xsl:template match="akap">
-        <xsl:param name="mixed" />
-        <p x-editable="true">
-            <xsl:call-template name="standard-attributes" />
-            <xsl:call-template name="context-menu" />
-            <xsl:apply-templates select="child::node()">
-                <xsl:with-param name="mixed" select="true()" />
-            </xsl:apply-templates>
-        </p>
-    </xsl:template>
-
-    <xsl:template match="akap_cd">
-        <xsl:param name="mixed" />
-        <p x-editable="true">
-            <xsl:call-template name="standard-attributes" />
-            <xsl:call-template name="context-menu" />
-            <xsl:apply-templates select="child::node()">
-                <xsl:with-param name="mixed" select="true()" />
-            </xsl:apply-templates>
-        </p>
-    </xsl:template>
-
-    <xsl:template match="akap_dialog">
-        <xsl:param name="mixed" />
-        <p x-editable="true">
-            <xsl:call-template name="standard-attributes" />
-            <xsl:call-template name="context-menu" />
-            <xsl:apply-templates select="child::node()">
-                <xsl:with-param name="mixed" select="true()" />
-            </xsl:apply-templates>
-        </p>
-    </xsl:template>
-
-    <!--
-        ********
-        STROFA
-        ********
-    -->
-    <xsl:template match="strofa">
-        <div x-editable="true">
-            <xsl:call-template name="standard-attributes" />
-            <xsl:call-template name="context-menu" />
-         
-            <xsl:choose>
-                <xsl:when test="count(br) > 0">
-                    <xsl:variable name="first-verse" select="br[1]/preceding-sibling::node()" />                    
-                    <xsl:call-template name="verse">
-                        <xsl:with-param name="verse-content" select="$first-verse" />                        
-                    </xsl:call-template>
-                    <xsl:for-each select="br">
-                        <xsl:variable name="lnum" select="count(preceding-sibling::br)" />
-                        <!-- select all nodes up to the next br or end of stanza -->
-                        <xsl:variable name="current-verse"
-                            select="following-sibling::node()[count(preceding-sibling::br) = $lnum+1]" />                        
-                        <xsl:call-template name="verse">
-                            <xsl:with-param name="verse-content" select="$current-verse" />                            
-                        </xsl:call-template>
-                    </xsl:for-each>
-                </xsl:when>
-                <xsl:otherwise>
-                    <xsl:call-template name="verse">
-                        <xsl:with-param name="verse-content" select="child::node()" />                        
-                    </xsl:call-template>
-                </xsl:otherwise>
-            </xsl:choose>
-        </div>
-    </xsl:template>
-
-    <xsl:template name="verse">
-        <!-- the verse contents including the last br (if any) -->
-        <xsl:param name="verse-content" />
-        <xsl:variable name="first-tag-name" select="name($verse-content/self::*)" />
-        <!-- name of text nodes is '' == false -->
-
-        <!-- THIS IS A HORROR!!! -->
-        <!-- Possible variants: -->
-        <xsl:choose>
-            <!-- Simple verse == not wers_ tags anywhere until the ending br -->
-            <xsl:when test="not($verse-content[starts-with(name(), 'wers_')])">
-                <p class="wers" x-node="wers" x-verse="true">
-                <xsl:apply-templates select="$verse-content[local-name(.) != 'br']">
-                    <xsl:with-param name="mixed" select="true()" />
-                </xsl:apply-templates>
-                </p>
-            </xsl:when>
-
-            <xsl:otherwise>
-            <xsl:apply-templates select="$verse-content[local-name(.) != 'br']">
-                <xsl:with-param name="mixed" select="false()" />
-            </xsl:apply-templates>
-            </xsl:otherwise>
-        </xsl:choose>
-    </xsl:template>
-
-    <xsl:template match="wers_cd|wers_akap|wers_wciety|wers_do_prawej">
-        <xsl:param name="mixed" />
-               <!-- <xsl:choose>
-               <xsl:when test="ancestor::*[0]/self::strofa"> -->
-               <p x-verse="true">
-               <xsl:call-template name="standard-attributes" />
-               <xsl:apply-templates select="child::node()">
-                       <xsl:with-param name="mixed" select="true()" />
-               </xsl:apply-templates>
-               </p>
-               <!-- </xsl:when> 
-               <xsl:choose> -->
-    </xsl:template>
-
-    <xsl:template match="br"><xsl:text>/</xsl:text></xsl:template>
-
-
-    <!--
-        *************
-        STYLE ZNAKOWE
-        *************
-    -->
-
-    <!--
-        Tagi obejmujące tytuł dzieła, np. książki, filmu, piosenki,
-        modlitwy, przedstawienia teatr. itd.:
-
-        <tytul_dziela> tytuł-dzieła </tytul_dziela>
-    -->
-    <xsl:template match="tytul_dziela">
-        <xsl:param name="mixed" />
-        <em>
-            <xsl:call-template name="standard-attributes" />
-            <xsl:apply-templates select="child::node()">
-                <xsl:with-param name="mixed" select="true()" />
-            </xsl:apply-templates>
-        </em>
-    </xsl:template>
-
-    <xsl:template match="wyroznienie|slowo_obce|mat|didask_tekst|osoba|wyp_osoba|www|wieksze_odstepy">
-        <xsl:param name="mixed" />
-        <em>
-            <xsl:call-template name="standard-attributes" />
-            <xsl:apply-templates select="child::node()">
-                <xsl:with-param name="mixed" select="true()" />
-            </xsl:apply-templates>
-        </em>
-    </xsl:template>
-
-    <xsl:template match="indeks_dolny">
-        <xsl:param name="mixed" />
-        <sub>
-            <xsl:call-template name="standard-attributes" />
-            <xsl:apply-templates select="child::node()">
-                <xsl:with-param name="mixed" select="true()" />
-            </xsl:apply-templates>
-        </sub>
-    </xsl:template>
-
-    <!--
-        **********
-        SEPARATORY
-        **********
-    -->
-    <xsl:template match="sekcja_swiatlo">
-        <xsl:param name="mixed" />
-        <hr><xsl:call-template name="standard-attributes" /></hr>
-    </xsl:template>
-
-    <xsl:template match="sekcja_asterysk">
-        <xsl:param name="mixed" />
-        <p><xsl:call-template name="standard-attributes" /></p>
-    </xsl:template>
-
-    <xsl:template match="separator_linia">
-        <xsl:param name="mixed" />
-        <hr><xsl:call-template name="standard-attributes" /></hr>
-    </xsl:template>
-
-    <xsl:template match="zastepnik_wersu">
-        <xsl:param name="mixed" />
-        <span>
-            <xsl:call-template name="standard-attributes" />
-            <xsl:apply-templates select="child::node()">
-                <xsl:with-param name="mixed" select="true()" />
-            </xsl:apply-templates>
-        </span>
-    </xsl:template>
-
-    <!--
-        *************
-           PRZYPISY
-        *************
-    -->
-
-    <!--
-        Przypisy i motywy
-    -->
-    <xsl:template match="pr|pa|pe|pt">       
-        <span x-editable="true" x-common="common">
-            <xsl:call-template name="standard-attributes">
-                <xsl:with-param name="extra-class" select="'annotation-inline-box'" />
-            </xsl:call-template>
-            <a name="anchor-{generate-id(.)}" />
-            <!-- the link to the non-inline version -->
-            <a href="#annotation-{generate-id(.)}" class="annotation"></a>
-            <!-- inline contents -->
-            <span x-annotation-box="true" x-pass-thru="true">
-                <xsl:call-template name="context-menu" />
-                <xsl:apply-templates select="node()">
-                    <xsl:with-param name="mixed" select="true()" />
-                </xsl:apply-templates>
-            </span>
-        </span>
-    </xsl:template>
-
-    <xsl:template match="begin">        
-        <span>
-            <xsl:call-template name="standard-attributes" />
-            <xsl:attribute name="theme-class">
-                <xsl:value-of select="substring-after(@id, 'b')" />
-            </xsl:attribute>
-        </span>
-    </xsl:template>
-
-    <xsl:template match="extra|uwaga">
-        <span x-common="common">
-            <xsl:call-template name="standard-attributes" />
-            <xsl:apply-templates select="child::node()">
-                <xsl:with-param name="mixed" select="true()" />
-            </xsl:apply-templates>
-        </span>
-    </xsl:template>
-
-    <xsl:template match="motyw">
-        <span x-editable="true" x-common="common">
-            <xsl:call-template name="standard-attributes" />
-            <xsl:attribute name="theme-class">
-                <xsl:value-of select="substring-after(@id, 'm')" />
-            </xsl:attribute>
-            <xsl:call-template name="context-menu" />
-            <span x-pass-thru="true" class="theme-text-list"><xsl:value-of select=".|node()" /></span>
-        </span>
-    </xsl:template>
-
-    <xsl:template match="end">
-        <span>
-            <xsl:call-template name="standard-attributes" />
-            <xsl:attribute name="theme-class">
-                <xsl:value-of select="substring-after(@id, 'e')" />
-            </xsl:attribute>
-        </span>
-    </xsl:template>
-
-
-    <!--
-        ****************
-         TEKST WŁAŚCIWY
-        ****************
-    -->
-
-    <xsl:template match="text()">
-        <!-- <xsl:value-of select="." /> -->
-        <xsl:param name="mixed" />
-        <xsl:choose>
-            <xsl:when test="normalize-space(.) = ''">
-                <xsl:value-of select="." />
-            </xsl:when>
-            <xsl:when test="not($mixed)">
-               <span x-node="out-of-flow-text" class="out-of-flow-text" x-editable="true">
-                       <xsl:value-of select="." />
-                               </span>
-                       </xsl:when>
-            <xsl:otherwise><xsl:value-of select="." /></xsl:otherwise>
-        </xsl:choose>
-    </xsl:template>
-
-    <xsl:template match="comment()">
-        <xsl:comment><xsl:value-of select="." /></xsl:comment>
-    </xsl:template>
-
-    <xsl:template match="*[name() != local-name()]">
-        <span>
-            <xsl:call-template name="standard-attributes" />
-            <xsl:apply-templates select="child::node()">
-                <xsl:with-param name="mixed" select="true()" />
-            </xsl:apply-templates>
-        </span>
-    </xsl:template>
-       
-       <!-- 
-               Earlier versions of html2wl introduced a BUG, that 'causes "out-of-flow-text" tag to appear.
-               Instead of marking it as "unknown", just pass thru it.
-               Keep a pass-thru span for out-of-flow box editing
-       -->
-    <xsl:template match="out-of-flow-text">
-        <span data-pass-thru="true">
-            <xsl:apply-templates select="child::node()">
-                <xsl:with-param name="mixed" select="false()" />
-            </xsl:apply-templates>        
-        </span>
-    </xsl:template>
-
-    <xsl:template match="*">
-        <span>
-            <xsl:call-template name="standard-attributes">
-                <xsl:with-param name="extra-class">unknown-tag</xsl:with-param>
-            </xsl:call-template>
-            <xsl:apply-templates select="child::node()">
-                <xsl:with-param name="mixed" select="true()" />
-            </xsl:apply-templates>        
-        </span>
-    </xsl:template>
-
-    <xsl:template name="context-menu">
-        <!-- <span class="default-menu context-menu"> -->
-        <!-- <button class="edit-button">Edytuj</button> -->
-            <!-- <span class="delete-button">Delete</span> -->
-        <!-- </span> -->
-        <!-- <span class="edit-menu context-menu">
-            <span class="accept-button">Accept</span>
-            <span class="reject-button">Close</span>
-        </span> -->
-    </xsl:template>
-
-    <xsl:template name="standard-attributes">
-        <xsl:param name="extra-class" />
-        <xsl:attribute name="class"><xsl:value-of select="local-name()" /><xsl:text>&#x0020;</xsl:text><xsl:value-of select="$extra-class" /></xsl:attribute>
-
-        <xsl:attribute name="x-node"><xsl:value-of select="local-name()" /></xsl:attribute>
-
-        <xsl:if test="local-name() != name()">
-            <xsl:attribute name="x-ns"><xsl:value-of select="namespace-uri()" /></xsl:attribute>
-        </xsl:if>
-
-        <xsl:for-each select="@*">
-            <xsl:variable name="id" select="generate-id()" />
-            <xsl:attribute name="x-attr-value-{$id}"><xsl:value-of select="."/></xsl:attribute>
-            <xsl:attribute name="x-attr-name-{$id}"><xsl:value-of select="local-name()"/></xsl:attribute>
-            <xsl:choose>
-               <xsl:when test="namespace-uri()">
-                <xsl:attribute name="x-attr-ns-{$id}"><xsl:value-of select="namespace-uri()"/></xsl:attribute>
-                               </xsl:when>
-                               <!-- if the element belongs to default namespace and attribut has no namespace -->
-               <xsl:when test="not(namespace-uri(.))">
-                               <xsl:attribute name="data-wlf-{local-name()}"><xsl:value-of select="."/></xsl:attribute>
-                               </xsl:when>
-                       </xsl:choose>               
-        </xsl:for-each>
-    </xsl:template>
-
-    <xsl:template match="alien">
-            <span class="alien" x-pass-thru="true">
-                <xsl:apply-templates select="node()">
-                    <xsl:with-param name="mixed" select="true()" />
-                </xsl:apply-templates>
-            </span>
-    </xsl:template>
-
-    <xsl:template match="comment()">
-        <xsl:comment><xsl:value-of select="."/></xsl:comment>
-    </xsl:template>
-</xsl:stylesheet>
diff --git a/redakcja/templates/404.html b/redakcja/templates/404.html
deleted file mode 100644 (file)
index 11edc9d..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-{% extends "catalogue/base.html" %}
-{% load i18n %}
-
-{% block titleextra %}{% trans "Page not found" %}{% endblock %}
-
-{% block content %}
-
-<h1>{% trans "Page not found" %}</h1>
-
-{% blocktrans with p=request.build_absolute_uri %}The page you're trying
-to reach (<tt>{{p}}</tt>) could not be found. If it's a document, try
-looking for it on the <a href="/">document list</a>.{% endblocktrans %}
-
-<p>
-{% blocktrans with m="mailto:radoslaw.czajka@nowoczesnapolska.org.pl" %}If you
-still can't find what you're looking for, please
-<a href="{{m}}">contact the administrator</a>.{% endblocktrans %}
-</p>
-
-{% url "catalogue_user" as m %}
-<p>
-{% blocktrans %}If you're coming from Redmine, please note that
-work is no longer managed there. 
-Go to the <a href="/">document list</a>
-or to <a href="{{m}}">your page</a> instead.{% endblocktrans %}
-</p>
-
-
-{% endblock content %}
diff --git a/redakcja/templates/500.html b/redakcja/templates/500.html
deleted file mode 100644 (file)
index bd7404f..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-{% extends "error_base.html" %}
-
-{% block "content" %}
-
-<h1>Błąd po stronie serwera.</h1>
-
-<p>Niestety nasz serwer WWW nie był w stanie dostarczyć Ci strony o którą prosisz.</p>
-<p><b>Serdecznie przepraszamy.</b></p>
-<p>Administrator został już powiadomiony o błędzie, ale jeśli chciałbyś
-przekazać nam więcej informacji na temat błędu, napisz na <a href="mailto:radoslaw.czajka@nowoczesnapolska.org.pl">nasz adres</a>.</p>
-</div>
-
-{% endblock %}
diff --git a/redakcja/templates/503.html b/redakcja/templates/503.html
deleted file mode 100644 (file)
index 1c1d39a..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-{% extends "error_base.html" %}
-
-{% block "content" %}
-
-<h1>Serwis tymczasowo niedostępny</h1>
-
-<p>
-Platfroma redakcyjna serwisu <a href="http://wolnelektury.pl/">Wolne Lektury</a> jest
-tymczasowo niedostępna z powodu prac administracyjnych.
-</p>
-
-<p>Prosimy o wyrozumiałość i ponowne odwiedziny.</p>
-
-{% endblock "content" %}
diff --git a/redakcja/templates/base.html b/redakcja/templates/base.html
deleted file mode 100644 (file)
index fc60995..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-<!DOCTYPE html >
-{% load i18n %}
-<html>
-    <head lang="{{ LANGUAGE_CODE }}">
-        <meta charset="utf-8" />
-        <link rel="icon" href="{{ STATIC_URL }}img/pr-icon.png" type="image/png" />
-        <title>{% block title %}{% block titleextra %}{% endblock titleextra %} ::
-            {% trans "Platforma Redakcyjna" %}{% endblock title %}</title>
-        {% block extrahead %}
-        {% endblock %}
-    </head>
-    <body id="{% block bodyid %}base{% endblock %}">
-
-    <div id="loading-overlay">
-       <div id="loading-message">
-               <img src="{{STATIC_URL}}img/spinner.gif" alt="Loading" />
-               <p>{% trans "Loading" %}</p>
-       </div>
-       </div>
-
-       <div id="body-wrap">
-               <!-- version: {{ APP_VERSION }} -->
-        <div id="content">{% block maincontent %} {% endblock %}</div>
-        </div>
-    <script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js"></script>
-    {% block extrabody %}{% endblock %}
-    </body>
-</html>
diff --git a/redakcja/templates/error_base.html b/redakcja/templates/error_base.html
deleted file mode 100755 (executable)
index 58784dc..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-    <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
-    <style type="text/css">
-
-body {
-    margin: 0;
-    font-family: verdana, sans-serif;
-    font-size: 10px;
-}
-
-img {
-    border: 0;
-}
-
-.clr {
-    clear: both;
-}
-
-#tabs-nav {
-    padding: 5px 5px 0 10px;
-    background: #ffdfbf;
-    border-bottom: 1px solid #ff8000;
-    position: relative;
-    height: 25px;
-}
-
-#logo {
-    position: absolute;
-    bottom: 0;
-}
-
-#content {
-    padding: 10px;
-}
-
-a, a:visited, a:active {
-       color: #bf6000;
-       text-decoration: none;
-}
-
-a:hover {
-       text-decoration: underline;
-}
-
-    </style>
-    <title>Platforma Redakcyjna</title>
-</head>
-<body>
-
-<div id="tabs-nav">
-
-<img id="logo" src='data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAC8AAAAcCAYAAADxyeavAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAD7AAAA+wBSobVeQAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAATYSURBVFiFvdhbjN1VFcfxzzpTR8tUYisJViJWJCCIVKUNYCGdaRPUMBYvqNFSYkyol4iNsalpaOfPf4bLKBiR8kTQCCG0tWMBa4WEgLY0lJBga61FiGKRahPqgxamUGjP8uF/Mr0wlzMHpzvZL2ft9Vvfvfb6r71zIjOd8FHGe/GgMKAnf9iqTO3/iNTcuDHeh02YJS1+K1InFr6MGQ7ZjDbcgDOUcW6rcicOvowzVBk/bJK52t2Ow+huVfLEwB8NTqfr8kXLcx+24DOtyk48fBkn4zeom6RLkXuGbOF+XOzmeHcr0hMLX0YN92EqulyXLxwX/WG0eV1XK/ITnfmb8Ul8UZHPv8m6Ip/FP2kNftJbQhttlPElLMMSRW4ZZeWjmNdKiInJfBkz8HPco8jbx1j9KD6kjPeMN8zEwIcL0GGy7zSx+rGGz7hLpzX4MmrKmDaK6g5w0IwxtcLZqGNkvRHDtDYW43llnDes9bC/4YC6D4+q0hcfkX6Fp53kF+OFaA3+7dbi39jgpjj1TfYi69jJKPA3xmnqNuIl7S63NAeVcW7je2lqjN5tyngXOjG/MUPNVVbm08pYgC3esFUZCxS58zjvHRj+3fKTmOyQDWjXZq7luU9vLMRduAffaAb+uMxH6IvZyuhTxlOq7N6PL2M76uoeU0anIndpMxuv4AlldB4rZbuRMr/fz3A2uq3Iv+uNa6R78XuTLW0GHCKv1y50SVdgAU7Da3hceER6ROGPZOqPqQ7aiI/hK4p8wK3RYdAGXIwvKPK3oIxZeNI0Ha7Ng0MRy1imurw+p8hf64sL1W3GeixS5KHxwG/DR/Gy6g2yXoeHLM3BYT3KOAkDqptziSLvaPy2EXOEhXpynTvjbfbajwsVuaPh+6nGuu8r8jZlnKV6sL2ATkW+1iw4Vdk8XG3DckV+VZEDI4JDkQdMd4WqPlcp4xaFV3XoxlZptTK6Lc438AfRKJ0yTsG9WK/I29wQH1T1+P1YMF5wiMykjDvwLeEqPbm6ae8yvo2fYgO+psNhgx5SldWlWIQDilypjLsxBx9X9fTNCMxR5D/GC34EXoTSXbhaVcsDTSv0xVx1A/gvrsRf8Tu0q1ms7gPYg8cxGy+pSmUKLlXkX1oBZ6jbZOIarMFqZVzZtMLK3GSSWXgRW/FdYQ3OV7dXkWtU38duHFKVSjvmjwhexjvcFNPHCh3H/HuwLtrsch8+j4WK/GXTm6iCfh23qt7vT5hmnmvzYOOFubax6k/oHiqVMk4RZkszMRPnq9pooF9hRSO5Y8BXYjX8GEuEUo/ekZxH2EA7zsJzinx96Pfe6JImq9krfUK6CBfhTNWJPIc/q27mncL7pVvwoKqFHhgb/gjEN7FK9TFerchXjrNPUd2g56kuo2qGpXpyreFG1T43YV8DsJp1zx6z0SMxLlOV8m5VR9pztHlkeOiN+dI67BZWSeccBXo6ErvwpGjMulOFmdJaRf7rKJB5+I/CtnGe5Jmq7E/FZxX5VHPwR5wHcI7qWLcPzcm2WZYvN9a1q3r/IgziGUXObiThB1J/Q3GLNoutyGea3sCP4p1etREX4NOK3NwcfAVWM11b4+IZzn4yHsAlqnZ7Or6nyOkN+wZMUbNMXT8uEa7Xo7/pU6ieIdUGai6zMrf+DykEtvGDDlIUAAAAAElFTkSuQmCC' />
-
-    <div class='clr' ></div>
-</div>
-
-<div id="content">
-{% block "content" %}
-{% endblock %}
-</div>
-
-
-</body>
-</html>
diff --git a/redakcja/templates/pagination/pagination.html b/redakcja/templates/pagination/pagination.html
deleted file mode 100755 (executable)
index fe566a8..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-{% if is_paginated %}
-{% load i18n %}
-<div class="pagination">
-    {% if page_obj.has_previous %}
-        <a href="?page={{ page_obj.previous_page_number }}{{ getvars }}{{ hashtag }}" class="prev">&lsaquo;&lsaquo; {% trans "previous" %}</a>
-    {% else %}
-        <span class="disabled prev">&lsaquo;&lsaquo; {% trans "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 }}{{ hashtag }}" class="page">{{ page }}</a>
-            {% endifequal %}
-        {% else %}
-            ...
-        {% endif %}
-    {% endfor %}
-    {% if page_obj.has_next %}
-        <a href="?page={{ page_obj.next_page_number }}{{ getvars }}{{ hashtag }}" class="next">{% trans "next" %} &rsaquo;&rsaquo;</a>
-    {% else %}
-        <span class="disabled next">{% trans "next" %} &rsaquo;&rsaquo;</span>
-    {% endif %}
-</div>
-{% endif %}
diff --git a/redakcja/templates/registration/head_login.html b/redakcja/templates/registration/head_login.html
deleted file mode 100644 (file)
index 2a8fd3b..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-{% load i18n %}
-
-{% if user.is_authenticated %}
-<span class="user_name">{{ user.username }}</span> |
-
-{% if user.is_staff %}
-    <a href="{% url 'admin:index' %}">{% trans "Admin" %}</a> |
-{% endif %}
-
-<a href='{% url "logout" %}{% if logout_to %}?next={{ logout_to }}{% endif %}'>{% trans "Log Out" %}</a>
-{% else %}
-{% url "login" as login_url %}
-{% ifnotequal login_url request.path %}
-    <a href='{{ login_url }}'>{% trans "Log In" %}</a>
-{% endifnotequal %}
-{% endif %}
diff --git a/redakcja/templates/registration/login.html b/redakcja/templates/registration/login.html
deleted file mode 100644 (file)
index adbef3c..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-{% extends "catalogue/base.html" %}
-
-{% block titleextra %}Logowanie{% endblock %}
-{% block subtitle %} - Logowanie {% endblock subtitle %}
-
-{% block content %}
-
-<div class="isection">
-<form method="POST" action="{% url 'login' %}">
-{% csrf_token %}
-{{ form.as_p }}
-<p><input type="submit" value="Login" /></p>
-<input type="hidden" name="next" value="{{ next|urlencode }}" />
-</form>
-</div>
-
-{% endblock content %}
diff --git a/redakcja/urls.py b/redakcja/urls.py
deleted file mode 100644 (file)
index c0629fa..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from django.conf.urls import include, patterns, url
-from django.contrib import admin
-from django.conf import settings
-from django.conf.urls.static import static
-from django.contrib.staticfiles.urls import staticfiles_urlpatterns
-from django.views.generic import RedirectView
-
-
-admin.autodiscover()
-
-urlpatterns = patterns('',
-    # Auth
-    url(r'^accounts/login/$', 'django_cas.views.login', name='login'),
-    url(r'^accounts/logout/$', 'django_cas.views.logout', name='logout'),
-    url(r'^admin/login/$', 'django_cas.views.login', name='login'),
-    url(r'^admin/logout/$', 'django_cas.views.logout', name='logout'),
-
-    # Admin panel
-    url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
-    (r'^admin/', include(admin.site.urls)),
-
-    (r'^comments/', include('django.contrib.comments.urls')),
-
-    url(r'^$', RedirectView.as_view(url= '/documents/')),
-    url(r'^documents/', include('catalogue.urls')),
-    url(r'^apiclient/', include('apiclient.urls')),
-    url(r'^editor/', include('wiki.urls')),
-    url(r'^images/', include('wiki_img.urls')),
-    url(r'^cover/', include('cover.urls')),
-)
-
-if settings.DEBUG:
-    urlpatterns += staticfiles_urlpatterns()
-    urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
diff --git a/redakcja/wsgi.py b/redakcja/wsgi.py
deleted file mode 100755 (executable)
index 5ffd742..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-import os
-import os.path
-import sys
-
-ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
-
-# Add apps and lib directories to PYTHONPATH
-sys.path = [
-    ROOT,
-    os.path.join(ROOT, 'apps'),
-] + sys.path
-
-
-os.environ.setdefault("DJANGO_SETTINGS_MODULE", "redakcja.settings")
-
-# This application object is used by the development server
-# as well as any WSGI server configured to use this file.
-from django.core.wsgi import get_wsgi_application
-application = get_wsgi_application()
diff --git a/src/apiclient/__init__.py b/src/apiclient/__init__.py
new file mode 100644 (file)
index 0000000..56ecb96
--- /dev/null
@@ -0,0 +1,51 @@
+import urllib
+
+import json
+import oauth2
+
+from apiclient.settings import WL_CONSUMER_KEY, WL_CONSUMER_SECRET, WL_API_URL, BETA_API_URL
+
+
+if WL_CONSUMER_KEY and WL_CONSUMER_SECRET:
+    wl_consumer = oauth2.Consumer(WL_CONSUMER_KEY, WL_CONSUMER_SECRET)
+else:
+    wl_consumer = None
+
+
+class ApiError(BaseException):
+    pass
+
+
+class NotAuthorizedError(BaseException):
+    pass
+
+
+def api_call(user, path, data=None, beta=False):
+    from .models import OAuthConnection
+    api_url = BETA_API_URL if beta else WL_API_URL
+    conn = OAuthConnection.get(user=user, beta=beta)
+    if not conn.access:
+        raise NotAuthorizedError("No WL authorization for user %s." % user)
+    token = oauth2.Token(conn.token, conn.token_secret)
+    client = oauth2.Client(wl_consumer, token)
+    if data is not None:
+        data = json.dumps(data)
+        data = urllib.urlencode({"data": data})
+        resp, content = client.request(
+                "%s%s" % (api_url, path),
+                method="POST",
+                body=data)
+    else:
+        resp, content = client.request(
+                "%s%s" % (api_url, path))
+    status = resp['status']
+
+    if status == '200':
+        return json.loads(content)
+    elif status.startswith('2'):
+        return
+    elif status == '401':
+        raise ApiError('User not authorized for publishing.')
+    else:
+        raise ApiError("WL API call error %s, path: %s" % (status, path))
+
diff --git a/src/apiclient/migrations/0001_initial.py b/src/apiclient/migrations/0001_initial.py
new file mode 100644 (file)
index 0000000..4af28a5
--- /dev/null
@@ -0,0 +1,75 @@
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+class Migration(SchemaMigration):
+
+    def forwards(self, orm):
+        
+        # Adding model 'OAuthConnection'
+        db.create_table('apiclient_oauthconnection', (
+            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('user', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['auth.User'], unique=True)),
+            ('access', self.gf('django.db.models.fields.BooleanField')(default=False)),
+            ('token', self.gf('django.db.models.fields.CharField')(max_length=64, null=True, blank=True)),
+            ('token_secret', self.gf('django.db.models.fields.CharField')(max_length=64, null=True, blank=True)),
+        ))
+        db.send_create_signal('apiclient', ['OAuthConnection'])
+
+
+    def backwards(self, orm):
+        
+        # Deleting model 'OAuthConnection'
+        db.delete_table('apiclient_oauthconnection')
+
+
+    models = {
+        'apiclient.oauthconnection': {
+            'Meta': {'object_name': 'OAuthConnection'},
+            'access': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'token': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'blank': 'True'}),
+            'token_secret': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True'})
+        },
+        'auth.group': {
+            'Meta': {'object_name': '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']", 'symmetrical': 'False', 'blank': 'True'})
+        },
+        'auth.permission': {
+            'Meta': {'ordering': "('content_type__app_label', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+            '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': {
+            'Meta': {'object_name': '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']", 'symmetrical': 'False', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            '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']", 'symmetrical': 'False', 'blank': 'True'}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+        },
+        'contenttypes.contenttype': {
+            'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', '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 = ['apiclient']
diff --git a/src/apiclient/migrations/0002_auto__add_field_oauthconnection_beta.py b/src/apiclient/migrations/0002_auto__add_field_oauthconnection_beta.py
new file mode 100644 (file)
index 0000000..094606f
--- /dev/null
@@ -0,0 +1,70 @@
+# -*- coding: utf-8 -*-
+from south.utils import datetime_utils as datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+    def forwards(self, orm):
+        # Adding field 'OAuthConnection.beta'
+        db.add_column(u'apiclient_oauthconnection', 'beta',
+                      self.gf('django.db.models.fields.BooleanField')(default=False),
+                      keep_default=False)
+
+
+    def backwards(self, orm):
+        # Deleting field 'OAuthConnection.beta'
+        db.delete_column(u'apiclient_oauthconnection', 'beta')
+
+
+    models = {
+        u'apiclient.oauthconnection': {
+            'Meta': {'object_name': 'OAuthConnection'},
+            'access': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'beta': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'token': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'blank': 'True'}),
+            'token_secret': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['auth.User']", 'unique': 'True'})
+        },
+        u'auth.group': {
+            'Meta': {'object_name': 'Group'},
+            u'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': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+        },
+        u'auth.permission': {
+            'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
+            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+        },
+        u'auth.user': {
+            'Meta': {'object_name': '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', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            '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', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+        },
+        u'contenttypes.contenttype': {
+            'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+            'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            u'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 = ['apiclient']
\ No newline at end of file
diff --git a/src/apiclient/migrations/0003_auto__chg_field_oauthconnection_user__del_unique_oauthconnection_user.py b/src/apiclient/migrations/0003_auto__chg_field_oauthconnection_user__del_unique_oauthconnection_user.py
new file mode 100644 (file)
index 0000000..ebd0fbb
--- /dev/null
@@ -0,0 +1,74 @@
+# -*- coding: utf-8 -*-
+from south.utils import datetime_utils as datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+    def forwards(self, orm):
+        # Removing unique constraint on 'OAuthConnection', fields ['user']
+        db.delete_unique(u'apiclient_oauthconnection', ['user_id'])
+
+
+        # Changing field 'OAuthConnection.user'
+        db.alter_column(u'apiclient_oauthconnection', 'user_id', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User']))
+
+    def backwards(self, orm):
+
+        # Changing field 'OAuthConnection.user'
+        db.alter_column(u'apiclient_oauthconnection', 'user_id', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['auth.User'], unique=True))
+        # Adding unique constraint on 'OAuthConnection', fields ['user']
+        db.create_unique(u'apiclient_oauthconnection', ['user_id'])
+
+
+    models = {
+        u'apiclient.oauthconnection': {
+            'Meta': {'object_name': 'OAuthConnection'},
+            'access': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'beta': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'token': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'blank': 'True'}),
+            'token_secret': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"})
+        },
+        u'auth.group': {
+            'Meta': {'object_name': 'Group'},
+            u'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': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+        },
+        u'auth.permission': {
+            'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
+            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+        },
+        u'auth.user': {
+            'Meta': {'object_name': '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', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            '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', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+        },
+        u'contenttypes.contenttype': {
+            'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+            'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            u'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 = ['apiclient']
\ No newline at end of file
diff --git a/src/apiclient/migrations/__init__.py b/src/apiclient/migrations/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/apiclient/models.py b/src/apiclient/models.py
new file mode 100644 (file)
index 0000000..64130bc
--- /dev/null
@@ -0,0 +1,21 @@
+from django.db import models
+from django.contrib.auth.models import User
+
+
+class OAuthConnection(models.Model):
+    user = models.ForeignKey(User)
+    access = models.BooleanField(default=False)
+    token = models.CharField(max_length=64, null=True, blank=True)
+    token_secret = models.CharField(max_length=64, null=True, blank=True)
+    beta = models.BooleanField(default=False)
+
+    @classmethod
+    def get(cls, user, beta=False):
+        try:
+            return cls.objects.get(user=user, beta=beta)
+        except cls.DoesNotExist:
+            o = cls(user=user, beta=beta)
+            o.save()
+            return o
+
+
diff --git a/src/apiclient/settings.py b/src/apiclient/settings.py
new file mode 100755 (executable)
index 0000000..51b4906
--- /dev/null
@@ -0,0 +1,20 @@
+from django.conf import settings
+
+
+WL_CONSUMER_KEY = getattr(settings, 'APICLIENT_WL_CONSUMER_KEY', None)
+WL_CONSUMER_SECRET = getattr(settings, 'APICLIENT_WL_CONSUMER_SECRET', None)
+
+WL_API_URL = getattr(settings, 'APICLIENT_WL_API_URL', 'https://wolnelektury.pl/api/')
+
+BETA_API_URL = getattr(settings, 'APICLIENT_BETA_API_URL', 'http://dev.wolnelektury.pl/api/')
+
+WL_REQUEST_TOKEN_URL = getattr(settings, 'APICLIENT_WL_REQUEST_TOKEN_URL', 
+        WL_API_URL + 'oauth/request_token/')
+WL_ACCESS_TOKEN_URL = getattr(settings, 'APICLIENT_WL_ACCESS_TOKEN_URL', 
+        WL_API_URL + 'oauth/access_token/')
+WL_AUTHORIZE_URL = getattr(settings, 'APICLIENT_WL_AUTHORIZE_URL', 
+        WL_API_URL + 'oauth/authorize/')
+
+BETA_REQUEST_TOKEN_URL = BETA_API_URL + 'oauth/request_token/'
+BETA_ACCESS_TOKEN_URL = BETA_API_URL + 'oauth/access_token/'
+BETA_AUTHORIZE_URL = BETA_API_URL + 'oauth/authorize/'
diff --git a/src/apiclient/urls.py b/src/apiclient/urls.py
new file mode 100755 (executable)
index 0000000..f623474
--- /dev/null
@@ -0,0 +1,9 @@
+# -*- coding: utf-8 -*-
+from django.conf.urls import patterns, url
+
+urlpatterns = patterns('apiclient.views',
+    url(r'^oauth/$', 'oauth', name='apiclient_oauth'),
+    url(r'^oauth_callback/$', 'oauth_callback', name='apiclient_oauth_callback'),
+    url(r'^oauth-beta/$', 'oauth', kwargs={'beta': True}, name='apiclient_beta_oauth'),
+    url(r'^oauth_callback-beta/$', 'oauth_callback', kwargs={'beta': True}, name='apiclient_beta_callback'),
+)
diff --git a/src/apiclient/views.py b/src/apiclient/views.py
new file mode 100644 (file)
index 0000000..239682a
--- /dev/null
@@ -0,0 +1,60 @@
+import cgi
+
+from django.contrib.auth.decorators import login_required
+from django.core.urlresolvers import reverse
+from django.http import HttpResponseRedirect, HttpResponse
+import oauth2
+
+from apiclient.models import OAuthConnection
+from apiclient import wl_consumer
+from apiclient.settings import WL_REQUEST_TOKEN_URL, WL_ACCESS_TOKEN_URL, WL_AUTHORIZE_URL
+from apiclient.settings import BETA_REQUEST_TOKEN_URL, BETA_ACCESS_TOKEN_URL, BETA_AUTHORIZE_URL
+
+
+@login_required
+def oauth(request, beta=False):
+    if wl_consumer is None:
+        return HttpResponse("OAuth consumer not configured.")
+
+    client = oauth2.Client(wl_consumer)
+    resp, content = client.request(WL_REQUEST_TOKEN_URL if not beta else BETA_REQUEST_TOKEN_URL)
+    if resp['status'] != '200':
+        raise Exception("Invalid response %s." % resp['status'])
+
+    request_token = dict(cgi.parse_qsl(content))
+    
+    conn = OAuthConnection.get(request.user, beta)
+    # this might reset existing auth!
+    conn.access = False
+    conn.token = request_token['oauth_token']
+    conn.token_secret = request_token['oauth_token_secret']
+    conn.save()
+
+    url = "%s?oauth_token=%s&oauth_callback=%s" % (
+            WL_AUTHORIZE_URL if not beta else BETA_AUTHORIZE_URL,
+            request_token['oauth_token'],
+            request.build_absolute_uri(reverse("apiclient_oauth_callback" if not beta else "apiclient_beta_callback")),
+            )
+
+    return HttpResponseRedirect(url)
+
+
+@login_required
+def oauth_callback(request, beta=False):
+    if wl_consumer is None:
+        return HttpResponse("OAuth consumer not configured.")
+
+    oauth_verifier = request.GET.get('oauth_verifier')
+    conn = OAuthConnection.get(request.user, beta)
+    token = oauth2.Token(conn.token, conn.token_secret)
+    token.set_verifier(oauth_verifier)
+    client = oauth2.Client(wl_consumer, token)
+    resp, content = client.request(WL_ACCESS_TOKEN_URL if not beta else BETA_ACCESS_TOKEN_URL, method="POST")
+    access_token = dict(cgi.parse_qsl(content))
+
+    conn.access = True
+    conn.token = access_token['oauth_token']
+    conn.token_secret = access_token['oauth_token_secret']
+    conn.save()
+
+    return HttpResponseRedirect('/')
diff --git a/src/catalogue/__init__.py b/src/catalogue/__init__.py
new file mode 100644 (file)
index 0000000..c53f0e7
--- /dev/null
@@ -0,0 +1 @@
+  # pragma: no cover
diff --git a/src/catalogue/admin.py b/src/catalogue/admin.py
new file mode 100644 (file)
index 0000000..53e8a25
--- /dev/null
@@ -0,0 +1,18 @@
+from django.contrib import admin
+
+from catalogue import models
+
+class BookAdmin(admin.ModelAdmin):
+    list_display = ['title', 'public', '_published', '_new_publishable', 'project']
+    list_filter = ['public', '_published', '_new_publishable', 'project']
+    prepopulated_fields = {'slug': ['title']}
+    search_fields = ['title']
+
+
+admin.site.register(models.Project)
+admin.site.register(models.Book, BookAdmin)
+admin.site.register(models.Chunk)
+admin.site.register(models.Chunk.tag_model)
+
+admin.site.register(models.Image)
+admin.site.register(models.Image.tag_model)
diff --git a/src/catalogue/constants.py b/src/catalogue/constants.py
new file mode 100644 (file)
index 0000000..0c84232
--- /dev/null
@@ -0,0 +1,16 @@
+# -*- coding: utf-8 -*-
+#
+# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
+TRIM_BEGIN = " TRIM_BEGIN "
+TRIM_END = " TRIM_END "
+
+MASTERS = ['powiesc',
+           'opowiadanie',
+           'liryka_l',
+           'liryka_lp',
+           'dramat_wierszowany_l',
+           'dramat_wierszowany_lp',
+           'dramat_wspolczesny',
+           ]
diff --git a/src/catalogue/ebook_utils.py b/src/catalogue/ebook_utils.py
new file mode 100644 (file)
index 0000000..dae2e76
--- /dev/null
@@ -0,0 +1,32 @@
+# -*- coding: utf-8 -*-
+from StringIO import StringIO
+from catalogue.models import Book
+from librarian import DocProvider
+from django.http import HttpResponse
+
+
+class RedakcjaDocProvider(DocProvider):
+    """Used for getting books' children."""
+
+    def __init__(self, publishable):
+        self.publishable = publishable
+
+    def by_slug(self, slug):
+        return StringIO(Book.objects.get(dc_slug=slug
+                    ).materialize(publishable=self.publishable
+                    ).encode('utf-8'))
+
+
+def serve_file(file_path, name, mime_type):
+    def read_chunks(f, size=8192):
+        chunk = f.read(size)
+        while chunk:
+            yield chunk
+            chunk = f.read(size)
+
+    response = HttpResponse(content_type=mime_type)
+    response['Content-Disposition'] = 'attachment; filename=%s' % name
+    with open(file_path) as f:
+        for chunk in read_chunks(f):
+            response.write(chunk)
+    return response
diff --git a/src/catalogue/feeds.py b/src/catalogue/feeds.py
new file mode 100644 (file)
index 0000000..4884a4c
--- /dev/null
@@ -0,0 +1,29 @@
+# -*- coding: utf-8 -*-
+from django.contrib.syndication.views import Feed
+from django.shortcuts import get_object_or_404
+from catalogue.models import Book, Chunk
+
+class PublishTrackFeed(Feed):
+    title = u"Planowane publikacje"
+    link = "/"
+
+    def description(self, obj):
+        tag, published = obj
+        return u"Publikacje, które dotarły co najmniej do etapu: %s" % tag.name
+
+    def get_object(self, request, slug):
+        published = request.GET.get('published')
+        if published is not None:
+            published = published == 'true'
+        return get_object_or_404(Chunk.tag_model, slug=slug), published
+
+    def item_title(self, item):
+        return item.title
+
+    def items(self, obj):
+        tag, published = obj
+        books = Book.objects.filter(public=True, _on_track__gte=tag.ordering
+                ).order_by('-_on_track', 'title')
+        if published is not None:
+            books = books.filter(_published=published)
+        return books
diff --git a/src/catalogue/fixtures/stages.json b/src/catalogue/fixtures/stages.json
new file mode 100644 (file)
index 0000000..5a46ec0
--- /dev/null
@@ -0,0 +1,83 @@
+[
+    {
+        "pk": 1, 
+        "model": "catalogue.chunktag", 
+        "fields": {
+            "ordering": 1, 
+            "name": "Autokorekta", 
+            "slug": "first_correction"
+        }
+    }, 
+    {
+        "pk": 2, 
+        "model": "catalogue.chunktag", 
+        "fields": {
+            "ordering": 2, 
+            "name": "Tagowanie", 
+            "slug": "tagging"
+        }
+    }, 
+    {
+        "pk": 3, 
+        "model": "catalogue.chunktag", 
+        "fields": {
+            "ordering": 3, 
+            "name": "Korekta", 
+            "slug": "proofreading"
+        }
+    }, 
+    {
+        "pk": 4, 
+        "model": "catalogue.chunktag", 
+        "fields": {
+            "ordering": 4, 
+            "name": "Sprawdzenie przypis\u00f3w \u017ar\u00f3d\u0142a", 
+            "slug": "annotation-proofreading"
+        }
+    }, 
+    {
+        "pk": 5, 
+        "model": "catalogue.chunktag", 
+        "fields": {
+            "ordering": 5, 
+            "name": "Uwsp\u00f3\u0142cze\u015bnienie", 
+            "slug": "modernisation"
+        }
+    }, 
+    {
+        "pk": 6, 
+        "model": "catalogue.chunktag", 
+        "fields": {
+            "ordering": 6, 
+            "name": "Przypisy", 
+            "slug": "annotations"
+        }
+    }, 
+    {
+        "pk": 7, 
+        "model": "catalogue.chunktag", 
+        "fields": {
+            "ordering": 7, 
+            "name": "Motywy", 
+            "slug": "themes"
+        }
+    }, 
+    {
+        "pk": 8, 
+        "model": "catalogue.chunktag", 
+        "fields": {
+            "ordering": 8, 
+            "name": "Ostateczna redakcja literacka", 
+            "slug": "editor-proofreading"
+        }
+    }, 
+    {
+        "pk": 9, 
+        "model": "catalogue.chunktag", 
+        "fields": {
+            "ordering": 9, 
+            "name": "Ostateczna redakcja techniczna", 
+            "slug": "technical-editor-proofreading"
+        }
+    }
+]
diff --git a/src/catalogue/forms.py b/src/catalogue/forms.py
new file mode 100644 (file)
index 0000000..ea6a4ae
--- /dev/null
@@ -0,0 +1,234 @@
+# -*- coding: utf-8 -*-
+#
+# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
+from catalogue.models import User
+from django.db.models import Count
+from django import forms
+from django.utils.translation import ugettext_lazy as _
+from django.conf import settings
+
+from catalogue.constants import MASTERS
+from catalogue.models import Book, Chunk, Image
+
+class DocumentCreateForm(forms.ModelForm):
+    """
+        Form used for creating new documents.
+    """
+    file = forms.FileField(required=False)
+    text = forms.CharField(required=False, widget=forms.Textarea)
+
+    class Meta:
+        model = Book
+        exclude = ['parent', 'parent_number', 'project']
+
+    def __init__(self, *args, **kwargs):
+        super(DocumentCreateForm, self).__init__(*args, **kwargs)
+        self.fields['slug'].widget.attrs={'class': 'autoslug'}
+        self.fields['gallery'].widget.attrs={'class': 'autoslug'}
+        self.fields['title'].widget.attrs={'class': 'autoslug-source'}
+
+    def clean(self):
+        super(DocumentCreateForm, self).clean()
+        file = self.cleaned_data['file']
+
+        if file is not None:
+            try:
+                self.cleaned_data['text'] = file.read().decode('utf-8')
+            except UnicodeDecodeError:
+                raise forms.ValidationError(_("Text file must be UTF-8 encoded."))
+
+        if not self.cleaned_data["text"]:
+            self._errors["file"] = self.error_class([_("You must either enter text or upload a file")])
+
+        return self.cleaned_data
+
+
+class DocumentsUploadForm(forms.Form):
+    """
+        Form used for uploading new documents.
+    """
+    file = forms.FileField(required=True, label=_('ZIP file'))
+    dirs = forms.BooleanField(label=_('Directories are documents in chunks'),
+            widget = forms.CheckboxInput(attrs={'disabled':'disabled'}))
+
+    def clean(self):
+        file = self.cleaned_data['file']
+
+        import zipfile
+        try:
+            z = self.cleaned_data['zip'] = zipfile.ZipFile(file)
+        except zipfile.BadZipfile:
+            raise forms.ValidationError("Should be a ZIP file.")
+        if z.testzip():
+            raise forms.ValidationError("ZIP file corrupt.")
+
+        return self.cleaned_data
+
+
+class ChunkForm(forms.ModelForm):
+    """
+        Form used for editing a chunk.
+    """
+    user = forms.ModelChoiceField(queryset=
+        User.objects.annotate(count=Count('chunk')).
+        order_by('last_name', 'first_name'), required=False,
+        label=_('Assigned to')) 
+
+    class Meta:
+        model = Chunk
+        fields = ['title', 'slug', 'gallery_start', 'user', 'stage']
+        exclude = ['number']
+
+    def __init__(self, *args, **kwargs):
+        super(ChunkForm, self).__init__(*args, **kwargs)
+        self.fields['gallery_start'].widget.attrs={'class': 'number-input'}
+        self.fields['slug'].widget.attrs={'class': 'autoslug'}
+        self.fields['title'].widget.attrs={'class': 'autoslug-source'}
+
+    def clean_slug(self):
+        slug = self.cleaned_data['slug']
+        try:
+            chunk = Chunk.objects.get(book=self.instance.book, slug=slug)
+        except Chunk.DoesNotExist:
+            return slug
+        if chunk == self.instance:
+            return slug
+        raise forms.ValidationError(_('Chunk with this slug already exists'))
+
+
+class ChunkAddForm(ChunkForm):
+    """
+        Form used for adding a chunk to a document.
+    """
+
+    def clean_slug(self):
+        slug = self.cleaned_data['slug']
+        try:
+            user = Chunk.objects.get(book=self.instance.book, slug=slug)
+        except Chunk.DoesNotExist:
+            return slug
+        raise forms.ValidationError(_('Chunk with this slug already exists'))
+
+
+class BookAppendForm(forms.Form):
+    """
+        Form for appending a book to another book.
+        It means moving all chunks from book A to book B and deleting A.
+    """
+    append_to = forms.ModelChoiceField(queryset=Book.objects.all(),
+            label=_("Append to"))
+
+    def __init__(self, book, *args, **kwargs):
+        ret =  super(BookAppendForm, self).__init__(*args, **kwargs)
+        self.fields['append_to'].queryset = Book.objects.exclude(pk=book.pk)
+        return ret
+
+
+class BookForm(forms.ModelForm):
+    """Form used for editing a Book."""
+
+    class Meta:
+        model = Book
+        exclude = ['project']
+
+    def __init__(self, *args, **kwargs):
+        ret = super(BookForm, self).__init__(*args, **kwargs)
+        self.fields['slug'].widget.attrs.update({"class": "autoslug"})
+        self.fields['title'].widget.attrs.update({"class": "autoslug-source"})
+        return ret
+
+    def save(self, **kwargs):
+        orig_instance = Book.objects.get(pk=self.instance.pk)
+        old_gallery = orig_instance.gallery
+        new_gallery = self.cleaned_data['gallery']
+        if new_gallery != old_gallery:
+            import shutil
+            import os.path
+            from django.conf import settings
+            shutil.move(orig_instance.gallery_path(),
+                        os.path.join(settings.MEDIA_ROOT, settings.IMAGE_DIR, new_gallery))
+        super(BookForm, self).save(**kwargs)
+
+
+class ReadonlyBookForm(BookForm):
+    """Form used for not editing a Book."""
+
+    def __init__(self, *args, **kwargs):
+        ret = super(ReadonlyBookForm, self).__init__(*args, **kwargs)
+        for field in self.fields.values():
+            field.widget.attrs.update({"disabled": "disabled"})
+        return ret
+
+
+class ChooseMasterForm(forms.Form):
+    """
+        Form used for fixing the chunks in a book.
+    """
+
+    master = forms.ChoiceField(choices=((m, m) for m in MASTERS))
+
+
+class ImageForm(forms.ModelForm):
+    """Form used for editing an Image."""
+    user = forms.ModelChoiceField(queryset=
+        User.objects.annotate(count=Count('chunk')).
+        order_by('-count', 'last_name', 'first_name'), required=False,
+        label=_('Assigned to')) 
+
+    class Meta:
+        model = Image
+        fields = ['title', 'slug', 'user', 'stage']
+
+    def __init__(self, *args, **kwargs):
+        super(ImageForm, self).__init__(*args, **kwargs)
+        self.fields['slug'].widget.attrs={'class': 'autoslug'}
+        self.fields['title'].widget.attrs={'class': 'autoslug-source'}
+
+
+class ReadonlyImageForm(ImageForm):
+    """Form used for not editing an Image."""
+
+    def __init__(self, *args, **kwargs):
+        super(ReadonlyImageForm, self).__init__(*args, **kwargs)
+        for field in self.fields.values():
+            field.widget.attrs.update({"disabled": "disabled"})
+
+
+class MarkFinalForm(forms.Form):
+    username = forms.CharField(initial=settings.LITERARY_DIRECTOR_USERNAME)
+    comment = forms.CharField(initial=u'Ostateczna akceptacja merytoryczna przez kierownika literackiego.')
+    books = forms.CharField(widget=forms.Textarea, help_text=u'linki do książek w redakcji, po jednym na wiersz')
+
+    def clean_books(self):
+        books_value = self.cleaned_data['books']
+        slugs = [line.strip().strip('/').split('/')[-1] for line in books_value.split('\n') if line.strip()]
+        books = Book.objects.filter(slug__in=slugs)
+        if len(books) != len(slugs):
+            raise forms.ValidationError(
+                'Incorrect slug(s): %s' % ' '.join(slug for slug in slugs if not Book.objects.filter(slug=slug)))
+        return books
+
+    def clean_username(self):
+        username = self.cleaned_data['username']
+        if not User.objects.filter(username=username):
+            raise forms.ValidationError('Invalid username')
+        return username
+
+    def save(self):
+        for book in self.cleaned_data['books']:
+            for chunk in book.chunk_set.all():
+                src = chunk.head.materialize()
+                chunk.commit(
+                    text=src,
+                    author=User.objects.get(username=self.cleaned_data['username']),
+                    description=self.cleaned_data['comment'],
+                    tags=[Chunk.tag_model.objects.get(slug='editor-proofreading')],
+                    publishable=True
+                )
+
+
+class PublishOptionsForm(forms.Form):
+    days = forms.IntegerField(label=u'po ilu dniach udostępnienić (0 = od razu)', min_value=0, initial=0)
+    beta = forms.BooleanField(label=u'Opublikuj na wersji testowej', required=False)
diff --git a/src/catalogue/helpers.py b/src/catalogue/helpers.py
new file mode 100644 (file)
index 0000000..d340b46
--- /dev/null
@@ -0,0 +1,148 @@
+from datetime import date
+from functools import wraps
+from os.path import join
+from os import listdir, stat
+from shutil import move, rmtree
+from django.conf import settings
+import re
+import filecmp
+
+from django.db.models import Count
+
+
+def active_tab(tab):
+    """
+        View decorator, which puts tab info on a request.
+    """
+    def wrapper(f):
+        @wraps(f)
+        def wrapped(request, *args, **kwargs):
+            request.catalogue_active_tab = tab
+            return f(request, *args, **kwargs)
+        return wrapped
+    return wrapper
+
+
+def cached_in_field(field_name):
+    def decorator(f):
+        @property
+        @wraps(f)
+        def wrapped(self, *args, **kwargs):
+            value = getattr(self, field_name)
+            if value is None:
+                value = f(self, *args, **kwargs)
+                type(self)._default_manager.filter(pk=self.pk).update(**{field_name: value})
+            return value
+        return wrapped
+    return decorator
+
+
+def parse_isodate(isodate):
+    try:
+        return date(*[int(p) for p in isodate.split('-')])
+    except (AttributeError, TypeError, ValueError):
+        raise ValueError("Not a date in ISO format.")
+
+
+class GalleryMerger(object):
+    def __init__(self, dest_gallery, src_gallery):
+        self.dest = dest_gallery
+        self.src = src_gallery
+        self.dest_size = None
+        self.src_size = None
+        self.num_deleted = 0
+
+    @staticmethod
+    def path(gallery):
+        return join(settings.MEDIA_ROOT, settings.IMAGE_DIR, gallery)
+
+    @staticmethod
+    def get_prefix(name):
+        m = re.match(r"^([0-9])-", name)
+        if m:
+            return int(m.groups()[0])
+        return None
+
+    @staticmethod
+    def set_prefix(name, prefix, always=False):
+        m = not always and re.match(r"^([0-9])-", name)
+        return "%1d-%s" % (prefix, m and name[2:] or name)
+
+    @property
+    def was_merged(self):
+        "Check if we have gallery size recorded"
+        return self.dest_size is not None
+
+    def merge(self):
+        if not self.dest:
+            return self.src
+        if not self.src:
+            return self.dest
+
+        files = listdir(self.path(self.dest))
+        files.sort()
+        self.dest_size = len(files)
+        files_other = listdir(self.path(self.src))
+        files_other.sort()
+        self.src_size = len(files_other)
+
+        if files and files_other:
+            print "compare %s with %s" % (files[-1], files_other[0])
+            if filecmp.cmp(
+                    join(self.path(self.dest), files[-1]),
+                    join(self.path(self.src), files_other[0]),
+                    False
+                    ):
+                files_other.pop(0)
+                self.num_deleted = 1
+
+        prefixes = {}
+        renamed_files = {}
+        renamed_files_other = {}
+        last_pfx = -1
+
+        # check if all elements of my files have a prefix
+        files_prefixed = True
+        for f in files:
+            p = self.get_prefix(f)
+            if p:
+                if p > last_pfx: last_pfx = p
+            else:
+                files_prefixed = False
+                break
+
+        # if not, add a 0 prefix to them
+        if not files_prefixed:
+            prefixes[0] = 0
+            for f in files:
+                renamed_files[f] = self.set_prefix(f, 0, True)
+
+        # two cases here - either all are prefixed or not.
+        files_other_prefixed = True
+        for f in files_other:
+            pfx = self.get_prefix(f)
+            if pfx is not None:
+                if not pfx in prefixes:
+                    last_pfx += 1
+                    prefixes[pfx] = last_pfx
+                renamed_files_other[f] = self.set_prefix(f, prefixes[pfx])
+            else:
+                # ops, not all files here were prefixed.
+                files_other_prefixed = False
+                break
+
+        # just set a 1- prefix to all of them
+        if not files_other_prefixed:
+            for f in files_other:
+                renamed_files_other[f] = self.set_prefix(f, 1, True)
+
+        # finally, move / rename files.
+        for frm, to in renamed_files.items():
+            move(join(self.path(self.dest), frm),
+                        join(self.path(self.dest), to))
+        for frm, to in renamed_files_other.items():
+            move(join(self.path(self.src), frm),
+                        join(self.path(self.dest), to))            
+
+        rmtree(join(self.path(self.src)))
+        return self.dest
diff --git a/src/catalogue/locale/pl/LC_MESSAGES/django.mo b/src/catalogue/locale/pl/LC_MESSAGES/django.mo
new file mode 100644 (file)
index 0000000..87bdfbf
Binary files /dev/null and b/src/catalogue/locale/pl/LC_MESSAGES/django.mo differ
diff --git a/src/catalogue/locale/pl/LC_MESSAGES/django.po b/src/catalogue/locale/pl/LC_MESSAGES/django.po
new file mode 100644 (file)
index 0000000..6387b5d
--- /dev/null
@@ -0,0 +1,804 @@
+# 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: Platforma Redakcyjna\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2018-12-20 12:30+0100\n"
+"PO-Revision-Date: 2014-03-27 13:17+0100\n"
+"Last-Translator: Radek Czajka <radoslaw.czajka@nowoczesnapolska.org.pl>\n"
+"Language-Team: Fundacja Nowoczesna Polska <fundacja@nowoczesnapolska.org."
+"pl>\n"
+"Language: pl\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
+"|| n%100>=20) ? 1 : 2);\n"
+"X-Generator: Poedit 1.5.4\n"
+
+#: forms.py:40
+msgid "Text file must be UTF-8 encoded."
+msgstr "Plik powinien mieć kodowanie UTF-8."
+
+#: forms.py:43
+msgid "You must either enter text or upload a file"
+msgstr "Proszę wpisać tekst albo wybrać plik do załadowania"
+
+#: forms.py:52
+msgid "ZIP file"
+msgstr "Plik ZIP"
+
+#: forms.py:53
+msgid "Directories are documents in chunks"
+msgstr "Katalogi zawierają dokumenty w częściach"
+
+#: forms.py:77 forms.py:178
+msgid "Assigned to"
+msgstr "Przypisane do"
+
+#: forms.py:98 forms.py:112
+msgid "Chunk with this slug already exists"
+msgstr "Część z tym slugiem już istnieje"
+
+#: forms.py:121
+msgid "Append to"
+msgstr "Dołącz do"
+
+#: views.py:172
+#, python-format
+msgid "Slug already used for %s"
+msgstr "Slug taki sam jak dla pliku %s"
+
+#: views.py:174
+msgid "Slug already used in repository."
+msgstr "Dokument o tym slugu już istnieje w repozytorium."
+
+#: views.py:180
+msgid "File should be UTF-8 encoded."
+msgstr "Plik powinien mieć kodowanie UTF-8."
+
+#: views.py:619 models/book.py:56
+msgid "books"
+msgstr "książki"
+
+#: views.py:621
+msgid "scan gallery"
+msgstr "galeria skanów"
+
+#: models/book.py:28 models/chunk.py:23 models/image.py:22
+msgid "title"
+msgstr "tytuł"
+
+#: models/book.py:29 models/chunk.py:24 models/image.py:23
+msgid "slug"
+msgstr "slug"
+
+#: models/book.py:30 models/image.py:24
+msgid "public"
+msgstr "publiczna"
+
+#: models/book.py:31
+msgid "scan gallery name"
+msgstr "nazwa galerii skanów"
+
+#: models/book.py:35
+msgid "parent"
+msgstr "rodzic"
+
+#: models/book.py:36
+msgid "parent number"
+msgstr "numeracja rodzica"
+
+#: models/book.py:55 models/chunk.py:21 models/publish_log.py:17
+msgid "book"
+msgstr "książka"
+
+#: models/book.py:261
+msgid "No chunks in the book."
+msgstr "Książka nie ma części."
+
+#: models/book.py:265
+msgid "Not all chunks have publishable revisions."
+msgstr "Niektóre części nie są gotowe do publikacji."
+
+#: models/book.py:272 models/image.py:86
+msgid "Invalid XML"
+msgstr "Nieprawidłowy XML"
+
+#: models/book.py:274 models/image.py:88
+msgid "No Dublin Core found."
+msgstr "Brak sekcji Dublin Core."
+
+#: models/book.py:276 models/image.py:90
+msgid "Invalid Dublin Core"
+msgstr "Nieprawidłowy Dublin Core"
+
+#: models/book.py:279 models/image.py:94
+msgid "rdf:about is not"
+msgstr "rdf:about jest różny od"
+
+#: models/chunk.py:22
+msgid "number"
+msgstr "numer"
+
+#: models/chunk.py:25
+msgid "gallery start"
+msgstr "początek galerii"
+
+#: models/chunk.py:40
+msgid "chunk"
+msgstr "część"
+
+#: models/chunk.py:41
+msgid "chunks"
+msgstr "części"
+
+#: models/image.py:21 models/image.py:36 models/publish_log.py:45
+msgid "image"
+msgstr "obraz"
+
+#: models/image.py:37
+msgid "images"
+msgstr "obrazy"
+
+#: models/image.py:79
+msgid "There is no publishable revision"
+msgstr "Żadna wersja nie została oznaczona do publikacji."
+
+#: models/project.py:13
+msgid "name"
+msgstr "nazwa"
+
+#: models/project.py:14
+msgid "notes"
+msgstr "notatki"
+
+#: models/project.py:19 templates/catalogue/image_table.html:58
+#: templates/catalogue/book_list/book_list.html:65
+msgid "project"
+msgstr "projekt"
+
+#: models/project.py:20
+msgid "projects"
+msgstr "projekty"
+
+#: models/publish_log.py:18 models/publish_log.py:46
+msgid "time"
+msgstr "czas"
+
+#: models/publish_log.py:19 models/publish_log.py:47
+#: templates/catalogue/wall.html:20
+msgid "user"
+msgstr "użytkownik"
+
+#: models/publish_log.py:24 models/publish_log.py:33
+msgid "book publish record"
+msgstr "zapis publikacji książki"
+
+#: models/publish_log.py:25
+msgid "book publish records"
+msgstr "zapisy publikacji książek"
+
+#: models/publish_log.py:34 models/publish_log.py:48
+msgid "change"
+msgstr "zmiana"
+
+#: models/publish_log.py:38
+msgid "chunk publish record"
+msgstr "zapis publikacji części"
+
+#: models/publish_log.py:39
+msgid "chunk publish records"
+msgstr "zapisy publikacji części"
+
+#: models/publish_log.py:53
+msgid "image publish record"
+msgstr "zapis publikacji obrazu"
+
+#: models/publish_log.py:54
+msgid "image publish records"
+msgstr "zapisy publikacji obrazów"
+
+#: templates/catalogue/active_users_list.html:5
+msgid "Active users"
+msgstr "Aktywni użytkownicy"
+
+#: templates/catalogue/active_users_list.html:11
+msgid "Active users since"
+msgstr "Użytkownicy aktywni od"
+
+#: templates/catalogue/activity.html:6 templates/catalogue/activity.html:12
+#: templatetags/catalogue.py:29
+msgid "Activity"
+msgstr "Aktywność"
+
+#: templates/catalogue/base.html:10
+msgid "Platforma Redakcyjna"
+msgstr "Platforma Redakcyjna"
+
+#: templates/catalogue/book_append_to.html:4
+#: templates/catalogue/book_append_to.html:11
+msgid "Append book"
+msgstr "Dołącz książkę"
+
+#: templates/catalogue/book_detail.html:18
+#: templates/catalogue/book_edit.html:13 templates/catalogue/chunk_edit.html:16
+#: templates/catalogue/image_detail.html:18
+msgid "Save"
+msgstr "Zapisz"
+
+#: templates/catalogue/book_detail.html:25
+msgid "Edit gallery"
+msgstr "Edytuj galerię"
+
+#: templates/catalogue/book_detail.html:28
+msgid "Append to other book"
+msgstr "Dołącz do innej książki"
+
+#: templates/catalogue/book_detail.html:34
+msgid "Chunks"
+msgstr "Części"
+
+#: templates/catalogue/book_detail.html:49
+#: templates/catalogue/image_detail.html:36 templatetags/wall.py:108
+#: templatetags/wall.py:129
+msgid "Publication"
+msgstr "Publikacja"
+
+#: templates/catalogue/book_detail.html:58
+#: templates/catalogue/image_detail.html:38
+msgid "Last published"
+msgstr "Ostatnio opublikowano"
+
+#: templates/catalogue/book_detail.html:68
+msgid "Full XML"
+msgstr "Pełny XML"
+
+#: templates/catalogue/book_detail.html:69
+msgid "HTML version"
+msgstr "Wersja HTML"
+
+#: templates/catalogue/book_detail.html:70
+msgid "TXT version"
+msgstr "Wersja TXT"
+
+#: templates/catalogue/book_detail.html:71
+msgid "PDF version"
+msgstr "Wersja PDF"
+
+#: templates/catalogue/book_detail.html:72
+msgid "PDF version for mobiles"
+msgstr "Wersja PDF na telefony"
+
+#: templates/catalogue/book_detail.html:73
+msgid "EPUB version"
+msgstr "Wersja EPUB"
+
+#: templates/catalogue/book_detail.html:74
+msgid "MOBI version"
+msgstr "Wersja MOBI"
+
+#: templates/catalogue/book_detail.html:88
+#: templates/catalogue/image_detail.html:57
+msgid "Publish"
+msgstr "Opublikuj"
+
+#: templates/catalogue/book_detail.html:92
+#: templates/catalogue/image_detail.html:61
+msgid "Log in to publish."
+msgstr "Zaloguj się, aby opublikować."
+
+#: templates/catalogue/book_detail.html:95
+#: templates/catalogue/image_detail.html:64
+msgid "This book can't be published yet, because:"
+msgstr "Ta książka nie może jeszcze zostać opublikowana. Powód:"
+
+#: templates/catalogue/book_edit.html:5
+msgid "Edit book"
+msgstr "Edytuj książkę"
+
+#: templates/catalogue/book_html.html:12 templates/catalogue/book_text.html:15
+msgid "Table of contents"
+msgstr "Spis treści"
+
+#: templates/catalogue/book_html.html:13 templates/catalogue/book_text.html:17
+msgid "Edit. note"
+msgstr "Nota red."
+
+#: templates/catalogue/book_html.html:14
+msgid "Infobox"
+msgstr "Informacje"
+
+#: templates/catalogue/book_text.html:7
+msgid "Redakcja"
+msgstr ""
+
+#: templates/catalogue/chunk_add.html:5 templates/catalogue/chunk_add.html:9
+#: templates/catalogue/chunk_edit.html:22
+msgid "Split chunk"
+msgstr "Podziel część"
+
+#: templates/catalogue/chunk_add.html:14
+msgid "Insert empty chunk after"
+msgstr "Wstaw pustą część po"
+
+#: templates/catalogue/chunk_add.html:17
+msgid "Add chunk"
+msgstr "Dodaj część"
+
+#: templates/catalogue/chunk_edit.html:5 templates/catalogue/chunk_edit.html:9
+#: templates/catalogue/book_list/book.html:9
+#: templates/catalogue/book_list/chunk.html:7
+msgid "Chunk settings"
+msgstr "Ustawienia części"
+
+#: templates/catalogue/chunk_edit.html:14
+msgid "Book"
+msgstr "Książka"
+
+#: templates/catalogue/document_create_missing.html:5
+#: templates/catalogue/document_create_missing.html:9
+msgid "Create a new book"
+msgstr "Utwórz nową książkę"
+
+#: templates/catalogue/document_create_missing.html:15
+msgid "Create book"
+msgstr "Utwórz książkę"
+
+#: templates/catalogue/document_list.html:7
+msgid "Book list"
+msgstr "Lista książek"
+
+#: templates/catalogue/document_upload.html:5
+msgid "Bulk document upload"
+msgstr "Hurtowe dodawanie dokumentów"
+
+#: templates/catalogue/document_upload.html:11
+msgid "Bulk documents upload"
+msgstr "Hurtowe dodawanie dokumentów"
+
+#: templates/catalogue/document_upload.html:14
+msgid ""
+"Please submit a ZIP with UTF-8 encoded XML files. Files not ending with "
+"<code>.xml</code> will be ignored."
+msgstr ""
+"Proszę wskazać archiwum ZIP z plikami XML w kodowaniu UTF-8. Pliki nie "
+"kończące się na <code>.xml</code> zostaną zignorowane."
+
+#: templates/catalogue/document_upload.html:20
+#: templates/catalogue/upload_pdf.html:16 templatetags/catalogue.py:36
+msgid "Upload"
+msgstr "Załaduj"
+
+#: templates/catalogue/document_upload.html:27
+msgid ""
+"There have been some errors. No files have been added to the repository."
+msgstr "Wystąpiły błędy. Żadne pliki nie zostały dodane do repozytorium."
+
+#: templates/catalogue/document_upload.html:28
+msgid "Offending files"
+msgstr "Błędne pliki"
+
+#: templates/catalogue/document_upload.html:36
+msgid "Correct files"
+msgstr "Poprawne pliki"
+
+#: templates/catalogue/document_upload.html:47
+msgid "Files have been successfully uploaded to the repository."
+msgstr "Pliki zostały dodane do repozytorium."
+
+#: templates/catalogue/document_upload.html:48
+msgid "Uploaded files"
+msgstr "Dodane pliki"
+
+#: templates/catalogue/document_upload.html:58
+msgid "Skipped files"
+msgstr "Pominięte pliki"
+
+#: templates/catalogue/document_upload.html:59
+msgid "Files skipped due to no <code>.xml</code> extension"
+msgstr "Pliki pominięte z powodu braku rozszerzenia <code>.xml</code>."
+
+#: templates/catalogue/image_detail.html:26
+msgid "Editor"
+msgstr "Edytor"
+
+#: templates/catalogue/image_detail.html:28
+msgid "Proceed to the editor."
+msgstr "Przejdź do edytora."
+
+#: templates/catalogue/image_list.html:8
+msgid "Image list"
+msgstr "Lista obrazów"
+
+#: templates/catalogue/image_short.html:6
+msgid "Image settings"
+msgstr "Ustawienia obrazu"
+
+#: templates/catalogue/image_table.html:23
+#: templates/catalogue/book_list/book_list.html:28
+msgid "Search in book titles"
+msgstr "Szukaj w tytułach książek"
+
+#: templates/catalogue/image_table.html:28
+#: templates/catalogue/book_list/book_list.html:33
+msgid "stage"
+msgstr "etap"
+
+#: templates/catalogue/image_table.html:30
+#: templates/catalogue/image_table.html:41
+#: templates/catalogue/image_table.html:60
+#: templates/catalogue/book_list/book_list.html:35
+#: templates/catalogue/book_list/book_list.html:46
+#: templates/catalogue/book_list/book_list.html:67
+msgid "none"
+msgstr "brak"
+
+#: templates/catalogue/image_table.html:39
+#: templates/catalogue/book_list/book_list.html:44
+msgid "editor"
+msgstr "redaktor"
+
+#: templates/catalogue/image_table.html:50
+#: templates/catalogue/book_list/book_list.html:57
+msgid "status"
+msgstr "status"
+
+#: templates/catalogue/image_table.html:77
+#, python-format
+msgid "%(c)s image"
+msgid_plural "%(c)s images"
+msgstr[0] "%(c)s obraz"
+msgstr[1] "%(c)s obrazy"
+msgstr[2] "%(c)s obrazów"
+
+#: templates/catalogue/image_table.html:82
+msgid "No images found."
+msgstr "Nie znaleziono obrazów."
+
+#: templates/catalogue/image_table.html:88
+#: templates/catalogue/book_list/book_list.html:102
+msgid "Set stage"
+msgstr "Ustaw etap"
+
+#: templates/catalogue/image_table.html:89
+#: templates/catalogue/book_list/book_list.html:103
+msgid "Set user"
+msgstr "Przypisz redaktora"
+
+#: templates/catalogue/image_table.html:91
+#: templates/catalogue/book_list/book_list.html:105
+msgid "Project"
+msgstr "Projekt"
+
+#: templates/catalogue/image_table.html:92
+#: templates/catalogue/book_list/book_list.html:106
+msgid "More users"
+msgstr "Więcej użytkowników"
+
+#: templates/catalogue/my_page.html:15 templatetags/catalogue.py:27
+msgid "My page"
+msgstr "Moja strona"
+
+#: templates/catalogue/my_page.html:24
+msgid "Your last edited documents"
+msgstr "Twoje ostatnie edycje"
+
+#: templates/catalogue/my_page.html:39 templates/catalogue/user_page.html:16
+msgid "Recent activity for"
+msgstr "Ostatnia aktywność dla:"
+
+#: templates/catalogue/upload_pdf.html:5 templates/catalogue/upload_pdf.html:11
+msgid "PDF file upload"
+msgstr "Ładowanie pliku PDF"
+
+#: templates/catalogue/user_list.html:7 templates/catalogue/user_list.html:12
+#: templatetags/catalogue.py:32
+msgid "Users"
+msgstr "Użytkownicy"
+
+#: templates/catalogue/wall.html:30
+msgid "not logged in"
+msgstr "nie zalogowany"
+
+#: templates/catalogue/wall.html:35
+msgid "No activity recorded."
+msgstr "Nie zanotowano aktywności."
+
+#: templates/catalogue/book_list/book.html:8
+#: templates/catalogue/book_list/book.html:29
+msgid "Book settings"
+msgstr "Ustawienia książki"
+
+#: templates/catalogue/book_list/book_list.html:23
+msgid "Show hidden books"
+msgstr "Pokaż ukryte książki"
+
+#: templates/catalogue/book_list/book_list.html:91
+#, python-format
+msgid "%(c)s book"
+msgid_plural "%(c)s books"
+msgstr[0] "%(c)s książka"
+msgstr[1] "%(c)s książki"
+msgstr[2] "%(c)s książek"
+
+#: templates/catalogue/book_list/book_list.html:96
+msgid "No books found."
+msgstr "Nie znaleziono książek."
+
+#: templatetags/book_list.py:84 templatetags/book_list.py:152
+msgid "publishable"
+msgstr "do publikacji"
+
+#: templatetags/book_list.py:85 templatetags/book_list.py:153
+msgid "changed"
+msgstr "zmienione"
+
+#: templatetags/book_list.py:86 templatetags/book_list.py:154
+msgid "published"
+msgstr "opublikowane"
+
+#: templatetags/book_list.py:87 templatetags/book_list.py:155
+msgid "unpublished"
+msgstr "nie opublikowane"
+
+#: templatetags/book_list.py:88 templatetags/book_list.py:156
+msgid "empty"
+msgstr "puste"
+
+#: templatetags/catalogue.py:30
+msgid "All"
+msgstr "Wszystkie"
+
+#: templatetags/catalogue.py:31
+msgid "Images"
+msgstr "Obrazy"
+
+#: templatetags/catalogue.py:35
+msgid "Add"
+msgstr "Dodaj"
+
+#: templatetags/catalogue.py:38
+msgid "Covers"
+msgstr "Okładki"
+
+#: templatetags/wall.py:49 templatetags/wall.py:78
+msgid "Related edit"
+msgstr "Powiązana zmiana"
+
+#: templatetags/wall.py:51 templatetags/wall.py:80
+msgid "Edit"
+msgstr "Zmiana"
+
+#: templatetags/wall.py:150
+msgid "Comment"
+msgstr "Komentarz"
+
+#~ msgid "Comments"
+#~ msgstr "Komentarze"
+
+#~ msgid "Mark publishable"
+#~ msgstr "Oznacz do publikacji"
+
+#~ msgid "Mark not publishable"
+#~ msgstr "Odznacz do publikacji"
+
+#~ msgid "Other user"
+#~ msgstr "Inny użytkownik"
+
+#~ msgid "Admin"
+#~ msgstr "Administracja"
+
+#~ msgid "edit"
+#~ msgstr "edytuj"
+
+#~ msgid "add basic document structure"
+#~ msgstr "dodaj podstawową strukturę dokumentu"
+
+#~ msgid "change master tag to"
+#~ msgstr "zmień tak master na"
+
+#~ msgid "add begin trimming tag"
+#~ msgstr "dodaj początkowy ogranicznik"
+
+#~ msgid "add end trimming tag"
+#~ msgstr "dodaj końcowy ogranicznik"
+
+#~ msgid "unstructured text"
+#~ msgstr "tekst bez struktury"
+
+#~ msgid "unknown XML"
+#~ msgstr "nieznany XML"
+
+#~ msgid "broken document"
+#~ msgstr "uszkodzony dokument"
+
+#~ msgid "Apply fixes"
+#~ msgstr "Wykonaj zmiany"
+
+#~ msgid "Can mark for publishing"
+#~ msgstr "Oznacza do publikacji"
+
+#~ msgid "Author"
+#~ msgstr "Autor"
+
+#~ msgid "Your name"
+#~ msgstr "Imię i nazwisko"
+
+#~ msgid "Author's email"
+#~ msgstr "E-mail autora"
+
+#~ msgid "Your email address, so we can show a gravatar :)"
+#~ msgstr "Adres e-mail, żebyśmy mogli pokazać gravatar :)"
+
+#~ msgid "Describe changes you made."
+#~ msgstr "Opisz swoje zmiany"
+
+#~ msgid "Completed"
+#~ msgstr "Ukończono"
+
+#~ msgid "If you completed a life cycle stage, select it."
+#~ msgstr "Jeśli został ukończony etap prac, wskaż go."
+
+#~ msgid "Describe the reason for reverting."
+#~ msgstr "Opisz powód przywrócenia."
+
+#~ msgid "theme"
+#~ msgstr "motyw"
+
+#~ msgid "themes"
+#~ msgstr "motywy"
+
+#~ msgid "Tag added"
+#~ msgstr "Dodano tag"
+
+#~ msgid "Revision marked"
+#~ msgstr "Wersja oznaczona"
+
+#~ msgid "New version"
+#~ msgstr "Nowa wersja"
+
+#~ msgid "Click to open/close gallery"
+#~ msgstr "Kliknij, aby (ro)zwinąć galerię"
+
+#~ msgid "Help"
+#~ msgstr "Pomoc"
+
+#~ msgid "Version"
+#~ msgstr "Wersja"
+
+#~ msgid "Unknown"
+#~ msgstr "nieznana"
+
+#~ msgid "Save attempt in progress"
+#~ msgstr "Trwa zapisywanie"
+
+#~ msgid "There is a newer version of this document!"
+#~ msgstr "Istnieje nowsza wersja tego dokumentu!"
+
+#~ msgid "Clear filter"
+#~ msgstr "Wyczyść filtr"
+
+#~ msgid "Cancel"
+#~ msgstr "Anuluj"
+
+#~ msgid "Revert"
+#~ msgstr "Przywróć"
+
+#~ msgid "all"
+#~ msgstr "wszystkie"
+
+#~ msgid "Annotations"
+#~ msgstr "Przypisy"
+
+#~ msgid "Previous"
+#~ msgstr "Poprzednie"
+
+#~ msgid "Next"
+#~ msgstr "Następne"
+
+#~ msgid "Zoom in"
+#~ msgstr "Powiększ"
+
+#~ msgid "Zoom out"
+#~ msgstr "Zmniejsz"
+
+#~ msgid "Gallery"
+#~ msgstr "Galeria"
+
+#~ msgid "Compare versions"
+#~ msgstr "Porównaj wersje"
+
+#~ msgid "Revert document"
+#~ msgstr "Przywróć wersję"
+
+#~ msgid "View version"
+#~ msgstr "Zobacz wersję"
+
+#~ msgid "History"
+#~ msgstr "Historia"
+
+#~ msgid "Search"
+#~ msgstr "Szukaj"
+
+#~ msgid "Replace with"
+#~ msgstr "Zamień na"
+
+#~ msgid "Replace"
+#~ msgstr "Zamień"
+
+#~ msgid "Options"
+#~ msgstr "Opcje"
+
+#~ msgid "Case sensitive"
+#~ msgstr "Rozróżniaj wielkość liter"
+
+#~ msgid "From cursor"
+#~ msgstr "Zacznij od kursora"
+
+#~ msgid "Search and replace"
+#~ msgstr "Znajdź i zamień"
+
+#~ msgid "Source code"
+#~ msgstr "Kod źródłowy"
+
+#~ msgid "Title"
+#~ msgstr "Tytuł"
+
+#~ msgid "Document ID"
+#~ msgstr "ID dokumentu"
+
+#~ msgid "Current version"
+#~ msgstr "Aktualna wersja"
+
+#~ msgid "Last edited by"
+#~ msgstr "Ostatnio edytowane przez"
+
+#~ msgid "Summary"
+#~ msgstr "Podsumowanie"
+
+#~ msgid "Insert theme"
+#~ msgstr "Wstaw motyw"
+
+#~ msgid "Insert annotation"
+#~ msgstr "Wstaw przypis"
+
+#~ msgid "Visual editor"
+#~ msgstr "Edytor wizualny"
+
+#~ msgid "Unassigned"
+#~ msgstr "Nie przypisane"
+
+#~ msgid "First correction"
+#~ msgstr "Autokorekta"
+
+#~ msgid "Tagging"
+#~ msgstr "Tagowanie"
+
+#~ msgid "Initial Proofreading"
+#~ msgstr "Korekta"
+
+#~ msgid "Annotation Proofreading"
+#~ msgstr "Sprawdzenie przypisów źródła"
+
+#~ msgid "Modernisation"
+#~ msgstr "Uwspółcześnienie"
+
+#~ msgid "Themes"
+#~ msgstr "Motywy"
+
+#~ msgid "Editor's Proofreading"
+#~ msgstr "Ostateczna redakcja literacka"
+
+#~ msgid "Technical Editor's Proofreading"
+#~ msgstr "Ostateczna redakcja techniczna"
+
+#~ msgid "Finished stage: %s"
+#~ msgstr "Ukończony etap: %s"
+
+#~ msgid "Refresh"
+#~ msgstr "Odśwież"
diff --git a/src/catalogue/management/__init__.py b/src/catalogue/management/__init__.py
new file mode 100644 (file)
index 0000000..bc3d6c0
--- /dev/null
@@ -0,0 +1,120 @@
+# -*- coding: utf-8 -*-
+#
+# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
+from collections import defaultdict
+from django.db import transaction
+from lxml import etree
+
+
+class XmlUpdater(object):
+    """A base class for massive XML updates.
+
+    In a subclass, override `fix_tree` and/or use `fixes_field` decorator.
+    Attributes:
+    * commit_desc: commits description
+    * retain_publishable: set publishable if head is (default: True)
+    * only_first_chunk: process only first chunks of books (default: False)
+    """
+    commit_desc = "auto-update"
+    retain_publishable = True
+    only_first_chunk = False
+
+    _element_fixers = defaultdict(list)
+
+    def __init__(self):
+        self.counters = defaultdict(lambda: 0)
+
+    @classmethod
+    def fixes_elements(cls, xpath):
+        """Decorator, registering a function as a fixer for given field type.
+
+        Any decorated function will be called like
+            f(element, change=..., verbose=...)
+        providing changeset as context.
+
+        :param xpath: element lookup, e.g. ".//{namespace-uri}tag-name"
+        :returns: True if anything changed
+        """
+        def wrapper(fixer):
+            cls._element_fixers[xpath].append(fixer)
+            return fixer
+        return wrapper
+
+    def fix_tree(self, tree, verbose):
+        """Override to provide general tree-fixing mechanism.
+
+        :param tree: the parsed XML tree
+        :param verbose: verbosity level
+        :returns: True if anythig changed
+        """
+        return False
+
+    def fix_chunk(self, chunk, user, verbose=0, dry_run=False):
+        """Runs the update for a single chunk."""
+        if verbose >= 2:
+            print chunk.get_absolute_url()
+        old_head = chunk.head
+        src = old_head.materialize()
+        try:
+            tree = etree.fromstring(src)
+        except:
+            if verbose:
+                print "%s: invalid XML" % chunk.get_absolute_url()
+            self.counters['Bad XML'] += 1
+            return
+
+        dirty = False
+        # Call the general fixing function.
+        if self.fix_tree(tree, verbose=verbose):
+            dirty = True
+        # Call the registered fixers.
+        for xpath, fixers in self._element_fixers.items():
+            for elem in tree.findall(xpath):
+                for fixer in fixers:
+                    if fixer(elem, change=old_head, verbose=verbose):
+                        dirty = True
+
+        if not dirty:
+            self.counters['Clean'] += 1
+            return
+
+        if not dry_run:
+            new_head = chunk.commit(
+                etree.tostring(tree, encoding=unicode),
+                author=user,
+                description=self.commit_desc
+            )
+            if self.retain_publishable:
+                if old_head.publishable:
+                    new_head.set_publishable(True)
+        if verbose >= 2:
+            print "done"
+        self.counters['Updated chunks'] += 1
+
+    def run(self, user, verbose=0, dry_run=False, books=None):
+        """Runs the actual update."""
+        if books is None:
+            from catalogue.models import Book
+            books = Book.objects.all()
+
+        # Start transaction management.
+        transaction.enter_transaction_management()
+
+        for book in books:
+            self.counters['All books'] += 1
+            chunks = book.chunk_set.all()
+            if self.only_first_chunk:
+                chunks = chunks[:1]
+            for chunk in chunks:
+                self.counters['All chunks'] += 1
+                self.fix_chunk(chunk, user, verbose, dry_run)
+
+        transaction.commit()
+        transaction.leave_transaction_management()
+
+    def print_results(self):
+        """Prints the counters."""
+        for item in sorted(self.counters.items()):
+            print "%s: %d" % item
diff --git a/src/catalogue/management/commands/__init__.py b/src/catalogue/management/commands/__init__.py
new file mode 100644 (file)
index 0000000..e6f146f
--- /dev/null
@@ -0,0 +1,43 @@
+# -*- coding: utf-8 -*-
+#
+# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
+import sys
+from optparse import make_option
+from django.contrib.auth.models import User
+from django.core.management.base import BaseCommand
+from catalogue.models import Book
+
+
+class XmlUpdaterCommand(BaseCommand):
+    """Base class for creating massive XML-updating commands.
+
+    In a subclass, provide an XmlUpdater class in the `updater' attribute.
+    """
+    option_list = BaseCommand.option_list + (
+        make_option('-q', '--quiet', action='store_false', dest='verbose',
+            default=True, help='Less output'),
+        make_option('-d', '--dry-run', action='store_true', dest='dry_run',
+            default=False, help="Don't actually touch anything"),
+        make_option('-u', '--username', dest='username', metavar='USER',
+            help='Assign commits to this user (required, preferably yourself).'),
+    )
+    args = "[slug]..."
+
+    def handle(self, *args, **options):
+        verbose = options.get('verbose')
+        dry_run = options.get('dry_run')
+        username = options.get('username')
+
+        if username:
+            user = User.objects.get(username=username)
+        else:
+            print 'Please provide a username.'
+            sys.exit(1)
+
+        books = Book.objects.filter(slug__in=args) if args else None
+
+        updater = self.updater()
+        updater.run(user, verbose=verbose, dry_run=dry_run, books=books)
+        updater.print_results()
diff --git a/src/catalogue/management/commands/add_parent.py b/src/catalogue/management/commands/add_parent.py
new file mode 100644 (file)
index 0000000..2ab0510
--- /dev/null
@@ -0,0 +1,101 @@
+# -*- coding: utf-8 -*-
+#
+# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
+import sys
+
+from datetime import date
+from lxml import etree
+
+from django.core.management import BaseCommand
+
+from catalogue.models import Book
+from librarian import RDFNS, DCNS
+
+TEMPLATE = '''<utwor>
+<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+<rdf:Description rdf:about="http://redakcja.wolnelektury.pl/documents/book/%(slug)s/">
+%(dc)s
+</rdf:Description>
+</rdf:RDF>
+
+</utwor>
+'''
+
+DC_TEMPLATE = '<dc:%(tag)s xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">%(value)s</dc:%(tag)s>'
+
+DC_TAGS = (
+    'creator',
+    'title',
+    'relation.hasPart',
+    'contributor.translator',
+    'contributor.editor',
+    'contributor.technical_editor',
+    'contributor.funding',
+    'contributor.thanks',
+    'publisher',
+    'subject.period',
+    'subject.type',
+    'subject.genre',
+    'description',
+    'identifier.url',
+    'source',
+    'source.URL',
+    'rights.license',
+    'rights',
+    'date.pd',
+    'format',
+    'type',
+    'date',
+    'audience',
+    'language',
+)
+
+IDENTIFIER_PREFIX = 'http://wolnelektury.pl/katalog/lektura/'
+
+
+def dc_desc_element(book):
+    xml = book.materialize()
+    tree = etree.fromstring(xml)
+    return tree.find(".//" + RDFNS("Description"))
+
+
+def distinct_dc_values(tag, desc_elements):
+    values = set()
+    for desc in desc_elements:
+        values.update(elem.text for elem in desc.findall(DCNS(tag)))
+    return values
+
+
+class Command(BaseCommand):
+    args = 'slug'
+
+    def handle(self, slug, **options):
+        children_slugs = [line.strip() for line in sys.stdin]
+        children = Book.objects.filter(dc_slug__in=children_slugs)
+        desc_elements = [dc_desc_element(child) for child in children]
+        title = u'Utwory wybrane'
+        own_attributes = {
+            'title': title,
+            'relation.hasPart': [IDENTIFIER_PREFIX + child_slug for child_slug in children_slugs],
+            'identifier.url': IDENTIFIER_PREFIX + slug,
+            'date': date.today().isoformat(),
+        }
+        dc_tags = []
+        for tag in DC_TAGS:
+            if tag in own_attributes:
+                values = own_attributes[tag]
+                if not isinstance(values, list):
+                    values = [values]
+            else:
+                values = distinct_dc_values(tag, desc_elements)
+            for value in values:
+                dc_tags.append(DC_TEMPLATE % {'tag': tag, 'value': value})
+        xml = TEMPLATE % {'slug': slug, 'dc': '\n'.join(dc_tags)}
+        Book.create(
+            text=xml,
+            creator=None,
+            slug=slug,
+            title=title,
+            gallery=slug)
diff --git a/src/catalogue/management/commands/assign_from_redmine.py b/src/catalogue/management/commands/assign_from_redmine.py
new file mode 100644 (file)
index 0000000..491fd83
--- /dev/null
@@ -0,0 +1,151 @@
+# -*- coding: utf-8 -*-
+
+import csv
+from optparse import make_option
+import re
+import sys
+import urllib
+import urllib2
+
+from django.contrib.auth.models import User
+from django.core.management.base import BaseCommand
+from django.core.management.color import color_style
+from django.db import transaction
+
+from slugify import slugify
+from catalogue.models import Chunk
+
+
+REDMINE_CSV = 'http://redmine.nowoczesnapolska.org.pl/projects/wl-publikacje/issues.csv'
+REDAKCJA_URL = 'http://redakcja.wolnelektury.pl/documents/'
+
+
+class Command(BaseCommand):
+    option_list = BaseCommand.option_list + (
+        make_option('-r', '--redakcja', dest='redakcja', metavar='URL',
+            help='Base URL of Redakcja documents',
+            default=REDAKCJA_URL),
+        make_option('-q', '--quiet', action='store_false', dest='verbose', default=True,
+            help='Less output'),
+        make_option('-f', '--force', action='store_true', dest='force', default=False,
+            help='Force assignment overwrite'),
+    )
+    help = 'Imports ticket assignments from Redmine.'
+    args = '[redmine-csv-url]'
+
+    def handle(self, *redmine_csv, **options):
+
+        self.style = color_style()
+
+        redakcja = options.get('redakcja')
+        verbose = options.get('verbose')
+        force = options.get('force')
+
+        if not redmine_csv:
+            if verbose:
+                print "Using default Redmine CSV URL:", REDMINE_CSV
+            redmine_csv = REDMINE_CSV
+
+        # Start transaction management.
+        transaction.enter_transaction_management()
+
+        redakcja_link = re.compile(re.escape(redakcja) + r'([-_.:?&%/a-zA-Z0-9]*)')
+
+        all_tickets = 0
+        all_chunks = 0
+        done_tickets = 0
+        done_chunks = 0
+        empty_users = 0
+        unknown_users = {}
+        unknown_books = []
+        forced = []
+
+        if verbose:
+            print 'Downloading CSV file'
+        for r in csv.reader(urllib2.urlopen(redmine_csv)):
+            if r[0] == '#':
+                continue
+            all_tickets += 1
+
+            username = r[6]
+            if not username:
+                if verbose:
+                    print "Empty user, skipping"
+                empty_users += 1
+                continue
+
+            first_name, last_name = unicode(username, 'utf-8').rsplit(u' ', 1)
+            try:
+                user = User.objects.get(first_name=first_name, last_name=last_name)
+            except User.DoesNotExist:
+                print self.style.ERROR('Unknown user: ' + username)
+                unknown_users.setdefault(username, 0)
+                unknown_users[username] += 1
+                continue
+
+            ticket_done = False
+            for fname in redakcja_link.findall(r[-1]):
+                fname = unicode(urllib.unquote(fname), 'utf-8', 'ignore')
+                if fname.endswith('.xml'):
+                    fname = fname[:-4]
+                fname = fname.replace(' ', '_')
+                fname = slugify(fname)
+
+                chunks = Chunk.objects.filter(book__slug=fname)
+                if not chunks:
+                    print self.style.ERROR('Unknown book: ' + fname)
+                    unknown_books.append(fname)
+                    continue
+                all_chunks += chunks.count()
+
+                for chunk in chunks:
+                    if chunk.user:
+                        if chunk.user == user:
+                            continue
+                        else:
+                            forced.append((chunk, chunk.user, user))
+                            if force:
+                                print self.style.WARNING(
+                                    '%s assigned to %s, forcing change to %s.' %
+                                    (chunk.pretty_name(), chunk.user, user))
+                            else:
+                                print self.style.WARNING(
+                                    '%s assigned to %s not to %s, skipping.' %
+                                    (chunk.pretty_name(), chunk.user, user))
+                                continue
+                    chunk.user = user
+                    chunk.save()
+                    ticket_done = True
+                    done_chunks += 1
+
+            if ticket_done:
+                done_tickets += 1
+
+
+        # Print results
+        print
+        print "Results:"
+        print "Assignments imported from %d/%d tickets to %d/%d relevalt chunks." % (
+                done_tickets, all_tickets, done_chunks, all_chunks)
+        if empty_users:
+            print "%d tickets were unassigned." % empty_users
+        if forced:
+            print "%d assignments conficts (%s):" % (
+                len(forced), "changed" if force else "left")
+            for chunk, orig, user in forced:
+                print "  %s: \t%s \t->  %s" % (
+                    chunk.pretty_name(), orig.username, user.username)
+        if unknown_books:
+            print "%d unknown books:" % len(unknown_books)
+            for fname in unknown_books:
+                print "  %s" % fname
+        if unknown_users:
+            print "%d unknown users:" % len(unknown_users)
+            for name in unknown_users:
+                print "  %s (%d tickets)" % (name, unknown_users[name])
+        print
+
+
+        transaction.commit()
+        transaction.leave_transaction_management()
+
diff --git a/src/catalogue/management/commands/fixdc.py b/src/catalogue/management/commands/fixdc.py
new file mode 100644 (file)
index 0000000..3f997d0
--- /dev/null
@@ -0,0 +1,54 @@
+# -*- coding: utf-8 -*-
+#
+# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
+from librarian import RDFNS, WLURI, ValidationError
+from librarian.dcparser import BookInfo
+from catalogue.management import XmlUpdater
+from catalogue.management.commands import XmlUpdaterCommand
+
+
+class FixDC(XmlUpdater):
+    commit_desc = "auto-fixing DC"
+    retain_publishable = True
+    only_first_chunk = True
+
+    def fix_wluri(elem, change, verbose):
+        try:
+            WLURI.strict(elem.text)
+        except ValidationError:
+            correct_field = unicode(WLURI.from_slug(
+                                WLURI(elem.text.strip()).slug))
+            try:
+                WLURI.strict(correct_field)
+            except ValidationError:
+                # Can't make a valid WLURI out of it, leave as is.
+                return False
+            if verbose:
+                print "Changing %s from %s to %s" % (
+                        elem.tag, elem.text, correct_field
+                    )
+            elem.text = correct_field
+            return True
+    for field in BookInfo.FIELDS:
+        if field.validator == WLURI:
+            XmlUpdater.fixes_elements('.//' + field.uri)(fix_wluri)
+
+    @XmlUpdater.fixes_elements(".//" + RDFNS("Description"))
+    def fix_rdfabout(elem, change, verbose):
+        correct_about = change.tree.book.correct_about()
+        attr_name = RDFNS("about")
+        current_about = elem.get(attr_name)
+        if current_about != correct_about:
+            if verbose:
+                print "Changing rdf:about from %s to %s" % (
+                        current_about, correct_about
+                    )
+            elem.set(attr_name, correct_about)
+            return True
+
+
+class Command(XmlUpdaterCommand):
+    updater = FixDC
+    help = 'Fixes obvious errors in DC: rdf:about and WLURI format.'
diff --git a/src/catalogue/management/commands/import_wl.py b/src/catalogue/management/commands/import_wl.py
new file mode 100644 (file)
index 0000000..45c9e33
--- /dev/null
@@ -0,0 +1,89 @@
+# -*- coding: utf-8 -*-
+
+from collections import defaultdict
+import json
+from optparse import make_option
+import urllib2
+
+from django.core.management.base import BaseCommand
+from django.core.management.color import color_style
+from django.db import transaction
+from librarian.dcparser import BookInfo
+from librarian import ParseError, ValidationError
+
+from catalogue.models import Book
+
+
+WL_API = 'http://www.wolnelektury.pl/api/books/'
+
+
+class Command(BaseCommand):
+    option_list = BaseCommand.option_list + (
+        make_option('-q', '--quiet', action='store_false', dest='verbose', default=True,
+            help='Less output'),
+    )
+    help = 'Imports XML files from WL.'
+
+    def handle(self, *args, **options):
+
+        self.style = color_style()
+
+        verbose = options.get('verbose')
+
+        # Start transaction management.
+        transaction.enter_transaction_management()
+
+        if verbose:
+            print 'Reading currently managed files (skipping hidden ones).'
+        slugs = defaultdict(list)
+        for b in Book.objects.exclude(slug__startswith='.').all():
+            if verbose:
+                print b.slug
+            text = b.materialize().encode('utf-8')
+            try:
+                info = BookInfo.from_bytes(text)
+            except (ParseError, ValidationError):
+                pass
+            else:
+                slugs[info.slug].append(b)
+
+        book_count = 0
+        commit_args = {
+            "author_name": 'Platforma',
+            "description": 'Automatycznie zaimportowane z Wolnych Lektur',
+            "publishable": True,
+        }
+
+        if verbose:
+            print 'Opening books list'
+        for book in json.load(urllib2.urlopen(WL_API)):
+            book_detail = json.load(urllib2.urlopen(book['href']))
+            xml_text = urllib2.urlopen(book_detail['xml']).read()
+            info = BookInfo.from_bytes(xml_text)
+            previous_books = slugs.get(info.slug)
+            if previous_books:
+                if len(previous_books) > 1:
+                    print self.style.ERROR("There is more than one book "
+                        "with slug %s:"), 
+                previous_book = previous_books[0]
+                comm = previous_book.slug
+            else:
+                previous_book = None
+                comm = '*'
+            print book_count, info.slug , '-->', comm
+            Book.import_xml_text(xml_text, title=info.title[:255],
+                slug=info.slug[:128], previous_book=previous_book,
+                commit_args=commit_args)
+            book_count += 1
+
+        # Print results
+        print
+        print "Results:"
+        print "Imported %d books from WL:" % (
+                book_count, )
+        print
+
+
+        transaction.commit()
+        transaction.leave_transaction_management()
+
diff --git a/src/catalogue/management/commands/insert_isbn.py b/src/catalogue/management/commands/insert_isbn.py
new file mode 100644 (file)
index 0000000..7548cb1
--- /dev/null
@@ -0,0 +1,103 @@
+# -*- coding: utf-8 -*-
+#
+# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
+import csv
+
+import sys
+from django.contrib.auth.models import User
+from lxml import etree
+from optparse import make_option
+
+from collections import defaultdict
+from django.core.management import BaseCommand
+
+from catalogue.models import Book
+from librarian import RDFNS, DCNS
+
+CONTENT_TYPES = {
+    'pdf':  'application/pdf',
+    'epub': 'application/epub+zip',
+    'mobi': 'application/x-mobipocket-ebook',
+    'txt':  'text/plain',
+    'html': 'text/html',
+}
+
+
+ISBN_TEMPLATES = (
+    r'<dc:relation.hasFormat id="%(format)s" xmlns:dc="http://purl.org/dc/elements/1.1/">%(url)s'
+    r'</dc:relation.hasFormat>',
+    r'<meta refines="#%(format)s" id="%(format)s-id" property="dcterms:identifier">ISBN-%(isbn)s</meta>',
+    r'<meta refines="#%(format)s-id" property="identifier-type">ISBN</meta>',
+    r'<meta refines="#%(format)s" property="dcterms:format">%(content_type)s</meta>',
+)
+
+
+def url_for_format(slug, format):
+    if format == 'html':
+        return 'https://wolnelektury.pl/katalog/lektura/%s.html' % slug
+    else:
+        return 'http://wolnelektury.pl/media/book/%(format)s/%(slug)s.%(format)s' % {'slug': slug, 'format': format}
+
+
+class Command(BaseCommand):
+    option_list = BaseCommand.option_list + (
+        # make_option('-q', '--quiet', action='store_false', dest='verbose',
+        #     default=True, help='Less output'),
+        # make_option('-d', '--dry-run', action='store_true', dest='dry_run',
+        #     default=False, help="Don't actually touch anything"),
+        make_option(
+            '-u', '--username', dest='username', metavar='USER',
+            help='Assign commits to this user (required, preferably yourself).'),
+    )
+    args = 'csv_file'
+
+    def handle(self, csv_file, **options):
+        username = options.get('username')
+
+        if username:
+            user = User.objects.get(username=username)
+        else:
+            print 'Please provide a username.'
+            sys.exit(1)
+
+        csvfile = open(csv_file, 'rb')
+        isbn_lists = defaultdict(list)
+        for slug, format, isbn in csv.reader(csvfile, delimiter=','):
+            isbn_lists[slug].append((format, isbn))
+        csvfile.close()
+
+        for slug, isbn_list in isbn_lists.iteritems():
+            print 'processing %s' % slug
+            book = Book.objects.get(dc_slug=slug)
+            chunk = book.chunk_set.first()
+            old_head = chunk.head
+            src = old_head.materialize()
+            tree = etree.fromstring(src)
+            isbn_node = tree.find('.//' + DCNS("relation.hasFormat"))
+            if isbn_node is not None:
+                print '%s already contains ISBN metadata, skipping' % slug
+                continue
+            desc = tree.find(".//" + RDFNS("Description"))
+            for format, isbn in isbn_list:
+                for template in ISBN_TEMPLATES:
+                    isbn_xml = template % {
+                        'format': format,
+                        'isbn': isbn,
+                        'content_type': CONTENT_TYPES[format],
+                        'url': url_for_format(slug, format),
+                    }
+                    element = etree.XML(isbn_xml)
+                    element.tail = '\n'
+                    desc.append(element)
+            new_head = chunk.commit(
+                etree.tostring(tree, encoding=unicode),
+                author=user,
+                description='automatyczne dodanie isbn'
+            )
+            print 'committed %s' % slug
+            if old_head.publishable:
+                new_head.set_publishable(True)
+            else:
+                print 'Warning: %s not publishable' % slug
diff --git a/src/catalogue/management/commands/mark_final.py b/src/catalogue/management/commands/mark_final.py
new file mode 100644 (file)
index 0000000..cdfaab9
--- /dev/null
@@ -0,0 +1,50 @@
+# -*- coding: utf-8 -*-
+#
+# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
+import sys
+from django.contrib.auth.models import User
+from optparse import make_option
+
+from django.core.management import BaseCommand
+
+from catalogue.models import Book, Chunk
+
+
+class Command(BaseCommand):
+    option_list = BaseCommand.option_list + (
+        # make_option('-q', '--quiet', action='store_false', dest='verbose',
+        #     default=True, help='Less output'),
+        # make_option('-d', '--dry-run', action='store_true', dest='dry_run',
+        #     default=False, help="Don't actually touch anything"),
+        make_option(
+            '-u', '--username', dest='username', metavar='USER',
+            help='Assign commits to this user (required).'),
+    )
+    args = 'slug_file'
+
+    def handle(self, slug_file, **options):
+        username = options.get('username')
+
+        if username:
+            user = User.objects.get(username=username)
+        else:
+            print 'Please provide a username.'
+            sys.exit(1)
+
+        slugs = [line.strip() for line in open(slug_file)]
+        books = Book.objects.filter(slug__in=slugs)
+
+        for book in books:
+            print 'processing %s' % book.slug
+            for chunk in book.chunk_set.all():
+                src = chunk.head.materialize()
+                chunk.commit(
+                    text=src,
+                    author=user,
+                    description=u'Ostateczna akceptacja merytoryczna przez kierownika literackiego.',
+                    tags=[Chunk.tag_model.objects.get(slug='editor-proofreading')],
+                    publishable=True
+                )
+            print 'committed %s' % book.slug
diff --git a/src/catalogue/management/commands/merge_books.py b/src/catalogue/management/commands/merge_books.py
new file mode 100644 (file)
index 0000000..82bd622
--- /dev/null
@@ -0,0 +1,215 @@
+# -*- coding: utf-8 -*-
+
+from optparse import make_option
+import sys
+
+from django.contrib.auth.models import User
+from django.core.management.base import BaseCommand
+from django.core.management.color import color_style
+from django.db import transaction
+
+from catalogue.models import Book
+
+
+def common_prefix(texts):
+    common = []
+
+    min_len = min(len(text) for text in texts)
+    for i in range(min_len):
+        chars = list(set([text[i] for text in texts]))
+        if len(chars) > 1:
+            break
+        common.append(chars[0])
+    return "".join(common)
+
+
+class Command(BaseCommand):
+    option_list = BaseCommand.option_list + (
+        make_option('-s', '--slug', dest='new_slug', metavar='SLUG',
+            help='New slug of the merged book (defaults to common part of all slugs).'),
+        make_option('-t', '--title', dest='new_title', metavar='TITLE',
+            help='New title of the merged book (defaults to common part of all titles).'),
+        make_option('-q', '--quiet', action='store_false', dest='verbose', default=True,
+            help='Less output'),
+        make_option('-g', '--guess', action='store_true', dest='guess', default=False,
+            help='Try to guess what merges are needed (but do not apply them).'),
+        make_option('-d', '--dry-run', action='store_true', dest='dry_run', default=False,
+            help='Dry run: do not actually change anything.'),
+        make_option('-f', '--force', action='store_true', dest='force', default=False,
+            help='On slug conflict, hide the original book to archive.'),
+    )
+    help = 'Merges multiple books into one.'
+    args = '[slug]...'
+
+
+    def print_guess(self, dry_run=True, force=False):
+        from collections import defaultdict
+        from pipes import quote
+        import re
+    
+        def read_slug(slug):
+            res = []
+            res.append((re.compile(ur'__?(przedmowa)$'), -1))
+            res.append((re.compile(ur'__?(cz(esc)?|ksiega|rozdzial)__?(?P<n>\d*)$'), None))
+            res.append((re.compile(ur'__?(rozdzialy__?)?(?P<n>\d*)-'), None))
+        
+            for r, default in res:
+                m = r.search(slug)
+                if m:
+                    start = m.start()
+                    try:
+                        return int(m.group('n')), slug[:start]
+                    except IndexError:
+                        return default, slug[:start]
+            return None, slug
+    
+        def file_to_title(fname):
+            """ Returns a title-like version of a filename. """
+            parts = (p.replace('_', ' ').title() for p in fname.split('__'))
+            return ' / '.join(parts)
+    
+        merges = defaultdict(list)
+        slugs = []
+        for b in Book.objects.all():
+            slugs.append(b.slug)
+            n, ns = read_slug(b.slug)
+            if n is not None:
+                merges[ns].append((n, b))
+    
+        conflicting_slugs = []
+        for slug in sorted(merges.keys()):
+            merge_list = sorted(merges[slug])
+            if len(merge_list) < 2:
+                continue
+    
+            merge_slugs = [b.slug for i, b in merge_list]
+            if slug in slugs and slug not in merge_slugs:
+                conflicting_slugs.append(slug)
+    
+            title = file_to_title(slug)
+            print "./manage.py merge_books %s%s--title=%s --slug=%s \\\n    %s\n" % (
+                '--dry-run ' if dry_run else '',
+                '--force ' if force else '',
+                quote(title), slug,
+                " \\\n    ".join(merge_slugs)
+                )
+    
+        if conflicting_slugs:
+            if force:
+                print self.style.NOTICE('# These books will be archived:')
+            else:
+                print self.style.ERROR('# ERROR: Conflicting slugs:')
+            for slug in conflicting_slugs:
+                print '#', slug
+
+
+    def handle(self, *slugs, **options):
+
+        self.style = color_style()
+
+        force = options.get('force')
+        guess = options.get('guess')
+        dry_run = options.get('dry_run')
+        new_slug = options.get('new_slug').decode('utf-8')
+        new_title = options.get('new_title').decode('utf-8')
+        verbose = options.get('verbose')
+
+        if guess:
+            if slugs:
+                print "Please specify either slugs, or --guess."
+                return
+            else:
+                self.print_guess(dry_run, force)
+                return
+        if not slugs:
+            print "Please specify some book slugs"
+            return
+
+        # Start transaction management.
+        transaction.enter_transaction_management()
+
+        books = [Book.objects.get(slug=slug) for slug in slugs]
+        common_slug = common_prefix(slugs)
+        common_title = common_prefix([b.title for b in books])
+
+        if not new_title:
+            new_title = common_title
+        elif common_title.startswith(new_title):
+            common_title = new_title
+
+        if not new_slug:
+            new_slug = common_slug
+        elif common_slug.startswith(new_slug):
+            common_slug = new_slug
+
+        if slugs[0] != new_slug and Book.objects.filter(slug=new_slug).exists():
+            self.style.ERROR('Book already exists, skipping!')
+
+
+        if dry_run and verbose:
+            print self.style.NOTICE('DRY RUN: nothing will be changed.')
+            print
+
+        if verbose:
+            print "New title:", self.style.NOTICE(new_title)
+            print "New slug:", self.style.NOTICE(new_slug)
+            print
+
+        for i, book in enumerate(books):
+            chunk_titles = []
+            chunk_slugs = []
+
+            book_title = book.title[len(common_title):].replace(' / ', ' ').lstrip()
+            book_slug = book.slug[len(common_slug):].replace('__', '_').lstrip('-_')
+            for j, chunk in enumerate(book):
+                if j:
+                    new_chunk_title = book_title + '_%d' % j
+                    new_chunk_slug = book_slug + '_%d' % j
+                else:
+                    new_chunk_title, new_chunk_slug = book_title, book_slug
+
+                chunk_titles.append(new_chunk_title)
+                chunk_slugs.append(new_chunk_slug)
+
+                if verbose:
+                    print "title: %s // %s  -->\n       %s // %s\nslug: %s / %s  -->\n      %s / %s" % (
+                        book.title, chunk.title,
+                        new_title, new_chunk_title,
+                        book.slug, chunk.slug,
+                        new_slug, new_chunk_slug)
+                    print
+
+            if not dry_run:
+                try:
+                    conflict = Book.objects.get(slug=new_slug)
+                except Book.DoesNotExist:
+                    conflict = None
+                else:
+                    if conflict == books[0]:
+                        conflict = None
+
+                if conflict:
+                    if force:
+                        # FIXME: there still may be a conflict
+                        conflict.slug = '.' + conflict.slug
+                        conflict.save()
+                        print self.style.NOTICE('Book with slug "%s" moved to "%s".' % (new_slug, conflict.slug))
+                    else:
+                        print self.style.ERROR('ERROR: Book with slug "%s" exists.' % new_slug)
+                        return
+
+                if i:
+                    books[0].append(books[i], slugs=chunk_slugs, titles=chunk_titles)
+                else:
+                    book.title = new_title
+                    book.slug = new_slug
+                    book.save()
+                    for j, chunk in enumerate(book):
+                        chunk.title = chunk_titles[j]
+                        chunk.slug = chunk_slugs[j]
+                        chunk.save()
+
+
+        transaction.commit()
+        transaction.leave_transaction_management()
+
diff --git a/src/catalogue/management/commands/prune_audience.py b/src/catalogue/management/commands/prune_audience.py
new file mode 100644 (file)
index 0000000..114a26f
--- /dev/null
@@ -0,0 +1,67 @@
+# -*- coding: utf-8 -*-
+#
+# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
+
+import sys
+from django.contrib.auth.models import User
+from lxml import etree
+from optparse import make_option
+
+from django.core.management import BaseCommand
+
+from catalogue.models import Book
+from librarian import DCNS
+
+
+class Command(BaseCommand):
+    option_list = BaseCommand.option_list + (
+        # make_option('-q', '--quiet', action='store_false', dest='verbose',
+        #     default=True, help='Less output'),
+        # make_option('-d', '--dry-run', action='store_true', dest='dry_run',
+        #     default=False, help="Don't actually touch anything"),
+        make_option(
+            '-u', '--username', dest='username', metavar='USER',
+            help='Assign commits to this user (required, preferably yourself).'),
+    )
+    args = 'exclude_file'
+
+    def handle(self, exclude_file, **options):
+        username = options.get('username')
+
+        if username:
+            user = User.objects.get(username=username)
+        else:
+            print 'Please provide a username.'
+            sys.exit(1)
+
+        excluded_slugs = [line.strip() for line in open(exclude_file, 'rb') if line.strip()]
+        books = Book.objects.exclude(slug__in=excluded_slugs)
+
+        for book in books:
+            if not book.is_published():
+                continue
+            print 'processing %s' % book.slug
+            chunk = book.chunk_set.first()
+            old_head = chunk.head
+            src = old_head.materialize()
+            tree = etree.fromstring(src)
+            audience_nodes = tree.findall('.//' + DCNS("audience"))
+            if not audience_nodes:
+                print '%s has no audience, skipping' % book.slug
+                continue
+
+            for node in audience_nodes:
+                node.getparent().remove(node)
+
+            chunk.commit(
+                etree.tostring(tree, encoding=unicode),
+                author=user,
+                description='automatyczne skasowanie audience',
+                publishable=old_head.publishable
+            )
+            print 'committed %s' % book.slug
+            if not old_head.publishable:
+                print 'Warning: %s not publishable, last head: %s, %s' % (
+                    book.slug, old_head.author.username, old_head.description[:40].replace('\n', ' '))
diff --git a/src/catalogue/managers.py b/src/catalogue/managers.py
new file mode 100644 (file)
index 0000000..a131ce9
--- /dev/null
@@ -0,0 +1,5 @@
+from django.db import models
+
+class VisibleManager(models.Manager):
+    def get_queryset(self):
+        return super(VisibleManager, self).get_queryset().exclude(_hidden=True)
diff --git a/src/catalogue/migrations/0001_initial.py b/src/catalogue/migrations/0001_initial.py
new file mode 100644 (file)
index 0000000..dccd9b7
--- /dev/null
@@ -0,0 +1,240 @@
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+class Migration(SchemaMigration):
+
+    def forwards(self, orm):
+        
+        # Adding model 'Book'
+        db.create_table('catalogue_book', (
+            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('title', self.gf('django.db.models.fields.CharField')(max_length=255, db_index=True)),
+            ('slug', self.gf('django.db.models.fields.SlugField')(unique=True, max_length=128, db_index=True)),
+            ('gallery', self.gf('django.db.models.fields.CharField')(max_length=255, blank=True)),
+            ('parent', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='children', null=True, to=orm['catalogue.Book'])),
+            ('parent_number', self.gf('django.db.models.fields.IntegerField')(db_index=True, null=True, blank=True)),
+            ('_short_html', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
+            ('_single', self.gf('django.db.models.fields.NullBooleanField')(db_index=True, null=True, blank=True)),
+            ('_new_publishable', self.gf('django.db.models.fields.NullBooleanField')(null=True, blank=True)),
+            ('_published', self.gf('django.db.models.fields.NullBooleanField')(null=True, blank=True)),
+        ))
+        db.send_create_signal('catalogue', ['Book'])
+
+        # Adding model 'Chunk'
+        db.create_table('catalogue_chunk', (
+            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('creator', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='created_documents', null=True, to=orm['auth.User'])),
+            ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], null=True, blank=True)),
+            ('book', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['catalogue.Book'])),
+            ('number', self.gf('django.db.models.fields.IntegerField')()),
+            ('slug', self.gf('django.db.models.fields.SlugField')(max_length=50, db_index=True)),
+            ('title', self.gf('django.db.models.fields.CharField')(max_length=255, blank=True)),
+            ('_short_html', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
+            ('_hidden', self.gf('django.db.models.fields.NullBooleanField')(null=True, blank=True)),
+            ('_changed', self.gf('django.db.models.fields.NullBooleanField')(null=True, blank=True)),
+            ('stage', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['catalogue.ChunkTag'], null=True, blank=True)),
+            ('head', self.gf('django.db.models.fields.related.ForeignKey')(default=None, to=orm['catalogue.ChunkChange'], null=True, blank=True)),
+        ))
+        db.send_create_signal('catalogue', ['Chunk'])
+
+        # Adding unique constraint on 'Chunk', fields ['book', 'number']
+        db.create_unique('catalogue_chunk', ['book_id', 'number'])
+
+        # Adding unique constraint on 'Chunk', fields ['book', 'slug']
+        db.create_unique('catalogue_chunk', ['book_id', 'slug'])
+
+        # Adding model 'ChunkTag'
+        db.create_table('catalogue_chunktag', (
+            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('name', self.gf('django.db.models.fields.CharField')(max_length=64)),
+            ('slug', self.gf('django.db.models.fields.SlugField')(db_index=True, max_length=64, unique=True, null=True, blank=True)),
+            ('ordering', self.gf('django.db.models.fields.IntegerField')()),
+        ))
+        db.send_create_signal('catalogue', ['ChunkTag'])
+
+        # Adding model 'ChunkChange'
+        db.create_table('catalogue_chunkchange', (
+            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('author', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], null=True, blank=True)),
+            ('author_name', self.gf('django.db.models.fields.CharField')(max_length=128, null=True, blank=True)),
+            ('author_email', self.gf('django.db.models.fields.CharField')(max_length=128, null=True, blank=True)),
+            ('revision', self.gf('django.db.models.fields.IntegerField')(db_index=True)),
+            ('parent', self.gf('django.db.models.fields.related.ForeignKey')(default=None, related_name='children', null=True, blank=True, to=orm['catalogue.ChunkChange'])),
+            ('merge_parent', self.gf('django.db.models.fields.related.ForeignKey')(default=None, related_name='merge_children', null=True, blank=True, to=orm['catalogue.ChunkChange'])),
+            ('description', self.gf('django.db.models.fields.TextField')(default='', blank=True)),
+            ('created_at', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.now, db_index=True)),
+            ('publishable', self.gf('django.db.models.fields.BooleanField')(default=False)),
+            ('tree', self.gf('django.db.models.fields.related.ForeignKey')(related_name='change_set', to=orm['catalogue.Chunk'])),
+            ('data', self.gf('django.db.models.fields.files.FileField')(max_length=100)),
+        ))
+        db.send_create_signal('catalogue', ['ChunkChange'])
+
+        # Adding unique constraint on 'ChunkChange', fields ['tree', 'revision']
+        db.create_unique('catalogue_chunkchange', ['tree_id', 'revision'])
+
+        # Adding M2M table for field tags on 'ChunkChange'
+        db.create_table('catalogue_chunkchange_tags', (
+            ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+            ('chunkchange', models.ForeignKey(orm['catalogue.chunkchange'], null=False)),
+            ('chunktag', models.ForeignKey(orm['catalogue.chunktag'], null=False))
+        ))
+        db.create_unique('catalogue_chunkchange_tags', ['chunkchange_id', 'chunktag_id'])
+
+        # Adding model 'BookPublishRecord'
+        db.create_table('catalogue_bookpublishrecord', (
+            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('book', self.gf('django.db.models.fields.related.ForeignKey')(related_name='publish_log', to=orm['catalogue.Book'])),
+            ('timestamp', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
+            ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'])),
+        ))
+        db.send_create_signal('catalogue', ['BookPublishRecord'])
+
+        # Adding model 'ChunkPublishRecord'
+        db.create_table('catalogue_chunkpublishrecord', (
+            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('book_record', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['catalogue.BookPublishRecord'])),
+            ('change', self.gf('django.db.models.fields.related.ForeignKey')(related_name='publish_log', to=orm['catalogue.ChunkChange'])),
+        ))
+        db.send_create_signal('catalogue', ['ChunkPublishRecord'])
+
+
+    def backwards(self, orm):
+        
+        # Removing unique constraint on 'ChunkChange', fields ['tree', 'revision']
+        db.delete_unique('catalogue_chunkchange', ['tree_id', 'revision'])
+
+        # Removing unique constraint on 'Chunk', fields ['book', 'slug']
+        db.delete_unique('catalogue_chunk', ['book_id', 'slug'])
+
+        # Removing unique constraint on 'Chunk', fields ['book', 'number']
+        db.delete_unique('catalogue_chunk', ['book_id', 'number'])
+
+        # Deleting model 'Book'
+        db.delete_table('catalogue_book')
+
+        # Deleting model 'Chunk'
+        db.delete_table('catalogue_chunk')
+
+        # Deleting model 'ChunkTag'
+        db.delete_table('catalogue_chunktag')
+
+        # Deleting model 'ChunkChange'
+        db.delete_table('catalogue_chunkchange')
+
+        # Removing M2M table for field tags on 'ChunkChange'
+        db.delete_table('catalogue_chunkchange_tags')
+
+        # Deleting model 'BookPublishRecord'
+        db.delete_table('catalogue_bookpublishrecord')
+
+        # Deleting model 'ChunkPublishRecord'
+        db.delete_table('catalogue_chunkpublishrecord')
+
+
+    models = {
+        'auth.group': {
+            'Meta': {'object_name': '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']", 'symmetrical': 'False', 'blank': 'True'})
+        },
+        'auth.permission': {
+            'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+            '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': {
+            'Meta': {'object_name': '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']", 'symmetrical': 'False', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            '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']", 'symmetrical': 'False', 'blank': 'True'}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+        },
+        'catalogue.book': {
+            'Meta': {'ordering': "['parent_number', 'title']", 'object_name': 'Book'},
+            '_new_publishable': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+            '_published': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+            '_short_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            '_single': ('django.db.models.fields.NullBooleanField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
+            'gallery': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': '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', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '128', 'db_index': 'True'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'})
+        },
+        'catalogue.bookpublishrecord': {
+            'Meta': {'ordering': "['-timestamp']", 'object_name': 'BookPublishRecord'},
+            'book': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publish_log'", 'to': "orm['catalogue.Book']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'timestamp': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+        },
+        'catalogue.chunk': {
+            'Meta': {'ordering': "['number']", 'unique_together': "[['book', 'number'], ['book', 'slug']]", 'object_name': 'Chunk'},
+            '_changed': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+            '_hidden': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+            '_short_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'book': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.Book']"}),
+            'creator': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'created_documents'", 'null': 'True', 'to': "orm['auth.User']"}),
+            'head': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['catalogue.ChunkChange']", 'null': 'True', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'number': ('django.db.models.fields.IntegerField', [], {}),
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'}),
+            'stage': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.ChunkTag']", 'null': 'True', 'blank': 'True'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'})
+        },
+        'catalogue.chunkchange': {
+            'Meta': {'ordering': "('created_at',)", 'unique_together': "(['tree', 'revision'],)", 'object_name': 'ChunkChange'},
+            'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
+            'author_email': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
+            'author_name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
+            'created_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}),
+            'data': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
+            'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'merge_parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'merge_children'", 'null': 'True', 'blank': 'True', 'to': "orm['catalogue.ChunkChange']"}),
+            'parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'children'", 'null': 'True', 'blank': 'True', 'to': "orm['catalogue.ChunkChange']"}),
+            'publishable': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'revision': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
+            'tags': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'change_set'", 'symmetrical': 'False', 'to': "orm['catalogue.ChunkTag']"}),
+            'tree': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'change_set'", 'to': "orm['catalogue.Chunk']"})
+        },
+        'catalogue.chunkpublishrecord': {
+            'Meta': {'object_name': 'ChunkPublishRecord'},
+            'book_record': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.BookPublishRecord']"}),
+            'change': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publish_log'", 'to': "orm['catalogue.ChunkChange']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
+        },
+        'catalogue.chunktag': {
+            'Meta': {'ordering': "['ordering']", 'object_name': 'ChunkTag'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
+            'ordering': ('django.db.models.fields.IntegerField', [], {}),
+            'slug': ('django.db.models.fields.SlugField', [], {'db_index': 'True', 'max_length': '64', 'unique': 'True', 'null': 'True', 'blank': 'True'})
+        },
+        'contenttypes.contenttype': {
+            'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', '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/src/catalogue/migrations/0002_stages.py b/src/catalogue/migrations/0002_stages.py
new file mode 100644 (file)
index 0000000..7155457
--- /dev/null
@@ -0,0 +1,122 @@
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import DataMigration
+from django.db import models
+
+class Migration(DataMigration):
+
+    def forwards(self, orm):
+
+        from django.core.management import call_command
+        call_command("loaddata", "stages.json")
+
+
+    def backwards(self, orm):
+        "Write your backwards methods here."
+
+
+    models = {
+        'auth.group': {
+            'Meta': {'object_name': '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']", 'symmetrical': 'False', 'blank': 'True'})
+        },
+        'auth.permission': {
+            'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+            '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': {
+            'Meta': {'object_name': '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']", 'symmetrical': 'False', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            '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']", 'symmetrical': 'False', 'blank': 'True'}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+        },
+        'catalogue.book': {
+            'Meta': {'ordering': "['parent_number', 'title']", 'object_name': 'Book'},
+            '_new_publishable': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+            '_published': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+            '_short_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            '_single': ('django.db.models.fields.NullBooleanField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
+            'gallery': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': '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', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '128', 'db_index': 'True'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'})
+        },
+        'catalogue.bookpublishrecord': {
+            'Meta': {'ordering': "['-timestamp']", 'object_name': 'BookPublishRecord'},
+            'book': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publish_log'", 'to': "orm['catalogue.Book']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'timestamp': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+        },
+        'catalogue.chunk': {
+            'Meta': {'ordering': "['number']", 'unique_together': "[['book', 'number'], ['book', 'slug']]", 'object_name': 'Chunk'},
+            '_changed': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+            '_hidden': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+            '_short_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'book': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.Book']"}),
+            'creator': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'created_documents'", 'null': 'True', 'to': "orm['auth.User']"}),
+            'head': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['catalogue.ChunkChange']", 'null': 'True', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'number': ('django.db.models.fields.IntegerField', [], {}),
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'}),
+            'stage': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.ChunkTag']", 'null': 'True', 'blank': 'True'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'})
+        },
+        'catalogue.chunkchange': {
+            'Meta': {'ordering': "('created_at',)", 'unique_together': "(['tree', 'revision'],)", 'object_name': 'ChunkChange'},
+            'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
+            'author_email': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
+            'author_name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
+            'created_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}),
+            'data': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
+            'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'merge_parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'merge_children'", 'null': 'True', 'blank': 'True', 'to': "orm['catalogue.ChunkChange']"}),
+            'parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'children'", 'null': 'True', 'blank': 'True', 'to': "orm['catalogue.ChunkChange']"}),
+            'publishable': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'revision': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
+            'tags': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'change_set'", 'symmetrical': 'False', 'to': "orm['catalogue.ChunkTag']"}),
+            'tree': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'change_set'", 'to': "orm['catalogue.Chunk']"})
+        },
+        'catalogue.chunkpublishrecord': {
+            'Meta': {'object_name': 'ChunkPublishRecord'},
+            'book_record': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.BookPublishRecord']"}),
+            'change': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publish_log'", 'to': "orm['catalogue.ChunkChange']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
+        },
+        'catalogue.chunktag': {
+            'Meta': {'ordering': "['ordering']", 'object_name': 'ChunkTag'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
+            'ordering': ('django.db.models.fields.IntegerField', [], {}),
+            'slug': ('django.db.models.fields.SlugField', [], {'db_index': 'True', 'max_length': '64', 'unique': 'True', 'null': 'True', 'blank': 'True'})
+        },
+        'contenttypes.contenttype': {
+            'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', '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/src/catalogue/migrations/0003_from_hg.py b/src/catalogue/migrations/0003_from_hg.py
new file mode 100644 (file)
index 0000000..e542d50
--- /dev/null
@@ -0,0 +1,281 @@
+# encoding: utf-8
+import datetime
+from zlib import compress
+import os
+import os.path
+import re
+import urllib
+
+from django.db import models
+from south.db import db
+from south.v2 import DataMigration
+
+from django.conf import settings
+from slugify import slugify
+
+META_REGEX = re.compile(r'\s*<!--\s(.*?)-->', re.DOTALL | re.MULTILINE)
+STAGE_TAGS_RE = re.compile(r'^#stage-finished: (.*)$', re.MULTILINE)
+AUTHOR_RE = re.compile(r'\s*(.*?)\s*<(.*)>\s*')
+
+
+def urlunquote(url):
+    """Unqotes URL
+
+    # >>> urlunquote('Za%C5%BC%C3%B3%C5%82%C4%87_g%C4%99%C5%9Bl%C4%85_ja%C5%BA%C5%84')
+    # u'Za\u017c\xf3\u0142\u0107_g\u0119\u015bl\u0105 ja\u017a\u0144'
+    """
+    return unicode(urllib.unquote(url), 'utf-8', 'ignore')
+
+
+def split_name(name):
+    parts = name.split('__')
+    return parts
+
+
+def file_to_title(fname):
+    """ Returns a title-like version of a filename. """
+    parts = (p.replace('_', ' ').title() for p in fname.split('__'))
+    return ' / '.join(parts)
+
+
+def plain_text(text):
+    return re.sub(META_REGEX, '', text, 1)
+
+
+def gallery(slug, text):
+    result = {}
+
+    m = re.match(META_REGEX, text)
+    if m:
+        for line in m.group(1).split('\n'):
+            try:
+                k, v = line.split(':', 1)
+                result[k.strip()] = v.strip()
+            except ValueError:
+                continue
+
+    gallery = result.get('gallery', slugify(slug))
+
+    if gallery.startswith('/'):
+        gallery = os.path.basename(gallery)
+
+    return gallery
+
+
+def migrate_file_from_hg(orm, fname, entry):
+    fname = urlunquote(fname)
+    print fname
+    if fname.endswith('.xml'):
+        fname = fname[:-4]
+    title = file_to_title(fname)
+    fname = slugify(fname)
+
+    # create all the needed objects
+    # what if it already exists?
+    book = orm.Book.objects.create(
+        title=title,
+        slug=fname)
+    chunk = orm.Chunk.objects.create(
+        book=book,
+        number=1,
+        slug='1')
+    try:
+        chunk.stage = orm.ChunkTag.objects.order_by('ordering')[0]
+    except IndexError:
+        chunk.stage = None
+
+    maxrev = entry.filerev()
+    gallery_link = None
+
+    # this will fail if directory exists
+    os.makedirs(os.path.join(settings.CATALOGUE_REPO_PATH, str(chunk.pk)))
+
+    for rev in xrange(maxrev + 1):
+        fctx = entry.filectx(rev)
+        data = fctx.data()
+        gallery_link = gallery(fname, data)
+        data = plain_text(data)
+
+        # get tags from description
+        description = fctx.description().decode("utf-8", 'replace')
+        tags = STAGE_TAGS_RE.findall(description)
+        tags = [orm.ChunkTag.objects.get(slug=slug.strip()) for slug in tags]
+
+        if tags:
+            max_ordering = max(tags, key=lambda x: x.ordering).ordering
+            try:
+                chunk.stage = orm.ChunkTag.objects.filter(ordering__gt=max_ordering).order_by('ordering')[0]
+            except IndexError:
+                chunk.stage = None
+
+        description = STAGE_TAGS_RE.sub('', description)
+
+        author = author_name = author_email = None
+        author_desc = fctx.user().decode("utf-8", 'replace')
+        m = AUTHOR_RE.match(author_desc)
+        if m:
+            try:
+                author = orm['auth.User'].objects.get(username=m.group(1), email=m.group(2))
+            except orm['auth.User'].DoesNotExist:
+                author_name = m.group(1)
+                author_email = m.group(2)
+        else:
+            author_name = author_desc
+
+        head = orm.ChunkChange.objects.create(
+            tree=chunk,
+            revision=rev + 1,
+            created_at=datetime.datetime.fromtimestamp(fctx.date()[0]),
+            description=description,
+            author=author,
+            author_name=author_name,
+            author_email=author_email,
+            parent=chunk.head
+            )
+
+        path = "%d/%d" % (chunk.pk, head.pk)
+        abs_path = os.path.join(settings.CATALOGUE_REPO_PATH, path)
+        f = open(abs_path, 'wb')
+        f.write(compress(data))
+        f.close()
+        head.data = path
+
+        head.tags = tags
+        head.save()
+
+        chunk.head = head
+
+    chunk.save()
+    if gallery_link:
+        book.gallery = gallery_link
+        book.save()
+
+
+class Migration(DataMigration):
+
+    def forwards(self, orm):
+        try:
+            hg_path = settings.WIKI_REPOSITORY_PATH
+        except:
+            print 'repository not configured, skipping'
+        else:
+            from mercurial import hg, ui
+
+            print 'migrate from', hg_path
+            repo = hg.repository(ui.ui(), hg_path)
+            tip = repo['tip']
+            for fname in tip:
+                if fname.startswith('.'):
+                    continue
+                migrate_file_from_hg(orm, fname, tip[fname])
+
+
+    def backwards(self, orm):
+        "Write your backwards methods here."
+        pass
+
+
+    models = {
+        'auth.group': {
+            'Meta': {'object_name': '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']", 'symmetrical': 'False', 'blank': 'True'})
+        },
+        'auth.permission': {
+            'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+            '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': {
+            'Meta': {'object_name': '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']", 'symmetrical': 'False', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            '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']", 'symmetrical': 'False', 'blank': 'True'}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+        },
+        'catalogue.book': {
+            'Meta': {'ordering': "['parent_number', 'title']", 'object_name': 'Book'},
+            '_new_publishable': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+            '_published': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+            '_short_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            '_single': ('django.db.models.fields.NullBooleanField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
+            'gallery': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': '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', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '128', 'db_index': 'True'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'})
+        },
+        'catalogue.bookpublishrecord': {
+            'Meta': {'ordering': "['-timestamp']", 'object_name': 'BookPublishRecord'},
+            'book': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publish_log'", 'to': "orm['catalogue.Book']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'timestamp': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+        },
+        'catalogue.chunk': {
+            'Meta': {'ordering': "['number']", 'unique_together': "[['book', 'number'], ['book', 'slug']]", 'object_name': 'Chunk'},
+            '_changed': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+            '_hidden': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+            '_short_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'book': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.Book']"}),
+            'creator': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'created_documents'", 'null': 'True', 'to': "orm['auth.User']"}),
+            'head': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['catalogue.ChunkChange']", 'null': 'True', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'number': ('django.db.models.fields.IntegerField', [], {}),
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'}),
+            'stage': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.ChunkTag']", 'null': 'True', 'blank': 'True'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'})
+        },
+        'catalogue.chunkchange': {
+            'Meta': {'ordering': "('created_at',)", 'unique_together': "(['tree', 'revision'],)", 'object_name': 'ChunkChange'},
+            'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
+            'author_email': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
+            'author_name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
+            'created_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}),
+            'data': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
+            'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'merge_parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'merge_children'", 'null': 'True', 'blank': 'True', 'to': "orm['catalogue.ChunkChange']"}),
+            'parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'children'", 'null': 'True', 'blank': 'True', 'to': "orm['catalogue.ChunkChange']"}),
+            'publishable': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'revision': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
+            'tags': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'change_set'", 'symmetrical': 'False', 'to': "orm['catalogue.ChunkTag']"}),
+            'tree': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'change_set'", 'to': "orm['catalogue.Chunk']"})
+        },
+        'catalogue.chunkpublishrecord': {
+            'Meta': {'object_name': 'ChunkPublishRecord'},
+            'book_record': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.BookPublishRecord']"}),
+            'change': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publish_log'", 'to': "orm['catalogue.ChunkChange']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
+        },
+        'catalogue.chunktag': {
+            'Meta': {'ordering': "['ordering']", 'object_name': 'ChunkTag'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
+            'ordering': ('django.db.models.fields.IntegerField', [], {}),
+            'slug': ('django.db.models.fields.SlugField', [], {'db_index': 'True', 'max_length': '64', 'unique': 'True', 'null': 'True', 'blank': 'True'})
+        },
+        'contenttypes.contenttype': {
+            'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', '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/src/catalogue/migrations/0004_fix_revisions.py b/src/catalogue/migrations/0004_fix_revisions.py
new file mode 100644 (file)
index 0000000..fe5c86b
--- /dev/null
@@ -0,0 +1,125 @@
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import DataMigration
+from django.db import models
+
+class Migration(DataMigration):
+
+    def forwards(self, orm):
+        "Make sure all revisions start with 1, not 0."
+        for zero_commit in orm.ChunkChange.objects.filter(revision=0):
+            for change in zero_commit.tree.change_set.all().order_by('-revision'):
+                change.revision=models.F('revision') + 1
+                change.save()
+
+
+    def backwards(self, orm):
+        "Write your backwards methods here."
+        pass
+
+
+    models = {
+        'auth.group': {
+            'Meta': {'object_name': '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']", 'symmetrical': 'False', 'blank': 'True'})
+        },
+        'auth.permission': {
+            'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+            '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': {
+            'Meta': {'object_name': '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']", 'symmetrical': 'False', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            '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']", 'symmetrical': 'False', 'blank': 'True'}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+        },
+        'catalogue.book': {
+            'Meta': {'ordering': "['parent_number', 'title']", 'object_name': 'Book'},
+            '_new_publishable': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+            '_published': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+            '_short_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            '_single': ('django.db.models.fields.NullBooleanField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
+            'gallery': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': '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', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '128', 'db_index': 'True'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'})
+        },
+        'catalogue.bookpublishrecord': {
+            'Meta': {'ordering': "['-timestamp']", 'object_name': 'BookPublishRecord'},
+            'book': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publish_log'", 'to': "orm['catalogue.Book']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'timestamp': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+        },
+        'catalogue.chunk': {
+            'Meta': {'ordering': "['number']", 'unique_together': "[['book', 'number'], ['book', 'slug']]", 'object_name': 'Chunk'},
+            '_changed': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+            '_hidden': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+            '_short_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'book': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.Book']"}),
+            'creator': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'created_chunk'", 'null': 'True', 'to': "orm['auth.User']"}),
+            'head': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['catalogue.ChunkChange']", 'null': 'True', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'number': ('django.db.models.fields.IntegerField', [], {}),
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'}),
+            'stage': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.ChunkTag']", 'null': 'True', 'blank': 'True'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'})
+        },
+        'catalogue.chunkchange': {
+            'Meta': {'ordering': "('created_at',)", 'unique_together': "(['tree', 'revision'],)", 'object_name': 'ChunkChange'},
+            'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
+            'author_email': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
+            'author_name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
+            'created_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}),
+            'data': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
+            'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'merge_parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'merge_children'", 'null': 'True', 'blank': 'True', 'to': "orm['catalogue.ChunkChange']"}),
+            'parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'children'", 'null': 'True', 'blank': 'True', 'to': "orm['catalogue.ChunkChange']"}),
+            'publishable': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'revision': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
+            'tags': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'change_set'", 'symmetrical': 'False', 'to': "orm['catalogue.ChunkTag']"}),
+            'tree': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'change_set'", 'to': "orm['catalogue.Chunk']"})
+        },
+        'catalogue.chunkpublishrecord': {
+            'Meta': {'object_name': 'ChunkPublishRecord'},
+            'book_record': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.BookPublishRecord']"}),
+            'change': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publish_log'", 'to': "orm['catalogue.ChunkChange']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
+        },
+        'catalogue.chunktag': {
+            'Meta': {'ordering': "['ordering']", 'object_name': 'ChunkTag'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
+            'ordering': ('django.db.models.fields.IntegerField', [], {}),
+            'slug': ('django.db.models.fields.SlugField', [], {'db_index': 'True', 'max_length': '64', 'unique': 'True', 'null': 'True', 'blank': 'True'})
+        },
+        'contenttypes.contenttype': {
+            'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', '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/src/catalogue/migrations/0005_auto__add_field_chunk_gallery_start.py b/src/catalogue/migrations/0005_auto__add_field_chunk_gallery_start.py
new file mode 100644 (file)
index 0000000..71af5f6
--- /dev/null
@@ -0,0 +1,125 @@
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+class Migration(SchemaMigration):
+
+    def forwards(self, orm):
+        
+        # Adding field 'Chunk.gallery_start'
+        db.add_column('catalogue_chunk', 'gallery_start', self.gf('django.db.models.fields.IntegerField')(null=True, blank=True), keep_default=False)
+
+
+    def backwards(self, orm):
+        
+        # Deleting field 'Chunk.gallery_start'
+        db.delete_column('catalogue_chunk', 'gallery_start')
+
+
+    models = {
+        'auth.group': {
+            'Meta': {'object_name': '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']", 'symmetrical': 'False', 'blank': 'True'})
+        },
+        'auth.permission': {
+            'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+            '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': {
+            'Meta': {'object_name': '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']", 'symmetrical': 'False', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            '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']", 'symmetrical': 'False', 'blank': 'True'}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+        },
+        'catalogue.book': {
+            'Meta': {'ordering': "['parent_number', 'title']", 'object_name': 'Book'},
+            '_new_publishable': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+            '_published': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+            '_short_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            '_single': ('django.db.models.fields.NullBooleanField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
+            'gallery': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': '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', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '128', 'db_index': 'True'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'})
+        },
+        'catalogue.bookpublishrecord': {
+            'Meta': {'ordering': "['-timestamp']", 'object_name': 'BookPublishRecord'},
+            'book': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publish_log'", 'to': "orm['catalogue.Book']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'timestamp': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+        },
+        'catalogue.chunk': {
+            'Meta': {'ordering': "['number']", 'unique_together': "[['book', 'number'], ['book', 'slug']]", 'object_name': 'Chunk'},
+            '_changed': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+            '_hidden': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+            '_short_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'book': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.Book']"}),
+            'creator': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'created_chunk'", 'null': 'True', 'to': "orm['auth.User']"}),
+            'gallery_start': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+            'head': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['catalogue.ChunkChange']", 'null': 'True', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'number': ('django.db.models.fields.IntegerField', [], {}),
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'}),
+            'stage': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.ChunkTag']", 'null': 'True', 'blank': 'True'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'})
+        },
+        'catalogue.chunkchange': {
+            'Meta': {'ordering': "('created_at',)", 'unique_together': "(['tree', 'revision'],)", 'object_name': 'ChunkChange'},
+            'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
+            'author_email': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
+            'author_name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
+            'created_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}),
+            'data': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
+            'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'merge_parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'merge_children'", 'null': 'True', 'blank': 'True', 'to': "orm['catalogue.ChunkChange']"}),
+            'parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'children'", 'null': 'True', 'blank': 'True', 'to': "orm['catalogue.ChunkChange']"}),
+            'publishable': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'revision': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
+            'tags': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'change_set'", 'symmetrical': 'False', 'to': "orm['catalogue.ChunkTag']"}),
+            'tree': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'change_set'", 'to': "orm['catalogue.Chunk']"})
+        },
+        'catalogue.chunkpublishrecord': {
+            'Meta': {'object_name': 'ChunkPublishRecord'},
+            'book_record': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.BookPublishRecord']"}),
+            'change': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publish_log'", 'to': "orm['catalogue.ChunkChange']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
+        },
+        'catalogue.chunktag': {
+            'Meta': {'ordering': "['ordering']", 'object_name': 'ChunkTag'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
+            'ordering': ('django.db.models.fields.IntegerField', [], {}),
+            'slug': ('django.db.models.fields.SlugField', [], {'db_index': 'True', 'max_length': '64', 'unique': 'True', 'null': 'True', 'blank': 'True'})
+        },
+        'contenttypes.contenttype': {
+            'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', '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/src/catalogue/migrations/0006_auto__add_field_book_public.py b/src/catalogue/migrations/0006_auto__add_field_book_public.py
new file mode 100644 (file)
index 0000000..fd1cea5
--- /dev/null
@@ -0,0 +1,126 @@
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+class Migration(SchemaMigration):
+
+    def forwards(self, orm):
+        
+        # Adding field 'Book.public'
+        db.add_column('catalogue_book', 'public', self.gf('django.db.models.fields.BooleanField')(default=True, db_index=True), keep_default=False)
+
+
+    def backwards(self, orm):
+        
+        # Deleting field 'Book.public'
+        db.delete_column('catalogue_book', 'public')
+
+
+    models = {
+        'auth.group': {
+            'Meta': {'object_name': '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']", 'symmetrical': 'False', 'blank': 'True'})
+        },
+        'auth.permission': {
+            'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+            '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': {
+            'Meta': {'object_name': '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']", 'symmetrical': 'False', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            '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']", 'symmetrical': 'False', 'blank': 'True'}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+        },
+        'catalogue.book': {
+            'Meta': {'ordering': "['title', 'slug']", 'object_name': 'Book'},
+            '_new_publishable': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+            '_published': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+            '_short_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            '_single': ('django.db.models.fields.NullBooleanField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
+            'gallery': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': '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', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
+            'public': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '128', 'db_index': 'True'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'})
+        },
+        'catalogue.bookpublishrecord': {
+            'Meta': {'ordering': "['-timestamp']", 'object_name': 'BookPublishRecord'},
+            'book': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publish_log'", 'to': "orm['catalogue.Book']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'timestamp': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+        },
+        'catalogue.chunk': {
+            'Meta': {'ordering': "['number']", 'unique_together': "[['book', 'number'], ['book', 'slug']]", 'object_name': 'Chunk'},
+            '_changed': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+            '_hidden': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+            '_short_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'book': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.Book']"}),
+            'creator': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'created_chunk'", 'null': 'True', 'to': "orm['auth.User']"}),
+            'gallery_start': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+            'head': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['catalogue.ChunkChange']", 'null': 'True', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'number': ('django.db.models.fields.IntegerField', [], {}),
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'}),
+            'stage': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.ChunkTag']", 'null': 'True', 'blank': 'True'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'})
+        },
+        'catalogue.chunkchange': {
+            'Meta': {'ordering': "('created_at',)", 'unique_together': "(['tree', 'revision'],)", 'object_name': 'ChunkChange'},
+            'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
+            'author_email': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
+            'author_name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
+            'created_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}),
+            'data': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
+            'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'merge_parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'merge_children'", 'null': 'True', 'blank': 'True', 'to': "orm['catalogue.ChunkChange']"}),
+            'parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'children'", 'null': 'True', 'blank': 'True', 'to': "orm['catalogue.ChunkChange']"}),
+            'publishable': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'revision': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
+            'tags': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'change_set'", 'symmetrical': 'False', 'to': "orm['catalogue.ChunkTag']"}),
+            'tree': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'change_set'", 'to': "orm['catalogue.Chunk']"})
+        },
+        'catalogue.chunkpublishrecord': {
+            'Meta': {'object_name': 'ChunkPublishRecord'},
+            'book_record': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.BookPublishRecord']"}),
+            'change': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publish_log'", 'to': "orm['catalogue.ChunkChange']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
+        },
+        'catalogue.chunktag': {
+            'Meta': {'ordering': "['ordering']", 'object_name': 'ChunkTag'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
+            'ordering': ('django.db.models.fields.IntegerField', [], {}),
+            'slug': ('django.db.models.fields.SlugField', [], {'db_index': 'True', 'max_length': '64', 'unique': 'True', 'null': 'True', 'blank': 'True'})
+        },
+        'contenttypes.contenttype': {
+            'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', '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/src/catalogue/migrations/0007_auto__add_field_book_dc_slug.py b/src/catalogue/migrations/0007_auto__add_field_book_dc_slug.py
new file mode 100644 (file)
index 0000000..5ae20ea
--- /dev/null
@@ -0,0 +1,127 @@
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+class Migration(SchemaMigration):
+
+    def forwards(self, orm):
+        
+        # Adding field 'Book.dc_slug'
+        db.add_column('catalogue_book', 'dc_slug', self.gf('django.db.models.fields.CharField')(max_length=128, null=True, blank=True), keep_default=False)
+
+
+    def backwards(self, orm):
+        
+        # Deleting field 'Book.dc_slug'
+        db.delete_column('catalogue_book', 'dc_slug')
+
+
+    models = {
+        'auth.group': {
+            'Meta': {'object_name': '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']", 'symmetrical': 'False', 'blank': 'True'})
+        },
+        'auth.permission': {
+            'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+            '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': {
+            'Meta': {'object_name': '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']", 'symmetrical': 'False', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            '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']", 'symmetrical': 'False', 'blank': 'True'}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+        },
+        'catalogue.book': {
+            'Meta': {'ordering': "['title', 'slug']", 'object_name': 'Book'},
+            '_new_publishable': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+            '_published': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+            '_short_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            '_single': ('django.db.models.fields.NullBooleanField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
+            'dc_slug': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
+            'gallery': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': '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', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
+            'public': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '128', 'db_index': 'True'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'})
+        },
+        'catalogue.bookpublishrecord': {
+            'Meta': {'ordering': "['-timestamp']", 'object_name': 'BookPublishRecord'},
+            'book': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publish_log'", 'to': "orm['catalogue.Book']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'timestamp': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+        },
+        'catalogue.chunk': {
+            'Meta': {'ordering': "['number']", 'unique_together': "[['book', 'number'], ['book', 'slug']]", 'object_name': 'Chunk'},
+            '_changed': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+            '_hidden': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+            '_short_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'book': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.Book']"}),
+            'creator': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'created_chunk'", 'null': 'True', 'to': "orm['auth.User']"}),
+            'gallery_start': ('django.db.models.fields.IntegerField', [], {'default': '1', 'null': 'True', 'blank': 'True'}),
+            'head': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['catalogue.ChunkChange']", 'null': 'True', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'number': ('django.db.models.fields.IntegerField', [], {}),
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'}),
+            'stage': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.ChunkTag']", 'null': 'True', 'blank': 'True'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'})
+        },
+        'catalogue.chunkchange': {
+            'Meta': {'ordering': "('created_at',)", 'unique_together': "(['tree', 'revision'],)", 'object_name': 'ChunkChange'},
+            'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
+            'author_email': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
+            'author_name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
+            'created_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}),
+            'data': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
+            'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'merge_parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'merge_children'", 'null': 'True', 'blank': 'True', 'to': "orm['catalogue.ChunkChange']"}),
+            'parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'children'", 'null': 'True', 'blank': 'True', 'to': "orm['catalogue.ChunkChange']"}),
+            'publishable': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'revision': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
+            'tags': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'change_set'", 'symmetrical': 'False', 'to': "orm['catalogue.ChunkTag']"}),
+            'tree': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'change_set'", 'to': "orm['catalogue.Chunk']"})
+        },
+        'catalogue.chunkpublishrecord': {
+            'Meta': {'object_name': 'ChunkPublishRecord'},
+            'book_record': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.BookPublishRecord']"}),
+            'change': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publish_log'", 'to': "orm['catalogue.ChunkChange']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
+        },
+        'catalogue.chunktag': {
+            'Meta': {'ordering': "['ordering']", 'object_name': 'ChunkTag'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
+            'ordering': ('django.db.models.fields.IntegerField', [], {}),
+            'slug': ('django.db.models.fields.SlugField', [], {'db_index': 'True', 'max_length': '64', 'unique': 'True', 'null': 'True', 'blank': 'True'})
+        },
+        'contenttypes.contenttype': {
+            'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', '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/src/catalogue/migrations/0008_auto.py b/src/catalogue/migrations/0008_auto.py
new file mode 100644 (file)
index 0000000..5276b27
--- /dev/null
@@ -0,0 +1,127 @@
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+class Migration(SchemaMigration):
+
+    def forwards(self, orm):
+        
+        # Adding index on 'Book', fields ['dc_slug']
+        db.create_index('catalogue_book', ['dc_slug'])
+
+
+    def backwards(self, orm):
+        
+        # Removing index on 'Book', fields ['dc_slug']
+        db.delete_index('catalogue_book', ['dc_slug'])
+
+
+    models = {
+        'auth.group': {
+            'Meta': {'object_name': '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']", 'symmetrical': 'False', 'blank': 'True'})
+        },
+        'auth.permission': {
+            'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+            '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': {
+            'Meta': {'object_name': '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']", 'symmetrical': 'False', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            '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']", 'symmetrical': 'False', 'blank': 'True'}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+        },
+        'catalogue.book': {
+            'Meta': {'ordering': "['title', 'slug']", 'object_name': 'Book'},
+            '_new_publishable': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+            '_published': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+            '_short_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            '_single': ('django.db.models.fields.NullBooleanField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
+            'dc_slug': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '128', 'null': 'True', 'blank': 'True'}),
+            'gallery': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': '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', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
+            'public': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '128', 'db_index': 'True'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'})
+        },
+        'catalogue.bookpublishrecord': {
+            'Meta': {'ordering': "['-timestamp']", 'object_name': 'BookPublishRecord'},
+            'book': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publish_log'", 'to': "orm['catalogue.Book']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'timestamp': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+        },
+        'catalogue.chunk': {
+            'Meta': {'ordering': "['number']", 'unique_together': "[['book', 'number'], ['book', 'slug']]", 'object_name': 'Chunk'},
+            '_changed': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+            '_hidden': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+            '_short_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'book': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.Book']"}),
+            'creator': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'created_chunk'", 'null': 'True', 'to': "orm['auth.User']"}),
+            'gallery_start': ('django.db.models.fields.IntegerField', [], {'default': '1', 'null': 'True', 'blank': 'True'}),
+            'head': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['catalogue.ChunkChange']", 'null': 'True', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'number': ('django.db.models.fields.IntegerField', [], {}),
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'}),
+            'stage': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.ChunkTag']", 'null': 'True', 'blank': 'True'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'})
+        },
+        'catalogue.chunkchange': {
+            'Meta': {'ordering': "('created_at',)", 'unique_together': "(['tree', 'revision'],)", 'object_name': 'ChunkChange'},
+            'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
+            'author_email': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
+            'author_name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
+            'created_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}),
+            'data': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
+            'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'merge_parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'merge_children'", 'null': 'True', 'blank': 'True', 'to': "orm['catalogue.ChunkChange']"}),
+            'parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'children'", 'null': 'True', 'blank': 'True', 'to': "orm['catalogue.ChunkChange']"}),
+            'publishable': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'revision': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
+            'tags': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'change_set'", 'symmetrical': 'False', 'to': "orm['catalogue.ChunkTag']"}),
+            'tree': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'change_set'", 'to': "orm['catalogue.Chunk']"})
+        },
+        'catalogue.chunkpublishrecord': {
+            'Meta': {'object_name': 'ChunkPublishRecord'},
+            'book_record': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.BookPublishRecord']"}),
+            'change': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publish_log'", 'to': "orm['catalogue.ChunkChange']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
+        },
+        'catalogue.chunktag': {
+            'Meta': {'ordering': "['ordering']", 'object_name': 'ChunkTag'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
+            'ordering': ('django.db.models.fields.IntegerField', [], {}),
+            'slug': ('django.db.models.fields.SlugField', [], {'db_index': 'True', 'max_length': '64', 'unique': 'True', 'null': 'True', 'blank': 'True'})
+        },
+        'contenttypes.contenttype': {
+            'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', '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/src/catalogue/migrations/0009_auto__add_field_book__on_track.py b/src/catalogue/migrations/0009_auto__add_field_book__on_track.py
new file mode 100644 (file)
index 0000000..f0509c4
--- /dev/null
@@ -0,0 +1,128 @@
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+class Migration(SchemaMigration):
+
+    def forwards(self, orm):
+        
+        # Adding field 'Book._on_track'
+        db.add_column('catalogue_book', '_on_track', self.gf('django.db.models.fields.IntegerField')(db_index=True, null=True, blank=True), keep_default=False)
+
+
+    def backwards(self, orm):
+        
+        # Deleting field 'Book._on_track'
+        db.delete_column('catalogue_book', '_on_track')
+
+
+    models = {
+        'auth.group': {
+            'Meta': {'object_name': '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']", 'symmetrical': 'False', 'blank': 'True'})
+        },
+        'auth.permission': {
+            'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+            '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': {
+            'Meta': {'object_name': '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']", 'symmetrical': 'False', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            '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']", 'symmetrical': 'False', 'blank': 'True'}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+        },
+        'catalogue.book': {
+            'Meta': {'ordering': "['title', 'slug']", 'object_name': 'Book'},
+            '_new_publishable': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+            '_on_track': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
+            '_published': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+            '_short_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            '_single': ('django.db.models.fields.NullBooleanField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
+            'dc_slug': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '128', 'null': 'True', 'blank': 'True'}),
+            'gallery': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': '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', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
+            'public': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '128', 'db_index': 'True'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'})
+        },
+        'catalogue.bookpublishrecord': {
+            'Meta': {'ordering': "['-timestamp']", 'object_name': 'BookPublishRecord'},
+            'book': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publish_log'", 'to': "orm['catalogue.Book']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'timestamp': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+        },
+        'catalogue.chunk': {
+            'Meta': {'ordering': "['number']", 'unique_together': "[['book', 'number'], ['book', 'slug']]", 'object_name': 'Chunk'},
+            '_changed': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+            '_hidden': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+            '_short_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'book': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.Book']"}),
+            'creator': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'created_chunk'", 'null': 'True', 'to': "orm['auth.User']"}),
+            'gallery_start': ('django.db.models.fields.IntegerField', [], {'default': '1', 'null': 'True', 'blank': 'True'}),
+            'head': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['catalogue.ChunkChange']", 'null': 'True', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'number': ('django.db.models.fields.IntegerField', [], {}),
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'}),
+            'stage': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.ChunkTag']", 'null': 'True', 'blank': 'True'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'})
+        },
+        'catalogue.chunkchange': {
+            'Meta': {'ordering': "('created_at',)", 'unique_together': "(['tree', 'revision'],)", 'object_name': 'ChunkChange'},
+            'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
+            'author_email': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
+            'author_name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
+            'created_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}),
+            'data': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
+            'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'merge_parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'merge_children'", 'null': 'True', 'blank': 'True', 'to': "orm['catalogue.ChunkChange']"}),
+            'parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'children'", 'null': 'True', 'blank': 'True', 'to': "orm['catalogue.ChunkChange']"}),
+            'publishable': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'revision': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
+            'tags': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'change_set'", 'symmetrical': 'False', 'to': "orm['catalogue.ChunkTag']"}),
+            'tree': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'change_set'", 'to': "orm['catalogue.Chunk']"})
+        },
+        'catalogue.chunkpublishrecord': {
+            'Meta': {'object_name': 'ChunkPublishRecord'},
+            'book_record': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.BookPublishRecord']"}),
+            'change': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publish_log'", 'to': "orm['catalogue.ChunkChange']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
+        },
+        'catalogue.chunktag': {
+            'Meta': {'ordering': "['ordering']", 'object_name': 'ChunkTag'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
+            'ordering': ('django.db.models.fields.IntegerField', [], {}),
+            'slug': ('django.db.models.fields.SlugField', [], {'db_index': 'True', 'max_length': '64', 'unique': 'True', 'null': 'True', 'blank': 'True'})
+        },
+        'contenttypes.contenttype': {
+            'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', '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/src/catalogue/migrations/0010_auto__add_field_book_dc_cover_image.py b/src/catalogue/migrations/0010_auto__add_field_book_dc_cover_image.py
new file mode 100644 (file)
index 0000000..aebbed9
--- /dev/null
@@ -0,0 +1,144 @@
+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+    depends_on = (
+        ("cover", "0001_initial"),
+    )
+
+    def forwards(self, orm):
+        # Adding field 'Book.dc_cover_image'
+        db.add_column('catalogue_book', 'dc_cover_image',
+                      self.gf('django.db.models.fields.related.ForeignKey')(to=orm['cover.Image'], null=True, on_delete=models.SET_NULL, blank=True),
+                      keep_default=False)
+
+
+    def backwards(self, orm):
+        # Deleting field 'Book.dc_cover_image'
+        db.delete_column('catalogue_book', 'dc_cover_image_id')
+
+
+    models = {
+        'auth.group': {
+            'Meta': {'object_name': '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']", 'symmetrical': 'False', 'blank': 'True'})
+        },
+        'auth.permission': {
+            'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+            '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': {
+            'Meta': {'object_name': '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']", 'symmetrical': 'False', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            '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']", 'symmetrical': 'False', 'blank': 'True'}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+        },
+        'catalogue.book': {
+            'Meta': {'ordering': "['title', 'slug']", 'object_name': 'Book'},
+            '_new_publishable': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+            '_on_track': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
+            '_published': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+            '_short_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            '_single': ('django.db.models.fields.NullBooleanField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
+            'dc_cover_image': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cover.Image']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'dc_slug': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '128', 'null': 'True', 'blank': 'True'}),
+            'gallery': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': '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', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
+            'public': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '128'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'})
+        },
+        'catalogue.bookpublishrecord': {
+            'Meta': {'ordering': "['-timestamp']", 'object_name': 'BookPublishRecord'},
+            'book': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publish_log'", 'to': "orm['catalogue.Book']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'timestamp': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+        },
+        'catalogue.chunk': {
+            'Meta': {'ordering': "['number']", 'unique_together': "[['book', 'number'], ['book', 'slug']]", 'object_name': 'Chunk'},
+            '_changed': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+            '_hidden': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+            '_short_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'book': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.Book']"}),
+            'creator': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'created_chunk'", 'null': 'True', 'to': "orm['auth.User']"}),
+            'gallery_start': ('django.db.models.fields.IntegerField', [], {'default': '1', 'null': 'True', 'blank': 'True'}),
+            'head': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['catalogue.ChunkChange']", 'null': 'True', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'number': ('django.db.models.fields.IntegerField', [], {}),
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50'}),
+            'stage': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.ChunkTag']", 'null': 'True', 'blank': 'True'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'})
+        },
+        'catalogue.chunkchange': {
+            'Meta': {'ordering': "('created_at',)", 'unique_together': "(['tree', 'revision'],)", 'object_name': 'ChunkChange'},
+            'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
+            'author_email': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
+            'author_name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
+            'created_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}),
+            'data': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
+            'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'merge_parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'merge_children'", 'null': 'True', 'blank': 'True', 'to': "orm['catalogue.ChunkChange']"}),
+            'parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'children'", 'null': 'True', 'blank': 'True', 'to': "orm['catalogue.ChunkChange']"}),
+            'publishable': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'revision': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
+            'tags': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'change_set'", 'symmetrical': 'False', 'to': "orm['catalogue.ChunkTag']"}),
+            'tree': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'change_set'", 'to': "orm['catalogue.Chunk']"})
+        },
+        'catalogue.chunkpublishrecord': {
+            'Meta': {'object_name': 'ChunkPublishRecord'},
+            'book_record': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.BookPublishRecord']"}),
+            'change': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publish_log'", 'to': "orm['catalogue.ChunkChange']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
+        },
+        'catalogue.chunktag': {
+            'Meta': {'ordering': "['ordering']", 'object_name': 'ChunkTag'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
+            'ordering': ('django.db.models.fields.IntegerField', [], {}),
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '64', 'unique': 'True', 'null': 'True', 'blank': 'True'})
+        },
+        'contenttypes.contenttype': {
+            'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', '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'})
+        },
+        'cover.image': {
+            'Meta': {'object_name': 'Image'},
+            'author': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'download_url': ('django.db.models.fields.URLField', [], {'unique': 'True', 'max_length': '200'}),
+            'file': ('django.db.models.fields.files.ImageField', [], {'max_length': '100'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'license_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'license_url': ('django.db.models.fields.URLField', [], {'max_length': '255', 'blank': 'True'}),
+            'source_url': ('django.db.models.fields.URLField', [], {'max_length': '200'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        }
+    }
+
+    complete_apps = ['catalogue']
\ No newline at end of file
diff --git a/src/catalogue/migrations/0011_auto__add_project__add_field_book_project.py b/src/catalogue/migrations/0011_auto__add_project__add_field_book_project.py
new file mode 100644 (file)
index 0000000..6f30cb4
--- /dev/null
@@ -0,0 +1,159 @@
+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+    def forwards(self, orm):
+        # Adding model 'Project'
+        db.create_table(u'catalogue_project', (
+            (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('name', self.gf('django.db.models.fields.CharField')(unique=True, max_length=255)),
+            ('notes', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
+        ))
+        db.send_create_signal('catalogue', ['Project'])
+
+        # Adding field 'Book.project'
+        db.add_column(u'catalogue_book', 'project',
+                      self.gf('django.db.models.fields.related.ForeignKey')(to=orm['catalogue.Project'], null=True),
+                      keep_default=False)
+
+
+    def backwards(self, orm):
+        # Deleting model 'Project'
+        db.delete_table(u'catalogue_project')
+
+        # Deleting field 'Book.project'
+        db.delete_column(u'catalogue_book', 'project_id')
+
+
+    models = {
+        u'auth.group': {
+            'Meta': {'object_name': 'Group'},
+            u'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': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+        },
+        u'auth.permission': {
+            'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
+            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+        },
+        u'auth.user': {
+            'Meta': {'object_name': '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': u"orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            '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': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+        },
+        'catalogue.book': {
+            'Meta': {'ordering': "['title', 'slug']", 'object_name': 'Book'},
+            '_new_publishable': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+            '_on_track': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
+            '_published': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+            '_short_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            '_single': ('django.db.models.fields.NullBooleanField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
+            'dc_cover_image': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['cover.Image']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'dc_slug': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '128', 'null': 'True', 'blank': 'True'}),
+            'gallery': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': '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', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
+            'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.Project']", 'null': 'True'}),
+            'public': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '128'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'})
+        },
+        'catalogue.bookpublishrecord': {
+            'Meta': {'ordering': "['-timestamp']", 'object_name': 'BookPublishRecord'},
+            'book': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publish_log'", 'to': "orm['catalogue.Book']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'timestamp': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"})
+        },
+        'catalogue.chunk': {
+            'Meta': {'ordering': "['number']", 'unique_together': "[['book', 'number'], ['book', 'slug']]", 'object_name': 'Chunk'},
+            '_changed': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+            '_hidden': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+            '_short_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'book': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.Book']"}),
+            'creator': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'created_chunk'", 'null': 'True', 'to': u"orm['auth.User']"}),
+            'gallery_start': ('django.db.models.fields.IntegerField', [], {'default': '1', 'null': 'True', 'blank': 'True'}),
+            'head': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['catalogue.ChunkChange']", 'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'number': ('django.db.models.fields.IntegerField', [], {}),
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50'}),
+            'stage': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.ChunkTag']", 'null': 'True', 'blank': 'True'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'})
+        },
+        'catalogue.chunkchange': {
+            'Meta': {'ordering': "('created_at',)", 'unique_together': "(['tree', 'revision'],)", 'object_name': 'ChunkChange'},
+            'author': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}),
+            'author_email': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
+            'author_name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
+            'created_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}),
+            'data': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
+            'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'merge_parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'merge_children'", 'null': 'True', 'blank': 'True', 'to': "orm['catalogue.ChunkChange']"}),
+            'parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'children'", 'null': 'True', 'blank': 'True', 'to': "orm['catalogue.ChunkChange']"}),
+            'publishable': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'revision': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
+            'tags': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'change_set'", 'symmetrical': 'False', 'to': "orm['catalogue.ChunkTag']"}),
+            'tree': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'change_set'", 'to': "orm['catalogue.Chunk']"})
+        },
+        'catalogue.chunkpublishrecord': {
+            'Meta': {'object_name': 'ChunkPublishRecord'},
+            'book_record': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.BookPublishRecord']"}),
+            'change': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publish_log'", 'to': "orm['catalogue.ChunkChange']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
+        },
+        'catalogue.chunktag': {
+            'Meta': {'ordering': "['ordering']", 'object_name': 'ChunkTag'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
+            'ordering': ('django.db.models.fields.IntegerField', [], {}),
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '64', 'unique': 'True', 'null': 'True', 'blank': 'True'})
+        },
+        'catalogue.project': {
+            'Meta': {'ordering': "['name']", 'object_name': 'Project'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+            'notes': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
+        },
+        u'contenttypes.contenttype': {
+            'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+            'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            u'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'})
+        },
+        u'cover.image': {
+            'Meta': {'object_name': 'Image'},
+            'author': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'download_url': ('django.db.models.fields.URLField', [], {'unique': 'True', 'max_length': '200'}),
+            'file': ('django.db.models.fields.files.ImageField', [], {'max_length': '100'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'license_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'license_url': ('django.db.models.fields.URLField', [], {'max_length': '255', 'blank': 'True'}),
+            'source_url': ('django.db.models.fields.URLField', [], {'max_length': '200'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        }
+    }
+
+    complete_apps = ['catalogue']
\ No newline at end of file
diff --git a/src/catalogue/migrations/0012_auto__add_imagepublishrecord__add_imagechange__add_unique_imagechange_.py b/src/catalogue/migrations/0012_auto__add_imagepublishrecord__add_imagechange__add_unique_imagechange_.py
new file mode 100644 (file)
index 0000000..599e103
--- /dev/null
@@ -0,0 +1,270 @@
+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+    def forwards(self, orm):
+        # Adding model 'ImagePublishRecord'
+        db.create_table(u'catalogue_imagepublishrecord', (
+            (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('image', self.gf('django.db.models.fields.related.ForeignKey')(related_name='publish_log', to=orm['catalogue.Image'])),
+            ('timestamp', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
+            ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'])),
+            ('change', self.gf('django.db.models.fields.related.ForeignKey')(related_name='publish_log', to=orm['catalogue.ImageChange'])),
+        ))
+        db.send_create_signal('catalogue', ['ImagePublishRecord'])
+
+        # Adding model 'ImageChange'
+        db.create_table(u'catalogue_imagechange', (
+            (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('author', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], null=True, blank=True)),
+            ('author_name', self.gf('django.db.models.fields.CharField')(max_length=128, null=True, blank=True)),
+            ('author_email', self.gf('django.db.models.fields.CharField')(max_length=128, null=True, blank=True)),
+            ('revision', self.gf('django.db.models.fields.IntegerField')(db_index=True)),
+            ('parent', self.gf('django.db.models.fields.related.ForeignKey')(default=None, related_name='children', null=True, blank=True, to=orm['catalogue.ImageChange'])),
+            ('merge_parent', self.gf('django.db.models.fields.related.ForeignKey')(default=None, related_name='merge_children', null=True, blank=True, to=orm['catalogue.ImageChange'])),
+            ('description', self.gf('django.db.models.fields.TextField')(default='', blank=True)),
+            ('created_at', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.now, db_index=True)),
+            ('publishable', self.gf('django.db.models.fields.BooleanField')(default=False)),
+            ('tree', self.gf('django.db.models.fields.related.ForeignKey')(related_name='change_set', to=orm['catalogue.Image'])),
+            ('data', self.gf('django.db.models.fields.files.FileField')(max_length=100)),
+        ))
+        db.send_create_signal('catalogue', ['ImageChange'])
+
+        # Adding M2M table for field tags on 'ImageChange'
+        db.create_table(u'catalogue_imagechange_tags', (
+            ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+            ('imagechange', models.ForeignKey(orm['catalogue.imagechange'], null=False)),
+            ('imagetag', models.ForeignKey(orm['catalogue.imagetag'], null=False))
+        ))
+        db.create_unique(u'catalogue_imagechange_tags', ['imagechange_id', 'imagetag_id'])
+
+        # Adding unique constraint on 'ImageChange', fields ['tree', 'revision']
+        db.create_unique(u'catalogue_imagechange', ['tree_id', 'revision'])
+
+        # Adding model 'ImageTag'
+        db.create_table(u'catalogue_imagetag', (
+            (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('name', self.gf('django.db.models.fields.CharField')(max_length=64)),
+            ('slug', self.gf('django.db.models.fields.SlugField')(max_length=64, unique=True, null=True, blank=True)),
+            ('ordering', self.gf('django.db.models.fields.IntegerField')()),
+        ))
+        db.send_create_signal('catalogue', ['ImageTag'])
+
+        # Adding model 'Image'
+        db.create_table(u'catalogue_image', (
+            (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], null=True, blank=True)),
+            ('image', self.gf('django.db.models.fields.files.FileField')(max_length=100)),
+            ('title', self.gf('django.db.models.fields.CharField')(max_length=255, blank=True)),
+            ('slug', self.gf('django.db.models.fields.SlugField')(unique=True, max_length=50)),
+            ('public', self.gf('django.db.models.fields.BooleanField')(default=True, db_index=True)),
+            ('_short_html', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
+            ('_new_publishable', self.gf('django.db.models.fields.NullBooleanField')(null=True, blank=True)),
+            ('_published', self.gf('django.db.models.fields.NullBooleanField')(null=True, blank=True)),
+            ('_changed', self.gf('django.db.models.fields.NullBooleanField')(null=True, blank=True)),
+            ('stage', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['catalogue.ImageTag'], null=True, blank=True)),
+            ('head', self.gf('django.db.models.fields.related.ForeignKey')(default=None, to=orm['catalogue.ImageChange'], null=True, blank=True)),
+            ('creator', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='created_image', null=True, to=orm['auth.User'])),
+        ))
+        db.send_create_signal('catalogue', ['Image'])
+
+
+    def backwards(self, orm):
+        # Removing unique constraint on 'ImageChange', fields ['tree', 'revision']
+        db.delete_unique(u'catalogue_imagechange', ['tree_id', 'revision'])
+
+        # Deleting model 'ImagePublishRecord'
+        db.delete_table(u'catalogue_imagepublishrecord')
+
+        # Deleting model 'ImageChange'
+        db.delete_table(u'catalogue_imagechange')
+
+        # Removing M2M table for field tags on 'ImageChange'
+        db.delete_table('catalogue_imagechange_tags')
+
+        # Deleting model 'ImageTag'
+        db.delete_table(u'catalogue_imagetag')
+
+        # Deleting model 'Image'
+        db.delete_table(u'catalogue_image')
+
+
+    models = {
+        u'auth.group': {
+            'Meta': {'object_name': 'Group'},
+            u'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': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+        },
+        u'auth.permission': {
+            'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
+            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+        },
+        u'auth.user': {
+            'Meta': {'object_name': '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': u"orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            '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': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+        },
+        'catalogue.book': {
+            'Meta': {'ordering': "['title', 'slug']", 'object_name': 'Book'},
+            '_new_publishable': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+            '_on_track': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
+            '_published': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+            '_short_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            '_single': ('django.db.models.fields.NullBooleanField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
+            'dc_cover_image': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['cover.Image']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'dc_slug': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '128', 'null': 'True', 'blank': 'True'}),
+            'gallery': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': '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', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
+            'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.Project']", 'null': 'True', 'blank': 'True'}),
+            'public': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '128'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'})
+        },
+        'catalogue.bookpublishrecord': {
+            'Meta': {'ordering': "['-timestamp']", 'object_name': 'BookPublishRecord'},
+            'book': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publish_log'", 'to': "orm['catalogue.Book']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'timestamp': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"})
+        },
+        'catalogue.chunk': {
+            'Meta': {'ordering': "['number']", 'unique_together': "[['book', 'number'], ['book', 'slug']]", 'object_name': 'Chunk'},
+            '_changed': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+            '_hidden': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+            '_short_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'book': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.Book']"}),
+            'creator': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'created_chunk'", 'null': 'True', 'to': u"orm['auth.User']"}),
+            'gallery_start': ('django.db.models.fields.IntegerField', [], {'default': '1', 'null': 'True', 'blank': 'True'}),
+            'head': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['catalogue.ChunkChange']", 'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'number': ('django.db.models.fields.IntegerField', [], {}),
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50'}),
+            'stage': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.ChunkTag']", 'null': 'True', 'blank': 'True'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'})
+        },
+        'catalogue.chunkchange': {
+            'Meta': {'ordering': "('created_at',)", 'unique_together': "(['tree', 'revision'],)", 'object_name': 'ChunkChange'},
+            'author': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}),
+            'author_email': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
+            'author_name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
+            'created_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}),
+            'data': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
+            'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'merge_parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'merge_children'", 'null': 'True', 'blank': 'True', 'to': "orm['catalogue.ChunkChange']"}),
+            'parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'children'", 'null': 'True', 'blank': 'True', 'to': "orm['catalogue.ChunkChange']"}),
+            'publishable': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'revision': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
+            'tags': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'change_set'", 'symmetrical': 'False', 'to': "orm['catalogue.ChunkTag']"}),
+            'tree': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'change_set'", 'to': "orm['catalogue.Chunk']"})
+        },
+        'catalogue.chunkpublishrecord': {
+            'Meta': {'object_name': 'ChunkPublishRecord'},
+            'book_record': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.BookPublishRecord']"}),
+            'change': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publish_log'", 'to': "orm['catalogue.ChunkChange']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
+        },
+        'catalogue.chunktag': {
+            'Meta': {'ordering': "['ordering']", 'object_name': 'ChunkTag'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
+            'ordering': ('django.db.models.fields.IntegerField', [], {}),
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '64', 'unique': 'True', 'null': 'True', 'blank': 'True'})
+        },
+        'catalogue.image': {
+            'Meta': {'ordering': "['title']", 'object_name': 'Image'},
+            '_changed': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+            '_new_publishable': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+            '_published': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+            '_short_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'creator': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'created_image'", 'null': 'True', 'to': u"orm['auth.User']"}),
+            'head': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['catalogue.ImageChange']", 'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'image': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
+            'public': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50'}),
+            'stage': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.ImageTag']", 'null': 'True', 'blank': 'True'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'})
+        },
+        'catalogue.imagechange': {
+            'Meta': {'ordering': "('created_at',)", 'unique_together': "(['tree', 'revision'],)", 'object_name': 'ImageChange'},
+            'author': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}),
+            'author_email': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
+            'author_name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
+            'created_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}),
+            'data': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
+            'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'merge_parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'merge_children'", 'null': 'True', 'blank': 'True', 'to': "orm['catalogue.ImageChange']"}),
+            'parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'children'", 'null': 'True', 'blank': 'True', 'to': "orm['catalogue.ImageChange']"}),
+            'publishable': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'revision': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
+            'tags': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'change_set'", 'symmetrical': 'False', 'to': "orm['catalogue.ImageTag']"}),
+            'tree': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'change_set'", 'to': "orm['catalogue.Image']"})
+        },
+        'catalogue.imagepublishrecord': {
+            'Meta': {'ordering': "['-timestamp']", 'object_name': 'ImagePublishRecord'},
+            'change': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publish_log'", 'to': "orm['catalogue.ImageChange']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'image': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publish_log'", 'to': "orm['catalogue.Image']"}),
+            'timestamp': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"})
+        },
+        'catalogue.imagetag': {
+            'Meta': {'ordering': "['ordering']", 'object_name': 'ImageTag'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
+            'ordering': ('django.db.models.fields.IntegerField', [], {}),
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '64', 'unique': 'True', 'null': 'True', 'blank': 'True'})
+        },
+        'catalogue.project': {
+            'Meta': {'ordering': "['name']", 'object_name': 'Project'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+            'notes': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
+        },
+        u'contenttypes.contenttype': {
+            'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+            'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            u'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'})
+        },
+        u'cover.image': {
+            'Meta': {'object_name': 'Image'},
+            'author': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'download_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'unique': 'True', 'null': 'True', 'blank': 'True'}),
+            'file': ('django.db.models.fields.files.ImageField', [], {'max_length': '100'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'license_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'license_url': ('django.db.models.fields.URLField', [], {'max_length': '255', 'blank': 'True'}),
+            'source_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        }
+    }
+
+    complete_apps = ['catalogue']
\ No newline at end of file
diff --git a/src/catalogue/migrations/0013_auto__add_field_image_project.py b/src/catalogue/migrations/0013_auto__add_field_image_project.py
new file mode 100644 (file)
index 0000000..6ae3564
--- /dev/null
@@ -0,0 +1,196 @@
+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+    def forwards(self, orm):
+        # Adding field 'Image.project'
+        db.add_column(u'catalogue_image', 'project',
+                      self.gf('django.db.models.fields.related.ForeignKey')(to=orm['catalogue.Project'], null=True, blank=True),
+                      keep_default=False)
+
+
+    def backwards(self, orm):
+        # Deleting field 'Image.project'
+        db.delete_column(u'catalogue_image', 'project_id')
+
+
+    models = {
+        u'auth.group': {
+            'Meta': {'object_name': 'Group'},
+            u'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': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+        },
+        u'auth.permission': {
+            'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
+            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+        },
+        u'auth.user': {
+            'Meta': {'object_name': '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': u"orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            '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': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+        },
+        'catalogue.book': {
+            'Meta': {'ordering': "['title', 'slug']", 'object_name': 'Book'},
+            '_new_publishable': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+            '_on_track': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
+            '_published': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+            '_short_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            '_single': ('django.db.models.fields.NullBooleanField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
+            'dc_cover_image': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['cover.Image']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+            'dc_slug': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '128', 'null': 'True', 'blank': 'True'}),
+            'gallery': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': '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', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
+            'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.Project']", 'null': 'True', 'blank': 'True'}),
+            'public': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '128'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'})
+        },
+        'catalogue.bookpublishrecord': {
+            'Meta': {'ordering': "['-timestamp']", 'object_name': 'BookPublishRecord'},
+            'book': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publish_log'", 'to': "orm['catalogue.Book']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'timestamp': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"})
+        },
+        'catalogue.chunk': {
+            'Meta': {'ordering': "['number']", 'unique_together': "[['book', 'number'], ['book', 'slug']]", 'object_name': 'Chunk'},
+            '_changed': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+            '_hidden': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+            '_short_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'book': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.Book']"}),
+            'creator': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'created_chunk'", 'null': 'True', 'to': u"orm['auth.User']"}),
+            'gallery_start': ('django.db.models.fields.IntegerField', [], {'default': '1', 'null': 'True', 'blank': 'True'}),
+            'head': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['catalogue.ChunkChange']", 'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'number': ('django.db.models.fields.IntegerField', [], {}),
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50'}),
+            'stage': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.ChunkTag']", 'null': 'True', 'blank': 'True'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'})
+        },
+        'catalogue.chunkchange': {
+            'Meta': {'ordering': "('created_at',)", 'unique_together': "(['tree', 'revision'],)", 'object_name': 'ChunkChange'},
+            'author': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}),
+            'author_email': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
+            'author_name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
+            'created_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}),
+            'data': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
+            'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'merge_parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'merge_children'", 'null': 'True', 'blank': 'True', 'to': "orm['catalogue.ChunkChange']"}),
+            'parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'children'", 'null': 'True', 'blank': 'True', 'to': "orm['catalogue.ChunkChange']"}),
+            'publishable': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'revision': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
+            'tags': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'change_set'", 'symmetrical': 'False', 'to': "orm['catalogue.ChunkTag']"}),
+            'tree': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'change_set'", 'to': "orm['catalogue.Chunk']"})
+        },
+        'catalogue.chunkpublishrecord': {
+            'Meta': {'object_name': 'ChunkPublishRecord'},
+            'book_record': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.BookPublishRecord']"}),
+            'change': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publish_log'", 'to': "orm['catalogue.ChunkChange']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
+        },
+        'catalogue.chunktag': {
+            'Meta': {'ordering': "['ordering']", 'object_name': 'ChunkTag'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
+            'ordering': ('django.db.models.fields.IntegerField', [], {}),
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '64', 'unique': 'True', 'null': 'True', 'blank': 'True'})
+        },
+        'catalogue.image': {
+            'Meta': {'ordering': "['title']", 'object_name': 'Image'},
+            '_changed': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+            '_new_publishable': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+            '_published': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+            '_short_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'creator': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'created_image'", 'null': 'True', 'to': u"orm['auth.User']"}),
+            'head': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['catalogue.ImageChange']", 'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'image': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
+            'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.Project']", 'null': 'True', 'blank': 'True'}),
+            'public': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50'}),
+            'stage': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.ImageTag']", 'null': 'True', 'blank': 'True'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'})
+        },
+        'catalogue.imagechange': {
+            'Meta': {'ordering': "('created_at',)", 'unique_together': "(['tree', 'revision'],)", 'object_name': 'ImageChange'},
+            'author': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}),
+            'author_email': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
+            'author_name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
+            'created_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}),
+            'data': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
+            'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'merge_parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'merge_children'", 'null': 'True', 'blank': 'True', 'to': "orm['catalogue.ImageChange']"}),
+            'parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'children'", 'null': 'True', 'blank': 'True', 'to': "orm['catalogue.ImageChange']"}),
+            'publishable': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'revision': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
+            'tags': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'change_set'", 'symmetrical': 'False', 'to': "orm['catalogue.ImageTag']"}),
+            'tree': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'change_set'", 'to': "orm['catalogue.Image']"})
+        },
+        'catalogue.imagepublishrecord': {
+            'Meta': {'ordering': "['-timestamp']", 'object_name': 'ImagePublishRecord'},
+            'change': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publish_log'", 'to': "orm['catalogue.ImageChange']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'image': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publish_log'", 'to': "orm['catalogue.Image']"}),
+            'timestamp': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"})
+        },
+        'catalogue.imagetag': {
+            'Meta': {'ordering': "['ordering']", 'object_name': 'ImageTag'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
+            'ordering': ('django.db.models.fields.IntegerField', [], {}),
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '64', 'unique': 'True', 'null': 'True', 'blank': 'True'})
+        },
+        'catalogue.project': {
+            'Meta': {'ordering': "['name']", 'object_name': 'Project'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+            'notes': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
+        },
+        u'contenttypes.contenttype': {
+            'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+            'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            u'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'})
+        },
+        u'cover.image': {
+            'Meta': {'object_name': 'Image'},
+            'author': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'download_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'unique': 'True', 'null': 'True', 'blank': 'True'}),
+            'file': ('django.db.models.fields.files.ImageField', [], {'max_length': '100'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'license_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'license_url': ('django.db.models.fields.URLField', [], {'max_length': '255', 'blank': 'True'}),
+            'source_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        }
+    }
+
+    complete_apps = ['catalogue']
\ No newline at end of file
diff --git a/src/catalogue/migrations/__init__.py b/src/catalogue/migrations/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/catalogue/models/__init__.py b/src/catalogue/models/__init__.py
new file mode 100755 (executable)
index 0000000..d0015c7
--- /dev/null
@@ -0,0 +1,21 @@
+# -*- coding: utf-8 -*-
+#
+# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
+from catalogue.models.project import Project
+from catalogue.models.chunk import Chunk
+from catalogue.models.image import Image
+from catalogue.models.publish_log import (BookPublishRecord,
+    ChunkPublishRecord, ImagePublishRecord)
+from catalogue.models.book import Book
+from catalogue.models.listeners import *
+
+from django.contrib.auth.models import User as AuthUser
+
+class User(AuthUser):
+    class Meta:
+        proxy = True
+
+    def __unicode__(self):
+        return "%s %s" % (self.first_name, self.last_name)
diff --git a/src/catalogue/models/book.py b/src/catalogue/models/book.py
new file mode 100755 (executable)
index 0000000..1fcd05a
--- /dev/null
@@ -0,0 +1,452 @@
+# -*- coding: utf-8 -*-
+#
+# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
+from django.contrib.sites.models import Site
+from django.db import models, transaction
+from django.template.loader import render_to_string
+from django.utils.translation import ugettext_lazy as _
+from django.conf import settings
+from slugify import slugify
+
+
+import apiclient
+from catalogue.helpers import cached_in_field, GalleryMerger
+from catalogue.models import BookPublishRecord, ChunkPublishRecord, Project
+from catalogue.signals import post_publish
+from catalogue.tasks import refresh_instance, book_content_updated
+from catalogue.xml_tools import compile_text, split_xml
+from cover.models import Image
+import os
+import shutil
+import re
+
+class Book(models.Model):
+    """ A document edited on the wiki """
+
+    title = models.CharField(_('title'), max_length=255, db_index=True)
+    slug = models.SlugField(_('slug'), max_length=128, unique=True, db_index=True)
+    public = models.BooleanField(_('public'), default=True, db_index=True)
+    gallery = models.CharField(_('scan gallery name'), max_length=255, blank=True)
+    project = models.ForeignKey(Project, null=True, blank=True)
+
+    #wl_slug = models.CharField(_('title'), max_length=255, null=True, db_index=True, editable=False)
+    parent = models.ForeignKey('self', null=True, blank=True, verbose_name=_('parent'), related_name="children", editable=False)
+    parent_number = models.IntegerField(_('parent number'), null=True, blank=True, db_index=True, editable=False)
+
+    # Cache
+    _short_html = models.TextField(null=True, blank=True, editable=False)
+    _single = models.NullBooleanField(editable=False, db_index=True)
+    _new_publishable = models.NullBooleanField(editable=False)
+    _published = models.NullBooleanField(editable=False)
+    _on_track = models.IntegerField(null=True, blank=True, db_index=True, editable=False)
+    dc_cover_image = models.ForeignKey(Image, blank=True, null=True,
+        db_index=True, on_delete=models.SET_NULL, editable=False)
+    dc_slug = models.CharField(max_length=128, null=True, blank=True,
+            editable=False, db_index=True)
+
+    class NoTextError(BaseException):
+        pass
+
+    class Meta:
+        app_label = 'catalogue'
+        ordering = ['title', 'slug']
+        verbose_name = _('book')
+        verbose_name_plural = _('books')
+
+
+    # Representing
+    # ============
+
+    def __iter__(self):
+        return iter(self.chunk_set.all())
+
+    def __getitem__(self, chunk):
+        return self.chunk_set.all()[chunk]
+
+    def __len__(self):
+        return self.chunk_set.count()
+
+    def __nonzero__(self):
+        """
+            Necessary so that __len__ isn't used for bool evaluation.
+        """
+        return True
+
+    def __unicode__(self):
+        return self.title
+
+    @models.permalink
+    def get_absolute_url(self):
+        return ("catalogue_book", [self.slug])
+
+    def correct_about(self):
+        return "http://%s%s" % (
+            Site.objects.get_current().domain,
+            self.get_absolute_url()
+        )
+
+    def gallery_path(self):
+        return os.path.join(settings.MEDIA_ROOT, settings.IMAGE_DIR, self.gallery)
+
+    def gallery_url(self):
+        return '%s%s%s/' % (settings.MEDIA_URL, settings.IMAGE_DIR, self.gallery)
+
+    # Creating & manipulating
+    # =======================
+
+    def accessible(self, request):
+        return self.public or request.user.is_authenticated()
+
+    @classmethod
+    @transaction.atomic
+    def create(cls, creator, text, *args, **kwargs):
+        b = cls.objects.create(*args, **kwargs)
+        b.chunk_set.all().update(creator=creator)
+        b[0].commit(text, author=creator)
+        return b
+
+    def add(self, *args, **kwargs):
+        """Add a new chunk at the end."""
+        return self.chunk_set.reverse()[0].split(*args, **kwargs)
+
+    @classmethod
+    @transaction.atomic
+    def import_xml_text(cls, text=u'', previous_book=None,
+                commit_args=None, **kwargs):
+        """Imports a book from XML, splitting it into chunks as necessary."""
+        texts = split_xml(text)
+        if previous_book:
+            instance = previous_book
+        else:
+            instance = cls(**kwargs)
+            instance.save()
+
+        # if there are more parts, set the rest to empty strings
+        book_len = len(instance)
+        for i in range(book_len - len(texts)):
+            texts.append((u'pusta część %d' % (i + 1), u''))
+
+        i = 0
+        for i, (title, text) in enumerate(texts):
+            if not title:
+                title = u'część %d' % (i + 1)
+
+            slug = slugify(title)
+
+            if i < book_len:
+                chunk = instance[i]
+                chunk.slug = slug[:50]
+                chunk.title = title[:255]
+                chunk.save()
+            else:
+                chunk = instance.add(slug, title)
+
+            chunk.commit(text, **commit_args)
+
+        return instance
+
+    def make_chunk_slug(self, proposed):
+        """ 
+            Finds a chunk slug not yet used in the book.
+        """
+        slugs = set(c.slug for c in self)
+        i = 1
+        new_slug = proposed[:50]
+        while new_slug in slugs:
+            new_slug = "%s_%d" % (proposed[:45], i)
+            i += 1
+        return new_slug
+
+    @transaction.atomic
+    def append(self, other, slugs=None, titles=None):
+        """Add all chunks of another book to self."""
+        assert self != other
+
+        number = self[len(self) - 1].number + 1
+        len_other = len(other)
+        single = len_other == 1
+
+        if slugs is not None:
+            assert len(slugs) == len_other
+        if titles is not None:
+            assert len(titles) == len_other
+            if slugs is None:
+                slugs = [slugify(t) for t in titles]
+
+        for i, chunk in enumerate(other):
+            # move chunk to new book
+            chunk.book = self
+            chunk.number = number
+
+            if titles is None:
+                # try some title guessing
+                if other.title.startswith(self.title):
+                    other_title_part = other.title[len(self.title):].lstrip(' /')
+                else:
+                    other_title_part = other.title
+
+                if single:
+                    # special treatment for appending one-parters:
+                    # just use the guessed title and original book slug
+                    chunk.title = other_title_part
+                    if other.slug.startswith(self.slug):
+                        chunk.slug = other.slug[len(self.slug):].lstrip('-_')
+                    else:
+                        chunk.slug = other.slug
+                else:
+                    chunk.title = ("%s, %s" % (other_title_part, chunk.title))[:255]
+            else:
+                chunk.slug = slugs[i]
+                chunk.title = titles[i]
+
+            chunk.slug = self.make_chunk_slug(chunk.slug)
+            chunk.save()
+            number += 1
+        assert not other.chunk_set.exists()
+
+        gm = GalleryMerger(self.gallery, other.gallery)
+        self.gallery = gm.merge()
+
+        # and move the gallery starts
+        if gm.was_merged:
+                for chunk in self[len(self) - len_other:]:
+                        old_start = chunk.gallery_start or 1
+                        chunk.gallery_start = old_start + gm.dest_size - gm.num_deleted
+                        chunk.save()
+
+        other.delete()
+
+
+    @transaction.atomic
+    def prepend_history(self, other):
+        """Prepend history from all the other book's chunks to own."""
+        assert self != other
+
+        for i in range(len(self), len(other)):
+            title = u"pusta część %d" % i
+            chunk = self.add(slugify(title), title)
+            chunk.commit('')
+
+        for i in range(len(other)):
+            self[i].prepend_history(other[0])
+
+        assert not other.chunk_set.exists()
+        other.delete()
+
+    def split(self):
+        """Splits all the chunks into separate books."""
+        self.title
+        for chunk in self:
+            book = Book.objects.create(title=chunk.title, slug=chunk.slug,
+                    public=self.public, gallery=self.gallery)
+            book[0].delete()
+            chunk.book = book
+            chunk.number = 1
+            chunk.save()
+        assert not self.chunk_set.exists()
+        self.delete()
+
+    # State & cache
+    # =============
+
+    def last_published(self):
+        try:
+            return self.publish_log.all()[0].timestamp
+        except IndexError:
+            return None
+
+    def assert_publishable(self):
+        assert self.chunk_set.exists(), _('No chunks in the book.')
+        try:
+            changes = self.get_current_changes(publishable=True)
+        except self.NoTextError:
+            raise AssertionError(_('Not all chunks have publishable revisions.'))
+
+        from librarian import NoDublinCore, ParseError, ValidationError
+
+        try:
+            bi = self.wldocument(changes=changes, strict=True).book_info
+        except ParseError, e:
+            raise AssertionError(_('Invalid XML') + ': ' + unicode(e))
+        except NoDublinCore:
+            raise AssertionError(_('No Dublin Core found.'))
+        except ValidationError, e:
+            raise AssertionError(_('Invalid Dublin Core') + ': ' + unicode(e))
+
+        valid_about = self.correct_about()
+        assert bi.about == valid_about, _("rdf:about is not") + " " + valid_about
+
+    def publishable_error(self):
+        try:
+            return self.assert_publishable()
+        except AssertionError, e:
+            return e
+        else:
+            return None
+
+    def hidden(self):
+        return self.slug.startswith('.')
+
+    def is_new_publishable(self):
+        """Checks if book is ready for publishing.
+
+        Returns True if there is a publishable version newer than the one
+        already published.
+
+        """
+        new_publishable = False
+        if not self.chunk_set.exists():
+            return False
+        for chunk in self:
+            change = chunk.publishable()
+            if not change:
+                return False
+            if not new_publishable and not change.publish_log.exists():
+                new_publishable = True
+        return new_publishable
+    new_publishable = cached_in_field('_new_publishable')(is_new_publishable)
+
+    def is_published(self):
+        return self.publish_log.exists()
+    published = cached_in_field('_published')(is_published)
+
+    def get_on_track(self):
+        if self.published:
+            return -1
+        stages = [ch.stage.ordering if ch.stage is not None else 0
+                    for ch in self]
+        if not len(stages):
+            return 0
+        return min(stages)
+    on_track = cached_in_field('_on_track')(get_on_track)
+
+    def is_single(self):
+        return len(self) == 1
+    single = cached_in_field('_single')(is_single)
+
+    @cached_in_field('_short_html')
+    def short_html(self):
+        return render_to_string('catalogue/book_list/book.html', {'book': self})
+
+    def book_info(self, publishable=True):
+        try:
+            book_xml = self.materialize(publishable=publishable)
+        except self.NoTextError:
+            pass
+        else:
+            from librarian.dcparser import BookInfo
+            from librarian import NoDublinCore, ParseError, ValidationError
+            try:
+                return BookInfo.from_bytes(book_xml.encode('utf-8'))
+            except (self.NoTextError, ParseError, NoDublinCore, ValidationError):
+                return None
+
+    def refresh_dc_cache(self):
+        update = {
+            'dc_slug': None,
+            'dc_cover_image': None,
+        }
+
+        info = self.book_info()
+        if info is not None:
+            update['dc_slug'] = info.url.slug
+            if info.cover_source:
+                try:
+                    image = Image.objects.get(pk=int(info.cover_source.rstrip('/').rsplit('/', 1)[-1]))
+                except:
+                    pass
+                else:
+                    if info.cover_source == image.get_full_url():
+                        update['dc_cover_image'] = image
+        Book.objects.filter(pk=self.pk).update(**update)
+
+    def touch(self):
+        # this should only really be done when text or publishable status changes
+        book_content_updated.delay(self)
+
+        update = {
+            "_new_publishable": self.is_new_publishable(),
+            "_published": self.is_published(),
+            "_single": self.is_single(),
+            "_on_track": self.get_on_track(),
+            "_short_html": None,
+        }
+        Book.objects.filter(pk=self.pk).update(**update)
+        refresh_instance(self)
+
+    def refresh(self):
+        """This should be done offline."""
+        self.short_html
+        self.single
+        self.new_publishable
+        self.published
+
+    # Materializing & publishing
+    # ==========================
+
+    def get_current_changes(self, publishable=True):
+        """
+            Returns a list containing one Change for every Chunk in the Book.
+            Takes the most recent revision (publishable, if set).
+            Throws an error, if a proper revision is unavailable for a Chunk.
+        """
+        if publishable:
+            changes = [chunk.publishable() for chunk in self]
+        else:
+            changes = [chunk.head for chunk in self if chunk.head is not None]
+        if None in changes:
+            raise self.NoTextError('Some chunks have no available text.')
+        return changes
+
+    def materialize(self, publishable=False, changes=None):
+        """ 
+            Get full text of the document compiled from chunks.
+            Takes the current versions of all texts
+            or versions most recently tagged for publishing,
+            or a specified iterable changes.
+        """
+        if changes is None:
+            changes = self.get_current_changes(publishable)
+        return compile_text(change.materialize() for change in changes)
+
+    def wldocument(self, publishable=True, changes=None, 
+            parse_dublincore=True, strict=False):
+        from catalogue.ebook_utils import RedakcjaDocProvider
+        from librarian.parser import WLDocument
+
+        return WLDocument.from_bytes(
+                self.materialize(publishable=publishable, changes=changes).encode('utf-8'),
+                provider=RedakcjaDocProvider(publishable=publishable),
+                parse_dublincore=parse_dublincore,
+                strict=strict)
+
+    def publish(self, user, fake=False, host=None, days=0, beta=False):
+        """
+            Publishes a book on behalf of a (local) user.
+        """
+        self.assert_publishable()
+        changes = self.get_current_changes(publishable=True)
+        if not fake:
+            book_xml = self.materialize(changes=changes)
+            data = {"book_xml": book_xml, "days": days}
+            if host:
+                data['gallery_url'] = host + self.gallery_url()
+            apiclient.api_call(user, "books/", data, beta=beta)
+        if not beta:
+            # record the publish
+            br = BookPublishRecord.objects.create(book=self, user=user)
+            for c in changes:
+                ChunkPublishRecord.objects.create(book_record=br, change=c)
+            if not self.public and days == 0:
+                self.public = True
+                self.save()
+            if self.public and days > 0:
+                self.public = False
+                self.save()
+            post_publish.send(sender=br)
+
+    def latex_dir(self):
+        doc = self.wldocument()
+        return doc.latex_dir(cover=True, ilustr_path=self.gallery_path())
diff --git a/src/catalogue/models/chunk.py b/src/catalogue/models/chunk.py
new file mode 100755 (executable)
index 0000000..fc3a9ea
--- /dev/null
@@ -0,0 +1,124 @@
+# -*- coding: utf-8 -*-
+#
+# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
+from django.conf import settings
+from django.db import models
+from django.db.utils import IntegrityError
+from django.template.loader import render_to_string
+from django.utils.translation import ugettext_lazy as _
+from catalogue.helpers import cached_in_field
+from catalogue.managers import VisibleManager
+from catalogue.tasks import refresh_instance
+from dvcs import models as dvcs_models
+
+
+class Chunk(dvcs_models.Document):
+    """ An editable chunk of text. Every Book text is divided into chunks. """
+    REPO_PATH = settings.CATALOGUE_REPO_PATH
+
+    book = models.ForeignKey('Book', editable=False, verbose_name=_('book'))
+    number = models.IntegerField(_('number'))
+    title = models.CharField(_('title'), max_length=255, blank=True)
+    slug = models.SlugField(_('slug'))
+    gallery_start = models.IntegerField(_('gallery start'), null=True, blank=True, default=1)
+
+    # cache
+    _short_html = models.TextField(null=True, blank=True, editable=False)
+    _hidden = models.NullBooleanField(editable=False)
+    _changed = models.NullBooleanField(editable=False)
+
+    # managers
+    objects = models.Manager()
+    visible_objects = VisibleManager()
+
+    class Meta:
+        app_label = 'catalogue'
+        unique_together = [['book', 'number'], ['book', 'slug']]
+        ordering = ['number']
+        verbose_name = _('chunk')
+        verbose_name_plural = _('chunks')
+        permissions = [('can_pubmark', 'Can mark for publishing')]
+
+    # Representing
+    # ============
+
+    def __unicode__(self):
+        return "%d:%d: %s" % (self.book_id, self.number, self.title)
+
+    @models.permalink
+    def get_absolute_url(self):
+        return "wiki_editor", [self.book.slug, self.slug]
+
+    def pretty_name(self, book_length=None):
+        title = self.book.title
+        if self.title:
+            title += ", %s" % self.title
+        if book_length > 1:
+            title += " (%d/%d)" % (self.number, book_length)
+        return title
+
+    # Creating and manipulation
+    # =========================
+
+    def split(self, slug, title='', **kwargs):
+        """ Create an empty chunk after this one """
+        self.book.chunk_set.filter(number__gt=self.number).update(
+                number=models.F('number')+1)
+        new_chunk = None
+        while not new_chunk:
+            new_slug = self.book.make_chunk_slug(slug)
+            try:
+                new_chunk = self.book.chunk_set.create(
+                    number=self.number+1,
+                    slug=new_slug[:50], title=title[:255], **kwargs)
+            except IntegrityError:
+                pass
+        return new_chunk
+
+    @classmethod
+    def get(cls, book_slug, chunk_slug=None):
+        if chunk_slug is None:
+            return cls.objects.get(book__slug=book_slug, number=1)
+        else:
+            return cls.objects.get(book__slug=book_slug, slug=chunk_slug)
+
+    # State & cache
+    # =============
+
+    def new_publishable(self):
+        change = self.publishable()
+        if not change:
+            return False
+        return not change.publish_log.exists()
+
+    def is_changed(self):
+        if self.head is None:
+            return False
+        return not self.head.publishable
+    changed = cached_in_field('_changed')(is_changed)
+
+    def is_hidden(self):
+        return self.book.hidden()
+    hidden = cached_in_field('_hidden')(is_hidden)
+
+    @cached_in_field('_short_html')
+    def short_html(self):
+        return render_to_string(
+                    'catalogue/book_list/chunk.html', {'chunk': self})
+
+    def touch(self):
+        update = {
+            "_changed": self.is_changed(),
+            "_hidden": self.is_hidden(),
+            "_short_html": None,
+        }
+        Chunk.objects.filter(pk=self.pk).update(**update)
+        refresh_instance(self)
+
+    def refresh(self):
+        """This should be done offline."""
+        self.changed
+        self.hidden
+        self.short_html
diff --git a/src/catalogue/models/image.py b/src/catalogue/models/image.py
new file mode 100755 (executable)
index 0000000..646dd0a
--- /dev/null
@@ -0,0 +1,172 @@
+# -*- coding: utf-8 -*-
+#
+# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
+from django.conf import settings
+from django.contrib.sites.models import Site
+from django.db import models
+from django.template.loader import render_to_string
+from django.utils.translation import ugettext_lazy as _
+from catalogue.helpers import cached_in_field
+from catalogue.models import Project
+from catalogue.tasks import refresh_instance
+from dvcs import models as dvcs_models
+
+
+class Image(dvcs_models.Document):
+    """ An editable chunk of text. Every Book text is divided into chunks. """
+    REPO_PATH = settings.CATALOGUE_IMAGE_REPO_PATH
+
+    image = models.FileField(_('image'), upload_to='catalogue/images')
+    title = models.CharField(_('title'), max_length=255, blank=True)
+    slug = models.SlugField(_('slug'), unique=True)
+    public = models.BooleanField(_('public'), default=True, db_index=True)
+    project = models.ForeignKey(Project, null=True, blank=True)
+
+    # cache
+    _short_html = models.TextField(null=True, blank=True, editable=False)
+    _new_publishable = models.NullBooleanField(editable=False)
+    _published = models.NullBooleanField(editable=False)
+    _changed = models.NullBooleanField(editable=False)
+
+    class Meta:
+        app_label = 'catalogue'
+        ordering = ['title']
+        verbose_name = _('image')
+        verbose_name_plural = _('images')
+        permissions = [('can_pubmark_image', 'Can mark images for publishing')]
+
+    # Representing
+    # ============
+
+    def __unicode__(self):
+        return self.title
+
+    @models.permalink
+    def get_absolute_url(self):
+        return ("catalogue_image", [self.slug])
+
+    def correct_about(self):
+        return ["http://%s%s" % (
+            Site.objects.get_current().domain,
+            self.get_absolute_url()
+            ),
+            "http://%s%s" % (
+                'obrazy.redakcja.wolnelektury.pl',
+                self.get_absolute_url()
+            )]
+
+    # State & cache
+    # =============
+
+    def last_published(self):
+        try:
+            return self.publish_log.all()[0].timestamp
+        except IndexError:
+            return None
+
+    def assert_publishable(self):
+        from librarian.picture import WLPicture
+        from librarian import NoDublinCore, ParseError, ValidationError
+
+        class SelfImageStore(object):
+            def path(self_, slug, mime_type):
+                """Returns own file object. Ignores slug ad mime_type."""
+                return open(self.image.path)
+
+        publishable = self.publishable()
+        assert publishable, _("There is no publishable revision")
+        picture_xml = publishable.materialize()
+
+        try:
+            picture = WLPicture.from_bytes(
+                    picture_xml.encode('utf-8'),
+                    image_store=SelfImageStore)
+        except ParseError, e:
+            raise AssertionError(_('Invalid XML') + ': ' + str(e))
+        except NoDublinCore:
+            raise AssertionError(_('No Dublin Core found.'))
+        except ValidationError, e:
+            raise AssertionError(_('Invalid Dublin Core') + ': ' + str(e))
+
+        valid_about = self.correct_about()
+        assert picture.picture_info.about in valid_about, \
+                _("rdf:about is not") + " " + valid_about[0]
+
+    def publishable_error(self):
+        try:
+            return self.assert_publishable()
+        except AssertionError, e:
+            return e
+        else:
+            return None
+
+    def accessible(self, request):
+        return self.public or request.user.is_authenticated()
+
+    def is_new_publishable(self):
+        change = self.publishable()
+        if not change:
+            return False
+        return not change.publish_log.exists()
+    new_publishable = cached_in_field('_new_publishable')(is_new_publishable)
+
+    def is_published(self):
+        return self.publish_log.exists()
+    published = cached_in_field('_published')(is_published)
+
+    def is_changed(self):
+        if self.head is None:
+            return False
+        return not self.head.publishable
+    changed = cached_in_field('_changed')(is_changed)
+
+    @cached_in_field('_short_html')
+    def short_html(self):
+        return render_to_string(
+                    'catalogue/image_short.html', {'image': self})
+
+    def refresh(self):
+        """This should be done offline."""
+        self.short_html
+        self.single
+        self.new_publishable
+        self.published
+
+    def touch(self):
+        update = {
+            "_changed": self.is_changed(),
+            "_short_html": None,
+            "_new_publishable": self.is_new_publishable(),
+            "_published": self.is_published(),
+        }
+        Image.objects.filter(pk=self.pk).update(**update)
+        refresh_instance(self)
+
+    def refresh(self):
+        """This should be done offline."""
+        self.changed
+        self.short_html
+
+
+    # Publishing
+    # ==========
+
+    def publish(self, user):
+        """Publishes the picture on behalf of a (local) user."""
+        from base64 import b64encode
+        import apiclient
+        from catalogue.signals import post_publish
+
+        self.assert_publishable()
+        change = self.publishable()
+        picture_xml = change.materialize()
+        picture_data = open(self.image.path).read()
+        apiclient.api_call(user, "pictures/", {
+                "picture_xml": picture_xml,
+                "picture_image_data": b64encode(picture_data),
+            })
+        # record the publish
+        log = self.publish_log.create(user=user, change=change)
+        post_publish.send(sender=log)
diff --git a/src/catalogue/models/listeners.py b/src/catalogue/models/listeners.py
new file mode 100755 (executable)
index 0000000..1cfac27
--- /dev/null
@@ -0,0 +1,70 @@
+# -*- coding: utf-8 -*-
+#
+# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
+from django.contrib.auth.models import User
+from django.db import models
+from catalogue.models import (Book, Chunk, Image, BookPublishRecord,
+        ImagePublishRecord)
+from catalogue.signals import post_publish
+from dvcs.signals import post_publishable
+
+
+def book_changed(sender, instance, created, **kwargs):
+    instance.touch()
+    for c in instance:
+        c.touch()
+models.signals.post_save.connect(book_changed, sender=Book)
+
+
+def chunk_changed(sender, instance, created, **kwargs):
+    instance.book.touch()
+    instance.touch()
+models.signals.post_save.connect(chunk_changed, sender=Chunk)
+
+
+def image_changed(sender, instance, created, **kwargs):
+    instance.touch()
+models.signals.post_save.connect(image_changed, sender=Image)
+
+
+def user_changed(sender, instance, *args, **kwargs):
+    if 'last_login' in (kwargs.get('update_fields') or {}):
+        # Quick hack - this change seems to result from logging user in so just ignore it.
+        return
+    books = set()
+    for c in instance.chunk_set.all():
+        books.add(c.book)
+        c.touch()
+    for b in books:
+        b.touch()
+models.signals.post_save.connect(user_changed, sender=User)
+
+
+def publish_listener(sender, *args, **kwargs):
+    if isinstance(sender, BookPublishRecord):
+        sender.book.touch()
+        for c in sender.book:
+            c.touch()
+    elif isinstance(sender, ImagePublishRecord):
+        sender.image.touch()
+post_publish.connect(publish_listener)
+
+
+def chunk_publishable_listener(sender, *args, **kwargs):
+    sender.tree.touch()
+    if isinstance(sender.tree, Chunk):
+        sender.tree.book.touch()
+post_publishable.connect(chunk_publishable_listener)
+
+def publishable_listener(sender, *args, **kwargs):
+    sender.tree.touch()
+post_publishable.connect(publishable_listener, sender=Image)
+
+
+def listener_create(sender, instance, created, **kwargs):
+    if created:
+        instance.chunk_set.create(number=1, slug='1')
+models.signals.post_save.connect(listener_create, sender=Book)
+
diff --git a/src/catalogue/models/project.py b/src/catalogue/models/project.py
new file mode 100755 (executable)
index 0000000..eb95102
--- /dev/null
@@ -0,0 +1,23 @@
+# -*- coding: utf-8 -*-
+#
+# This file is part of FNP-Redakcja, 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 _
+
+
+class Project(models.Model):
+    """ A project, tracked for funding purposes. """
+
+    name = models.CharField(_('name'), max_length=255, unique=True)
+    notes = models.TextField(_('notes'), blank=True, null=True)
+
+    class Meta:
+        app_label = 'catalogue'
+        ordering = ['name']
+        verbose_name = _('project')
+        verbose_name_plural = _('projects')
+
+    def __unicode__(self):
+        return self.name
diff --git a/src/catalogue/models/publish_log.py b/src/catalogue/models/publish_log.py
new file mode 100755 (executable)
index 0000000..7a8e2f9
--- /dev/null
@@ -0,0 +1,54 @@
+# -*- coding: utf-8 -*-
+#
+# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
+from django.contrib.auth.models import User
+from django.db import models
+from django.utils.translation import ugettext_lazy as _
+from catalogue.models import Chunk, Image
+
+
+class BookPublishRecord(models.Model):
+    """
+        A record left after publishing a Book.
+    """
+
+    book = models.ForeignKey('Book', verbose_name=_('book'), related_name='publish_log')
+    timestamp = models.DateTimeField(_('time'), auto_now_add=True)
+    user = models.ForeignKey(User, verbose_name=_('user'))
+
+    class Meta:
+        app_label = 'catalogue'
+        ordering = ['-timestamp']
+        verbose_name = _('book publish record')
+        verbose_name_plural = _('book publish records')
+
+
+class ChunkPublishRecord(models.Model):
+    """
+        BookPublishRecord details for each Chunk.
+    """
+
+    book_record = models.ForeignKey(BookPublishRecord, verbose_name=_('book publish record'))
+    change = models.ForeignKey(Chunk.change_model, related_name='publish_log', verbose_name=_('change'))
+
+    class Meta:
+        app_label = 'catalogue'
+        verbose_name = _('chunk publish record')
+        verbose_name_plural = _('chunk publish records')
+
+
+class ImagePublishRecord(models.Model):
+    """A record left after publishing an Image."""
+
+    image = models.ForeignKey(Image, verbose_name=_('image'), related_name='publish_log')
+    timestamp = models.DateTimeField(_('time'), auto_now_add=True)
+    user = models.ForeignKey(User, verbose_name=_('user'))
+    change = models.ForeignKey(Image.change_model, related_name='publish_log', verbose_name=_('change'))
+
+    class Meta:
+        app_label = 'catalogue'
+        ordering = ['-timestamp']
+        verbose_name = _('image publish record')
+        verbose_name_plural = _('image publish records')
diff --git a/src/catalogue/signals.py b/src/catalogue/signals.py
new file mode 100644 (file)
index 0000000..62ca514
--- /dev/null
@@ -0,0 +1,3 @@
+from django.dispatch import Signal
+
+post_publish = Signal()
diff --git a/src/catalogue/tasks.py b/src/catalogue/tasks.py
new file mode 100644 (file)
index 0000000..9507c41
--- /dev/null
@@ -0,0 +1,25 @@
+# -*- coding: utf-8 -*-
+from celery.task import task
+from django.utils import translation
+
+
+@task(ignore_result=True)
+def _refresh_by_pk(cls, pk, language=None):
+    prev_language = translation.get_language()
+    if language:
+        translation.activate(language)
+    try:
+        cls._default_manager.get(pk=pk).refresh()
+    except cls.DoesNotExist:
+        pass
+    finally:
+        translation.activate(prev_language)
+
+
+def refresh_instance(instance):
+    _refresh_by_pk.delay(type(instance), instance.pk, translation.get_language())
+
+
+@task(ignore_result=True)
+def book_content_updated(book):
+    book.refresh_dc_cache()
diff --git a/src/catalogue/templates/catalogue/active_users_list.html b/src/catalogue/templates/catalogue/active_users_list.html
new file mode 100755 (executable)
index 0000000..f711b60
--- /dev/null
@@ -0,0 +1,20 @@
+{% extends "catalogue/base.html" %}
+{% load i18n %}
+
+
+{% block titleextra %}{% trans "Active users" %}{% endblock %}
+
+
+{% block content %}
+
+<h1>
+    {% trans "Active users since" %} {{ since }}
+</h1>
+
+<ul>
+{% for email, names, count in users %}
+<li>{% for name in names %}{{ name }},  {% endfor %}<a href="mailto:{{ email }}">{{ email }}</a> ({{ count }})</li>
+{% endfor %}
+</ul>
+
+{% endblock content %}
diff --git a/src/catalogue/templates/catalogue/activity.html b/src/catalogue/templates/catalogue/activity.html
new file mode 100755 (executable)
index 0000000..3bb8afb
--- /dev/null
@@ -0,0 +1,19 @@
+{% extends "catalogue/base.html" %}
+{% load i18n %}
+{% load wall %}
+
+
+{% block titleextra %}{% trans "Activity" %}{% endblock %}
+
+
+{% block content %}
+
+<h1><a href='{% url "catalogue_activity" prev_day.isoformat %}'>&lt;</a>
+    {% trans "Activity" %}: {{ day }}
+    {% if next_day %}
+        <a href='{% url "catalogue_activity" next_day.isoformat %}'>&gt;</a>
+    {% endif %}
+</h1>
+
+    {% day_wall day %}
+{% endblock content %}
diff --git a/src/catalogue/templates/catalogue/base.html b/src/catalogue/templates/catalogue/base.html
new file mode 100644 (file)
index 0000000..8577f10
--- /dev/null
@@ -0,0 +1,54 @@
+{% load pipeline i18n %}
+{% load catalogue %}
+<!DOCTYPE html>
+<html>
+<head lang="{{ LANGUAGE_CODE }}">
+    <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
+    <link rel="icon" href="{{ STATIC_URL }}img/pr-icon.png" type="image/png" />
+    {% stylesheet 'catalogue' %}
+    <title>{% block title %}{% block titleextra %}{% endblock %} ::
+        {% trans "Platforma Redakcyjna" %}{% endblock title %}</title>
+    {% block add_css %}{% endblock %}
+</head>
+<body>
+<!--#include file='/pozor.html'-->
+<div id="tabs-nav">
+
+    <a href="{% url 'catalogue_document_list' %}">
+        <img id="logo" src="{{ STATIC_URL }}img/wl-orange.png" alt="Platforma" />
+    </a>
+
+    <div id="tabs-nav-left">
+        {% main_tabs %}
+    </div>
+
+    <span id="login-box">
+        {% include "registration/head_login.html" %}
+    </span>
+
+    <div class='clr' ></div>
+</div>
+
+<div id="content">
+
+{% block content %}
+<div id="catalogue_layout_left_column">
+       {% block leftcolumn %}
+       {% endblock leftcolumn %}
+</div>
+<div id="catalogue_layout_right_column">
+       {% block rightcolumn %}
+       {% endblock rightcolumn %}
+</div>
+{% endblock content %}
+
+</div>
+
+
+<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
+{% javascript 'catalogue' %}
+{% block add_js %}{% endblock %}
+{% block extrabody %}
+{% endblock %}
+</body>
+</html>
diff --git a/src/catalogue/templates/catalogue/book_append_to.html b/src/catalogue/templates/catalogue/book_append_to.html
new file mode 100755 (executable)
index 0000000..c1ecc29
--- /dev/null
@@ -0,0 +1,16 @@
+{% extends "catalogue/base.html" %}
+{% load i18n %}
+
+{% block titleextra %}{% trans "Append book" %}{% endblock %}
+
+{% block leftcolumn %}
+       <form enctype="multipart/form-data" method="POST" action="">
+    {% csrf_token %}
+       {{ form.as_p }}
+
+       <p><button type="submit">{% trans "Append book" %}</button></p>
+       </form>
+{% endblock leftcolumn %}
+
+{% block rightcolumn %}
+{% endblock rightcolumn %}
diff --git a/src/catalogue/templates/catalogue/book_detail.html b/src/catalogue/templates/catalogue/book_detail.html
new file mode 100755 (executable)
index 0000000..4712edf
--- /dev/null
@@ -0,0 +1,103 @@
+{% extends "catalogue/base.html" %}
+{% load book_list i18n %}
+
+
+{% block titleextra %}{{ book.title }}{% endblock %}
+
+
+{% block content %}
+
+
+<h1>{{ book.title }}</h1>
+
+
+{% if editable %}<form method='POST'>{% csrf_token %}{% endif %}
+<table class='editable'><tbody>
+    {{ form.as_table }}
+    {% if editable %}
+        <tr><td></td><td><button type="submit">{% trans "Save" %}</button></td></tr>
+    {% endif %}
+</tbody></table>
+{% if editable %}</form>{% endif %}
+
+{% if editable %}
+    {% if book.gallery %}
+    <p><a href="{% url 'catalogue_book_gallery' book.slug %}">{% trans "Edit gallery" %}</a></p>
+    {% endif %}
+
+    <p><a href="{% url 'catalogue_book_append' book.slug %}">{% trans "Append to other book" %}</a></p>
+{% endif %}
+
+
+<div class='section'>
+
+    <h2>{% trans "Chunks" %}</h2>
+
+    <table class='single-book-list'><tbody>
+    {% for chunk in book %}
+        {{ chunk.short_html|safe }}
+    {% endfor %}
+    </tbody></table>
+
+</div>
+
+
+
+<div class='section'>
+
+
+<h2>{% trans "Publication" %}</h2>
+
+<div class="cover-preview">
+<img class="cover-preview" src="{% url 'cover_preview' book.slug %}" />
+{% if book.dc_cover_image %}
+    <a href="{{ book.dc_cover_image.get_absolute_url }}">{{ book.dc_cover_image }}</a>
+{% endif %}
+</div>
+
+<p>{% trans "Last published" %}: 
+    {% if book.last_published %}
+        {{ book.last_published }}
+    {% else %}
+        &mdash;
+    {% endif %}
+</p>
+
+{% if publishable %}
+    <p>
+    <a href="{% url 'catalogue_book_xml' book.slug %}" rel="nofollow">{% trans "Full XML" %}</a><br/>
+    <a target="_blank" href="{% url 'catalogue_book_html' book.slug %}" rel="nofollow">{% trans "HTML version" %}</a><br/>
+    <a href="{% url 'catalogue_book_txt' book.slug %}" rel="nofollow">{% trans "TXT version" %}</a><br/>
+    <a href="{% url 'catalogue_book_pdf' book.slug %}" rel="nofollow">{% trans "PDF version" %}</a><br/>
+    <a href="{% url 'catalogue_book_pdf_mobile' book.slug %}" rel="nofollow">{% trans "PDF version for mobiles" %}</a><br/>
+    <a href="{% url 'catalogue_book_epub' book.slug %}" rel="nofollow">{% trans "EPUB version" %}</a><br/>
+    <a href="{% url 'catalogue_book_mobi' book.slug %}" rel="nofollow">{% trans "MOBI version" %}</a><br/>
+    </p>
+
+    {% if user.is_authenticated %}
+        <!--
+        Angel photos:
+        Angels in Ely Cathedral (http://www.flickr.com/photos/21804434@N02/4483220595/) /
+        mira66 (http://www.flickr.com/photos/21804434@N02/) /
+        CC BY 2.0 (http://creativecommons.org/licenses/by/2.0/)
+        -->
+        <form method="POST" action="{% url 'catalogue_publish' book.slug %}">{% csrf_token %}
+            {{ publish_options_form.as_p }}
+            <img src="{{ STATIC_URL }}img/angel-left.png" style="vertical-align: middle" />
+            <button id="publish-button" type="submit">
+                <span>{% trans "Publish" %}</span></button>
+            <img src="{{ STATIC_URL }}img/angel-right.png" style="vertical-align: middle" />
+            </form>
+    {% else %}
+        <a href="{% url 'login' %}">{% trans "Log in to publish." %}</a>
+    {% endif %}
+{% else %}
+    <p>{% trans "This book can't be published yet, because:" %}</p>
+    <ul><li>{{ publishable_error }}</li></ul>
+{% endif %}
+
+<div style="clear: both;"></div>
+</div>
+
+
+{% endblock content %}
diff --git a/src/catalogue/templates/catalogue/book_edit.html b/src/catalogue/templates/catalogue/book_edit.html
new file mode 100755 (executable)
index 0000000..43fe0ea
--- /dev/null
@@ -0,0 +1,18 @@
+{% extends "catalogue/base.html" %}
+{% load i18n %}
+
+
+{% block titleextra %}{% trans "Edit book" %}{% endblock %}
+
+
+{% block leftcolumn %}
+       <form enctype="multipart/form-data" method="POST" action="">
+    {% csrf_token %}
+       {{ form.as_p }}
+
+       <p><button type="submit">{% trans "Save" %}</button></p>
+       </form>
+{% endblock leftcolumn %}
+
+{% block rightcolumn %}
+{% endblock rightcolumn %}
diff --git a/src/catalogue/templates/catalogue/book_html.html b/src/catalogue/templates/catalogue/book_html.html
new file mode 100755 (executable)
index 0000000..518811e
--- /dev/null
@@ -0,0 +1,29 @@
+{% 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">
+    <head>
+        <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
+        <title>{{ book.title }}</title>
+    </head>
+    <body>
+        <div id="menu">
+            <ul>
+                <li><a href="#toc">{% trans "Table of contents" %}</a></li>
+                <li><a href="#nota_red">{% trans "Edit. note" %}</a></li>
+                <li><a href="#info">{% trans "Infobox" %}</a></li>
+            </ul>
+        </div>
+        <div id="info">
+            {#% book_info book %#}
+        </div>
+        <div id="header">
+            <div id="logo">
+                <a href="/"><img src="http://static.wolnelektury.pl/img/logo.png" alt="WolneLektury.pl - logo" /></a>
+            </div>
+        </div>
+
+        {{ html|safe }}
+
+    </body>
+</html>
diff --git a/src/catalogue/templates/catalogue/book_list/book.html b/src/catalogue/templates/catalogue/book_list/book.html
new file mode 100755 (executable)
index 0000000..f6a0fcd
--- /dev/null
@@ -0,0 +1,40 @@
+{% load i18n %}
+{% load username from common_tags %}
+
+{% if book.single %}
+    {% with book.0 as chunk %}
+    <tr>
+        <td><input type="checkbox" name="select_book" value="{{book.id}}" data-chunk-id="{{chunk.id}}"/></td>
+        <td><a href="{% url 'catalogue_book' book.slug %}" title='{% trans "Book settings" %}'>[B]</a></td>
+        <td><a href="{% url 'catalogue_chunk_edit' book.slug chunk.slug %}" title='{% trans "Chunk settings" %}'>[c]</a></td>
+        <td><a target="_blank"
+                    href="{% url 'wiki_editor' book.slug %}">
+                    {{ book.title }}</a></td>
+        <td>{% if chunk.stage %}
+            {{ chunk.stage }}
+        {% else %}–
+        {% endif %}</td>
+        <td class='user-column'>{% if chunk.user %}<a href="{% url 'catalogue_user' chunk.user.username %}">{{ chunk.user|username }}</a>{% endif %}</td>
+        <td>
+            {% if chunk.published %}P{% endif %}
+            {% if book.new_publishable %}p{% endif %}
+            {% if chunk.changed %}+{% endif %}
+        </td>
+        <td>{{ book.project.name }}</td>
+    </tr>
+    {% endwith %}
+{% else %}
+    <tr>
+        <td><input type="checkbox" name="select_book" value="{{book.id}}"/></td>
+        <td class='book-settings-link'><a href="{% url 'catalogue_book' book.slug %}" title='{% trans "Book settings" %}'>[B]</a></td>
+        <td></td>
+        <td>{{ book.title }}</td>
+        <td></td>
+        <td class='user-column'></td>
+        <td>
+            {% if book.published %}P{% endif %}
+            {% if book.new_publishable %}p{% endif %}
+        </td>
+        <td>{{ book.project.name }}</td>
+    </tr>
+{% endif %}
diff --git a/src/catalogue/templates/catalogue/book_list/book_list.html b/src/catalogue/templates/catalogue/book_list/book_list.html
new file mode 100755 (executable)
index 0000000..e238827
--- /dev/null
@@ -0,0 +1,114 @@
+{% load i18n %}
+{% load pagination_tags %}
+{% load username from common_tags %}
+
+
+<form name='filter' action='{{ request.path }}'>
+<input type='hidden' name="title" value="{{ request.GET.title }}" />
+<input type='hidden' name="stage" value="{{ request.GET.stage }}" />
+{% if not viewed_user %}
+    <input type='hidden' name="user" value="{{ request.GET.user }}" />
+{% endif %}
+<input type='hidden' name="all" value="{{ request.GET.all }}" />
+<input type='hidden' name="status" value="{{ request.GET.status }}" />
+<input type='hidden' name="project" value="{{ request.GET.project }}" />
+</form>
+
+
+<table id="file-list"{% if viewed_user %} class="book-list-user"{% endif %}>
+    <thead><tr>
+       <th></th>
+        <th></th>
+        <th>
+            <input class='check-filter' type='checkbox' name='all' title='{% trans "Show hidden books" %}'
+                {% if request.GET.all %}checked='checked'{% endif %} />
+            </th>
+        <th class='book-search-column'>
+            <form>
+            <input title='{% trans "Search in book titles" %}' name="title"
+                class='text-filter' value="{{ request.GET.title }}" />
+            </form>
+        </th>
+        <th><select name="stage" class="filter">
+            <option value=''>- {% trans "stage" %} -</option>
+            <option {% if request.GET.stage == '-' %}selected="selected"
+                    {% endif %}value="-">- {% trans "none" %} -</option>
+            {% for stage in stages %}
+                <option {% if request.GET.stage == stage.slug %}selected="selected"
+                    {% endif %}value="{{ stage.slug }}">{{ stage.name }}</option>
+            {% endfor %}
+        </select></th>
+
+        {% if not viewed_user %}
+            <th><select name="user" class="filter">
+                <option value=''>- {% trans "editor" %} -</option>
+                <option {% if request.GET.user == '-' %}selected="selected"
+                        {% endif %}value="-">- {% trans "none" %} -</option>
+                {% for user in users %}
+                    <option {% if request.GET.user == user.username %}selected="selected"
+                        {% endif %}value="{{ user.username }}">{{ user|username }} ({{ user.count }})</option>
+                {% endfor %}
+            </select></th>
+        {% else %}
+            <th style='display: none'></th>
+        {% endif %}
+
+        <th><select name="status" class="filter">
+            <option value=''>- {% trans "status" %} -</option>
+            {% for state, label in states %}
+                <option {% if request.GET.status == state %}selected="selected"
+                        {% endif %}value='{{ state }}'>{{ label }}</option>
+            {% endfor %}
+        </select></th>
+
+        <th><select name="project" class="filter">
+            <option value=''>- {% trans "project" %} -</option>
+                <option {% if request.GET.project == '-' %}selected="selected"
+                        {% endif %}value="-">- {% trans "none" %} -</option>
+            {% for project in projects %}
+                <option {% if request.GET.project == project.pk|slugify %}selected="selected"
+                        {% endif %}value='{{ project.pk }}'>{{ project.name }}</option>
+            {% endfor %}
+        </select></th>
+
+    </tr></thead>
+
+    {% with cnt=books|length %}
+    {% autopaginate books 100 %}
+    <tbody>
+    {% for item in books %}
+        {% with item.book as book %}
+            {{ book.short_html|safe }}
+            {% if not book.single %}
+                {% for chunk in item.chunks %}
+                    {{ chunk.short_html|safe }}
+                {% endfor %}
+            {% endif %}
+        {% endwith %}
+    {% endfor %}
+    <tr><th class='paginator' colspan="6">
+        {% paginate %}
+        {% blocktrans count c=cnt %}{{c}} book{% plural %}{{c}} books{% endblocktrans %}</th></tr>
+    </tbody>
+    {% endwith %}
+</table>
+{% if not books %}
+    <p>{% trans "No books found." %}</p>
+{% endif %}
+
+<form id='chunk_mass_edit' action='{% url "catalogue_chunk_mass_edit" %}' style="display:none;">
+{% csrf_token %}
+<input type="hidden" name="ids" />
+<label for="mass_edit_stage">{% trans "Set stage" %}</label><input type="hidden" name="stage" id="mass_edit_stage"/>
+<label for="mass_edit_user">{% trans "Set user" %}</label><input type="hidden" name="user" id="mass_edit_user" />
+<input type="hidden" name="status" />
+<label for="mass_edit_project">{% trans "Project" %}</label><input type="hidden" name="project" id="mass_edit_project" />
+<label for="mass_edit_more_users">{% trans "More users" %}</label>
+</form>
+
+<select name="other-user" style="display:none;">
+  {% for user in other_users %}
+  <option {% if request.GET.user == user.username %}selected="selected"
+          {% endif %}value="{{ user.username }}">{{ user|username }} ({{ user.count }})</option>
+  {% endfor %}
+</select>
diff --git a/src/catalogue/templates/catalogue/book_list/chunk.html b/src/catalogue/templates/catalogue/book_list/chunk.html
new file mode 100755 (executable)
index 0000000..0d21895
--- /dev/null
@@ -0,0 +1,27 @@
+{% load i18n %}
+{% load username from common_tags %}
+
+<tr>
+    <td><input type="checkbox" name="select_chunk" value="{{chunk.id}}" data-book-id="{{chunk.book.id}}" /></td>
+    <td class='book-settings-column'></td>
+    <td><a href="{% url 'catalogue_chunk_edit' chunk.book.slug chunk.slug %}" title='{% trans "Chunk settings" %}'>[c]</a></td>
+    <td><a target="_blank" href="{{ chunk.get_absolute_url }}">
+            <span class='chunkno'>{{ chunk.number }}.</span>
+            {{ chunk.title }}</a></td>
+    <td>{% if chunk.stage %}
+            {{ chunk.stage }}
+        {% else %}
+            –
+        {% endif %}</td>
+        <td class='user-column'>{% if chunk.user %}
+            <a href="{% url 'catalogue_user' chunk.user.username %}">
+                {{ chunk.user|username }}
+            </a>{% else %}
+            
+            {% endif %}</td>
+</td>
+<td>
+    {% if chunk.new_publishable %}p{% endif %}
+    {% if chunk.changed %}+{% endif %}
+</td>
+</tr>
diff --git a/src/catalogue/templates/catalogue/book_text.html b/src/catalogue/templates/catalogue/book_text.html
new file mode 100644 (file)
index 0000000..2e48448
--- /dev/null
@@ -0,0 +1,28 @@
+{% load i18n pipeline %}
+<!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">
+    <head>
+        <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
+        <title>{% trans "Redakcja" %} :: {{ book.title }}</title>
+       {% stylesheet 'book' %}
+        <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
+       {% javascript 'book' %}
+    </head>
+    <body>
+        <div id="menu">
+            <ul>
+                <li><a class="menu" href="#toc">{% trans "Table of contents" %}</a></li>
+{#                <li><a class="menu" href="#themes">{% trans "Themes" %}</a></li>#}
+                <li><a class="menu" href="#nota_red">{% trans "Edit. note" %}</a></li>
+{#                <li><a class="menu" href="#info">{% trans "Infobox" %}</a></li>#}
+{#                <li><a href="{{ book.get_absolute_url }}">{% trans "Book's page" %}</a></li> #}
+{#                <li><a class="menu" href="#download">{% trans "Download" %}</a></li>#}
+            </ul>
+        </div>
+        <div id="header">
+            <a href="/"><img src="/media/static/img/logo-220.png" alt="Wolne Lektury" /></a>
+        </div>
+        {{ html|safe }}
+    </body>
+</html>
diff --git a/src/catalogue/templates/catalogue/chunk_add.html b/src/catalogue/templates/catalogue/chunk_add.html
new file mode 100755 (executable)
index 0000000..f813b6f
--- /dev/null
@@ -0,0 +1,20 @@
+{% extends "catalogue/base.html" %}
+{% load i18n %}
+
+
+{% block titleextra %}{% trans "Split chunk" %}{% endblock %}
+
+
+{% block content %}
+    <h1>{% trans "Split chunk" %}</h1>
+
+       <form enctype="multipart/form-data" method="POST">
+    {% csrf_token %}
+    <table class='editable'>
+        <tr><th>{% trans "Insert empty chunk after" %}:</th>
+            <td><a href="{{ chunk.get_absolute_url }}">{{ chunk.pretty_name }}</a></td></tr>
+        {{ form.as_table }}
+        <tr><td></td><td><button type="submit">{% trans "Add chunk" %}</button></td></tr>
+    </table>
+       </form>
+{% endblock content %}
diff --git a/src/catalogue/templates/catalogue/chunk_edit.html b/src/catalogue/templates/catalogue/chunk_edit.html
new file mode 100755 (executable)
index 0000000..2006226
--- /dev/null
@@ -0,0 +1,24 @@
+{% extends "catalogue/base.html" %}
+{% load i18n %}
+
+
+{% block titleextra %}{% trans "Chunk settings" %}{% endblock %}
+
+
+{% block content %}
+    <h1>{% trans "Chunk settings" %}</h1>
+
+       <form enctype="multipart/form-data" method="POST" action="{% if go_next %}?next={{ go_next }}{% endif %}">
+    {% csrf_token %}
+    <table class='editable'>
+        <tr><th>{% trans "Book" %}:</th><td>{{ chunk.book }} ({{ chunk.number }}/{{ chunk.book|length }})</td></tr>
+        {{ form.as_table}}
+        <tr><td></td><td><button type="submit">{% trans "Save" %}</button></td></tr>
+    </table>
+
+       </form>
+
+
+    <p><a href="{% url "catalogue_chunk_add" chunk.book.slug chunk.slug %}">{% trans "Split chunk" %}</a></p>
+
+{% endblock content %}
diff --git a/src/catalogue/templates/catalogue/document_create_missing.html b/src/catalogue/templates/catalogue/document_create_missing.html
new file mode 100644 (file)
index 0000000..aa2ce06
--- /dev/null
@@ -0,0 +1,18 @@
+{% extends "catalogue/base.html" %}
+{% load i18n %}
+
+
+{% block titleextra %}{% trans "Create a new book" %}{% endblock %}
+
+
+{% block content %}
+    <h1>{% trans "Create a new book" %}</h1>
+
+    <form enctype="multipart/form-data" method="POST">
+    {% csrf_token %}
+    <table class='editable'>
+        {{ form.as_table}}
+        <tr><td></td><td><button type="submit">{% trans "Create book" %}</button></td></tr>
+    </table>
+    </form>
+{% endblock content %}
diff --git a/src/catalogue/templates/catalogue/document_list.html b/src/catalogue/templates/catalogue/document_list.html
new file mode 100644 (file)
index 0000000..fe3598e
--- /dev/null
@@ -0,0 +1,20 @@
+{% extends "catalogue/base.html" %}
+
+{% load i18n %}
+{% load catalogue book_list %}
+{% load pipeline %}
+
+{% block titleextra %}{% trans "Book list" %}{% endblock %}
+
+
+{% block add_js %}
+{% javascript 'book_list' %}
+{% endblock %}
+
+{% block add_css %}
+{% stylesheet 'book_list' %}
+{% endblock %}
+
+{% block content %}
+    {% book_list %}
+{% endblock content %}
diff --git a/src/catalogue/templates/catalogue/document_upload.html b/src/catalogue/templates/catalogue/document_upload.html
new file mode 100644 (file)
index 0000000..009d154
--- /dev/null
@@ -0,0 +1,72 @@
+{% extends "catalogue/base.html" %}
+{% load i18n %}
+
+
+{% block titleextra %}{% trans "Bulk document upload" %}{% endblock %}
+
+
+{% block leftcolumn %}
+
+
+<h2>{% trans "Bulk documents upload" %}</h2>
+
+<p>
+{% trans "Please submit a ZIP with UTF-8 encoded XML files. Files not ending with <code>.xml</code> will be ignored." %}
+</p>
+
+<form enctype="multipart/form-data" method="POST" action="{{ request.path }}">
+{% csrf_token %}
+{{ form.as_p }}
+<p><button type="submit">{% trans "Upload" %}</button></p>
+</form>
+
+<hr/>
+
+{% if error_list %}
+
+    <p class='error'>{% trans "There have been some errors. No files have been added to the repository." %}
+    <h3>{% trans "Offending files" %}</h3>
+    <ul id='error-list'>
+        {% for filename, title, error in error_list %}
+            <li>{{ title }} (<code>{{ filename }}</code>): {{ error }}</li>
+        {% endfor %}
+    </ul>
+
+    {% if ok_list %}
+    <h3>{% trans "Correct files" %}</h3>
+        <ul>
+            {% for filename, slug, title in ok_list %}
+                <li>{{ title }} (<code>{{ filename }}</code>)</li>
+            {% endfor %}
+        </ul>
+    {% endif %}
+
+{% else %}
+
+    {% if ok_list %}
+        <p class='success'>{% trans "Files have been successfully uploaded to the repository." %}</p>
+        <h3>{% trans "Uploaded files" %}</h3>
+        <ul id='ok-list'>
+        {% for filename, slug, title in ok_list %}
+            <li><a href='{% url "wiki_editor" slug %}'>{{ title }}</a> (<code>{{ filename }})</a></li>
+        {% endfor %}
+        </ul>
+    {% endif %}
+{% endif %}
+
+{% if skipped_list %}
+    <h3>{% trans "Skipped files" %}</h3>
+    <p>{% trans "Files skipped due to no <code>.xml</code> extension" %}</p>
+    <ul id='skipped-list'>
+        {% for filename in skipped_list %}
+            <li>{{ filename }}</li>
+        {% endfor %}
+    </ul>
+{% endif %}
+
+
+{% endblock leftcolumn %}
+
+
+{% block rightcolumn %}
+{% endblock rightcolumn %}
diff --git a/src/catalogue/templates/catalogue/image_detail.html b/src/catalogue/templates/catalogue/image_detail.html
new file mode 100755 (executable)
index 0000000..8ad2a63
--- /dev/null
@@ -0,0 +1,71 @@
+{% extends "catalogue/base.html" %}
+{% load book_list i18n %}
+
+
+{% block titleextra %}{{ object.title }}{% endblock %}
+
+
+{% block content %}
+
+
+<h1>{{ object.title }}</h1>
+
+
+{% if editable %}<form method='POST'>{% csrf_token %}{% endif %}
+<table class='editable'><tbody>
+    {{ form.as_table }}
+    {% if editable %}
+        <tr><td></td><td><button type="submit">{% trans "Save" %}</button></td></tr>
+    {% endif %}
+</tbody></table>
+{% if editable %}</form>{% endif %}
+
+
+
+<div class='section'>
+    <h2>{% trans "Editor" %}</h2>
+
+    <p><a href="{% url 'wiki_img_editor' object.slug %}">{% trans "Proceed to the editor." %}</a></p>
+</div>
+
+
+
+<div class='section'>
+
+
+<h2>{% trans "Publication" %}</h2>
+
+<p>{% trans "Last published" %}: 
+    {% if object.last_published %}
+        {{ object.last_published }}
+    {% else %}
+        &mdash;
+    {% endif %}
+</p>
+
+{% if publishable %}
+    {% if user.is_authenticated %}
+        <!--
+        Angel photos:
+        Angels in Ely Cathedral (http://www.flickr.com/photos/21804434@N02/4483220595/) /
+        mira66 (http://www.flickr.com/photos/21804434@N02/) /
+        CC BY 2.0 (http://creativecommons.org/licenses/by/2.0/)
+        -->
+        <form method="POST" action="{% url 'catalogue_publish_image' object.slug %}">{% csrf_token %}
+            <!--img src="{{ STATIC_URL }}img/angel-left.png" style="vertical-align: middle" /-->
+            <button id="publish-button" type="submit">
+                <span>{% trans "Publish" %}</span></button>
+            <!--img src="{{ STATIC_URL }}img/angel-right.png" style="vertical-align: middle" /-->
+            </form>
+    {% else %}
+        <a href="{% url 'login' %}">{% trans "Log in to publish." %}</a>
+    {% endif %}
+{% else %}
+    <p>{% trans "This book can't be published yet, because:" %}</p>
+    <ul><li>{{ publishable_error }}</li></ul>
+{% endif %}
+
+</div>
+
+
+{% endblock content %}
diff --git a/src/catalogue/templates/catalogue/image_list.html b/src/catalogue/templates/catalogue/image_list.html
new file mode 100755 (executable)
index 0000000..4ce1668
--- /dev/null
@@ -0,0 +1,22 @@
+{% extends "catalogue/base.html" %}
+
+{% load i18n %}
+{% load catalogue book_list %}
+{% load pipeline %}
+
+
+{% block titleextra %}{% trans "Image list" %}{% endblock %}
+
+
+{% block add_js %}
+{% javascript 'book_list' %}
+{% endblock %}
+
+{% block add_css %}
+{% stylesheet 'book_list' %}
+{% endblock %}
+
+
+{% block content %}
+    {% image_list %}
+{% endblock content %}
diff --git a/src/catalogue/templates/catalogue/image_short.html b/src/catalogue/templates/catalogue/image_short.html
new file mode 100755 (executable)
index 0000000..c7ff77b
--- /dev/null
@@ -0,0 +1,21 @@
+{% load i18n %}
+{% load username from common_tags %}
+
+<tr>
+    <td><input type="checkbox" name="select_chunk" value="{{image.id}}"/></td>
+    <td><a href="{% url 'catalogue_image' image.slug %}" title='{% trans "Image settings" %}'>[B]</a></td>
+    <td><a target="_blank"
+                href="{% url 'wiki_img_editor' image.slug %}">
+                {{ image.title }}</a></td>
+    <td>{% if image.stage %}
+        {{ image.stage }}
+    {% else %}–
+    {% endif %}</td>
+    <td class='user-column'>{% if image.user %}<a href="{% url 'catalogue_user' image.user.username %}">{{ image.user|username }}</a>{% endif %}</td>
+    <td>
+        {% if image.published %}P{% endif %}
+        {% if image.new_publishable %}p{% endif %}
+        {% if image.changed %}+{% endif %}
+    </td>
+    <td>{{ image.project.name }}</td>
+</tr>
diff --git a/src/catalogue/templates/catalogue/image_table.html b/src/catalogue/templates/catalogue/image_table.html
new file mode 100755 (executable)
index 0000000..e6caedd
--- /dev/null
@@ -0,0 +1,100 @@
+{% load i18n %}
+{% load pagination_tags %}
+{% load username from common_tags %}
+
+
+
+<form name='filter' action='{{ request.path }}'>
+<input type='hidden' name="title" value="{{ request.GET.title }}" />
+<input type='hidden' name="stage" value="{{ request.GET.stage }}" />
+{% if not viewed_user %}
+    <input type='hidden' name="user" value="{{ request.GET.user }}" />
+{% endif %}
+<input type='hidden' name="status" value="{{ request.GET.status }}" />
+<input type='hidden' name="project" value="{{ request.GET.project }}" />
+</form>
+
+<table id="file-list"{% if viewed_user %} class="book-list-user"{% endif %}>
+    <thead><tr>
+        <th></th>
+        <th></th>
+        <th class='book-search-column'>
+            <form>
+            <input title='{% trans "Search in book titles" %}' name="title"
+                class='text-filter' value="{{ request.GET.title }}" />
+            </form>
+        </th>
+        <th><select name="stage" class="filter">
+            <option value=''>- {% trans "stage" %} -</option>
+            <option {% if request.GET.stage == '-' %}selected="selected"
+                    {% endif %}value="-">- {% trans "none" %} -</option>
+            {% for stage in stages %}
+                <option {% if request.GET.stage == stage.slug %}selected="selected"
+                    {% endif %}value="{{ stage.slug }}">{{ stage.name }}</option>
+            {% endfor %}
+        </select></th>
+
+        {% if not viewed_user %}
+            <th><select name="user" class="filter">
+                <option value=''>- {% trans "editor" %} -</option>
+                <option {% if request.GET.user == '-' %}selected="selected"
+                        {% endif %}value="-">- {% trans "none" %} -</option>
+                {% for user in users %}
+                    <option {% if request.GET.user == user.username %}selected="selected"
+                        {% endif %}value="{{ user.username }}">{{ user|username }} ({{ user.count }})</option>
+                {% endfor %}
+            </select></th>
+        {% endif %}
+
+        <th><select name="status" class="filter">
+            <option value=''>- {% trans "status" %} -</option>
+            {% for state, label in states %}
+                <option {% if request.GET.status == state %}selected="selected"
+                        {% endif %}value='{{ state }}'>{{ label }}</option>
+            {% endfor %}
+        </select></th>
+
+        <th><select name="project" class="filter">
+            <option value=''>- {% trans "project" %} -</option>
+                <option {% if request.GET.project == '-' %}selected="selected"
+                        {% endif %}value="-">- {% trans "none" %} -</option>
+            {% for project in projects %}
+                <option {% if request.GET.project == project.pk|slugify %}selected="selected"
+                        {% endif %}value='{{ project.pk }}'>{{ project.name }}</option>
+            {% endfor %}
+        </select></th>
+
+    </tr></thead>
+
+    {% with cnt=objects|length %}
+    {% autopaginate objects 100 %}
+    <tbody>
+    {% for item in objects %}
+        {{ item.short_html|safe }}
+    {% endfor %}
+    <tr><th class='paginator' colspan="6">
+        {% paginate %}
+        {% blocktrans count c=cnt %}{{c}} image{% plural %}{{c}} images{% endblocktrans %}</th></tr>
+    </tbody>
+    {% endwith %}
+</table>
+{% if not objects %}
+    <p>{% trans "No images found." %}</p>
+{% endif %}
+
+<form id='chunk_mass_edit' action='{% url "catalogue_image_mass_edit" %}' style="display:none;">
+{% csrf_token %}
+<input type="hidden" name="ids" />
+<label for="mass_edit_stage">{% trans "Set stage" %}</label><input type="hidden" name="stage" id="mass_edit_stage"/>
+<label for="mass_edit_user">{% trans "Set user" %}</label><input type="hidden" name="user" id="mass_edit_stage" />
+<input type="hidden" name="status" />
+<label for="mass_edit_project">{% trans "Project" %}</label><input type="hidden" name="project" id="mass_edit_project" />
+<label for="mass_edit_more_users">{% trans "More users" %}</label>
+</form>
+
+<select name="other-user" style="display:none;">
+  {% for user in other_users %}
+  <option {% if request.GET.user == user.username %}selected="selected"
+          {% endif %}value="{{ user.username }}">{{ user|username }} ({{ user.count }})</option>
+  {% endfor %}
+</select>
diff --git a/src/catalogue/templates/catalogue/main_tabs.html b/src/catalogue/templates/catalogue/main_tabs.html
new file mode 100755 (executable)
index 0000000..82321cc
--- /dev/null
@@ -0,0 +1,3 @@
+{% for tab in tabs %}
+    <a {% ifequal active_tab tab.slug %}class="active" {% endifequal %}href="{{ tab.url }}">{{ tab.caption }}</a>
+{% endfor %}
diff --git a/src/catalogue/templates/catalogue/mark_final.html b/src/catalogue/templates/catalogue/mark_final.html
new file mode 100644 (file)
index 0000000..9ed740c
--- /dev/null
@@ -0,0 +1,16 @@
+{% extends "catalogue/base.html" %}
+
+{% block titleextra %}Oznacz książki{% endblock %}
+
+
+{% block leftcolumn %}
+
+<h1>Oznacz książki</h1>
+
+<form method="post" action="">
+    {% csrf_token %}
+    {{ form.as_p }}
+    <input type="submit" value="Oznacz">
+</form>
+
+{% endblock leftcolumn %}
\ No newline at end of file
diff --git a/src/catalogue/templates/catalogue/mark_final_completed.html b/src/catalogue/templates/catalogue/mark_final_completed.html
new file mode 100644 (file)
index 0000000..1b37c83
--- /dev/null
@@ -0,0 +1,12 @@
+{% extends "catalogue/base.html" %}
+
+{% block titleextra %}Oznaczono książki{% endblock %}
+
+
+{% block leftcolumn %}
+
+<h1>Oznaczono książki</h1>
+
+<p>Książki zostały oznaczone.</p>
+
+{% endblock leftcolumn %}
\ No newline at end of file
diff --git a/src/catalogue/templates/catalogue/my_page.html b/src/catalogue/templates/catalogue/my_page.html
new file mode 100755 (executable)
index 0000000..d334750
--- /dev/null
@@ -0,0 +1,41 @@
+{% extends "catalogue/base.html" %}
+
+{% load i18n %}
+{% load catalogue book_list wall %}
+{% load pipeline %}
+
+{% block add_js %}
+{% javascript 'book_list' %}
+{% endblock %}
+
+{% block add_css %}
+{% stylesheet 'book_list' %}
+{% endblock %}
+
+{% block titleextra %}{% trans "My page" %}{% endblock %}
+
+
+{% block leftcolumn %}
+    {% book_list request.user %}
+{% endblock leftcolumn %}
+
+{% block rightcolumn %}
+    <div id="last-edited-list">
+        <h2>{% trans "Your last edited documents" %}</h2>
+        <ol>
+            {% for edit_url, item in last_books %}
+                <li><a
+                {% if edit_url|length == 2 %}
+                    {# Temporary support for old-style last_books. #}
+                    href="{% url 'wiki_editor' edit_url.0 edit_url.1 %}"
+                {% else %}
+                    href="{{ edit_url }}"
+                {% endif %}
+                target="_blank">{{ item.title }}</a><br/><span class="date">({{ item.time|date:"H:i:s, d/m/Y" }})</span></li>
+            {% endfor %}
+        </ol>
+    </div>
+
+    <h2>{% trans "Recent activity for" %} {{ request.user|nice_name }}</h2>
+    {% wall request.user 10 %}
+{% endblock rightcolumn %}
diff --git a/src/catalogue/templates/catalogue/upload_pdf.html b/src/catalogue/templates/catalogue/upload_pdf.html
new file mode 100755 (executable)
index 0000000..265b84a
--- /dev/null
@@ -0,0 +1,20 @@
+{% extends "catalogue/base.html" %}
+{% load i18n %}
+
+
+{% block titleextra %}{% trans "PDF file upload" %}{% endblock %}
+
+
+{% block content %}
+
+
+<h2>{% trans "PDF file upload" %}</h2>
+
+<form enctype="multipart/form-data" method="POST" action="">
+{% csrf_token %}
+{{ form.as_p }}
+<p><button type="submit">{% trans "Upload" %}</button></p>
+</form>
+
+
+{% endblock content %}
diff --git a/src/catalogue/templates/catalogue/user_list.html b/src/catalogue/templates/catalogue/user_list.html
new file mode 100755 (executable)
index 0000000..460a38b
--- /dev/null
@@ -0,0 +1,23 @@
+{% extends "catalogue/base.html" %}
+
+{% load i18n %}
+{% load username from common_tags %}
+
+
+{% block titleextra %}{% trans "Users" %}{% endblock %}
+
+
+{% block leftcolumn %}
+
+<h1>{% trans "Users" %}</h1>
+
+<ul>
+{% for user in users %}
+    <li><a href="{% url 'catalogue_user' user.username %}">
+        <span class="chunkno">{{ forloop.counter }}.</span>
+        {{ user|username }}</a>
+        ({{ user.count }})</li>
+{% endfor %}
+</ul>
+
+{% endblock leftcolumn %}
diff --git a/src/catalogue/templates/catalogue/user_page.html b/src/catalogue/templates/catalogue/user_page.html
new file mode 100755 (executable)
index 0000000..4be4ca3
--- /dev/null
@@ -0,0 +1,18 @@
+{% extends "catalogue/base.html" %}
+
+{% load i18n %}
+{% load catalogue book_list wall %}
+
+
+{% block titleextra %}{{ viewed_user|nice_name }}{% endblock %}
+
+
+{% block leftcolumn %}
+    <h1>{{ viewed_user|nice_name }}</h1>
+    {% book_list viewed_user %}
+{% endblock leftcolumn %}
+
+{% block rightcolumn %}
+    <h2>{% trans "Recent activity for" %} {{ viewed_user|nice_name }}</h2>
+    {% wall viewed_user 10 %}
+{% endblock rightcolumn %}
diff --git a/src/catalogue/templates/catalogue/wall.html b/src/catalogue/templates/catalogue/wall.html
new file mode 100755 (executable)
index 0000000..a107dfa
--- /dev/null
@@ -0,0 +1,37 @@
+{% load i18n %}
+{% load gravatar %}
+{% load email %}
+{% load username from common_tags %}
+
+<ul class='wall'>
+{% for item in wall %}
+    <li class="{{ item.tag }}{% if not item.user %} anonymous{% endif %}">
+        <div class='gravatar'>
+            {% if item.get_email %}
+                <img src="{% gravatar_url item.get_email 32 %}"
+                    height="32" width="32" alt='Avatar' />
+                <br/>
+            {% endif %}
+        </div>
+
+        <div class="time">{{ item.timestamp }}</div>
+        <h3>{{ item.header }}</h3>
+        <a target="_blank" href='{{ item.url }}'>{{ item.title }}</a>
+        <br/><strong>{% trans "user" %}:</strong>
+        {% if item.user %}
+            <a href="{% url 'catalogue_user' item.user.username %}">
+            {{ item.user|username }}</a>
+            &lt;{{ item.user.email|email_link }}>
+        {% else %}
+            {{ item.user_name }}
+            {% if item.email %}
+                &lt;{{ item.email|email_link }}>
+            {% endif %}
+            ({% trans "not logged in" %})
+        {% endif %}
+        <br/>{{ item.summary|linebreaksbr }}
+    </li>
+{% empty %}
+    <li>{% trans "No activity recorded." %}</li>
+{% endfor %}
+</ul>
diff --git a/src/catalogue/templatetags/__init__.py b/src/catalogue/templatetags/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/catalogue/templatetags/book_list.py b/src/catalogue/templatetags/book_list.py
new file mode 100755 (executable)
index 0000000..9ac996b
--- /dev/null
@@ -0,0 +1,209 @@
+from __future__ import absolute_import
+
+from re import split
+from django.db.models import Q, Count
+from django import template
+from django.utils.translation import ugettext_lazy as _
+from django.contrib.auth.models import User
+from catalogue.models import Chunk, Image, Project
+
+register = template.Library()
+
+
+class ChunksList(object):
+    def __init__(self, chunk_qs):
+        #self.chunk_qs = chunk_qs#.annotate(
+            #book_length=Count('book__chunk')).select_related(
+            #'book')#, 'stage__name',
+            #'user')
+        self.chunk_qs = chunk_qs.select_related('book__hidden')
+
+        self.book_qs = chunk_qs.values('book_id')
+
+    def __getitem__(self, key):
+        if isinstance(key, slice):
+            return self.get_slice(key)
+        elif isinstance(key, int):
+            return self.get_slice(slice(key, key+1))[0]
+        else:
+            raise TypeError('Unsupported list index. Must be a slice or an int.')
+
+    def __len__(self):
+        return self.book_qs.count()
+
+    def get_slice(self, slice_):
+        book_ids = [x['book_id'] for x in self.book_qs[slice_]]
+        chunk_qs = self.chunk_qs.filter(book__in=book_ids)
+
+        chunks_list = []
+        book = None
+        for chunk in chunk_qs:
+            if chunk.book != book:
+                book = chunk.book
+                chunks_list.append(ChoiceChunks(book, [chunk]))
+            else:
+                chunks_list[-1].chunks.append(chunk)
+        return chunks_list
+
+
+class ChoiceChunks(object):
+    """
+        Associates the given chunks iterable for a book.
+    """
+
+    chunks = None
+
+    def __init__(self, book, chunks):
+        self.book = book
+        self.chunks = chunks
+
+
+def foreign_filter(qs, value, filter_field, model, model_field='slug', unset='-'):
+    if value == unset:
+        return qs.filter(**{filter_field: None})
+    if not value:
+        return qs
+    try:
+        obj = model._default_manager.get(**{model_field: value})
+    except model.DoesNotExist:
+        return qs.none()
+    else:
+        return qs.filter(**{filter_field: obj})
+
+
+def search_filter(qs, value, filter_fields):
+    if not value:
+        return qs
+    q = Q(**{"%s__icontains" % filter_fields[0]: value})
+    for field in filter_fields[1:]:
+        q |= Q(**{"%s__icontains" % field: value})
+    return qs.filter(q)
+
+
+_states = [
+        ('publishable', _('publishable'), Q(book___new_publishable=True)),
+        ('changed', _('changed'), Q(_changed=True)),
+        ('published', _('published'), Q(book___published=True)),
+        ('unpublished', _('unpublished'), Q(book___published=False)),
+        ('empty', _('empty'), Q(head=None)),
+    ]
+_states_options = [s[:2] for s in _states]
+_states_dict = dict([(s[0], s[2]) for s in _states])
+
+
+def document_list_filter(request, **kwargs):
+
+    def arg_or_GET(field):
+        return kwargs.get(field, request.GET.get(field))
+
+    if arg_or_GET('all'):
+        chunks = Chunk.objects.all()
+    else:
+        chunks = Chunk.visible_objects.all()
+
+    chunks = chunks.order_by('book__title', 'book', 'number')
+
+    if not request.user.is_authenticated():
+        chunks = chunks.filter(book__public=True)
+
+    state = arg_or_GET('status')
+    if state in _states_dict:
+        chunks = chunks.filter(_states_dict[state])
+
+    chunks = foreign_filter(chunks, arg_or_GET('user'), 'user', User, 'username')
+    chunks = foreign_filter(chunks, arg_or_GET('stage'), 'stage', Chunk.tag_model, 'slug')
+    chunks = search_filter(chunks, arg_or_GET('title'), ['book__title', 'title'])
+    chunks = foreign_filter(chunks, arg_or_GET('project'), 'book__project', Project, 'pk')
+    return chunks
+
+
+@register.inclusion_tag('catalogue/book_list/book_list.html', takes_context=True)
+def book_list(context, user=None):
+    request = context['request']
+
+    if user:
+        filters = {"user": user}
+        new_context = {"viewed_user": user}
+    else:
+        filters = {}
+        new_context = {
+            "users": User.objects.annotate(
+                count=Count('chunk')).filter(count__gt=0).order_by(
+                '-count', 'last_name', 'first_name'),
+            "other_users": User.objects.annotate(
+                count=Count('chunk')).filter(count=0).order_by(
+                'last_name', 'first_name'),
+                }
+
+    new_context.update({
+        "filters": True,
+        "request": request,
+        "books": ChunksList(document_list_filter(request, **filters)),
+        "stages": Chunk.tag_model.objects.all(),
+        "states": _states_options,
+        "projects": Project.objects.all(),
+    })
+
+    return new_context
+
+
+
+_image_states = [
+        ('publishable', _('publishable'), Q(_new_publishable=True)),
+        ('changed', _('changed'), Q(_changed=True)),
+        ('published', _('published'), Q(_published=True)),
+        ('unpublished', _('unpublished'), Q(_published=False)),
+        ('empty', _('empty'), Q(head=None)),
+    ]
+_image_states_options = [s[:2] for s in _image_states]
+_image_states_dict = dict([(s[0], s[2]) for s in _image_states])
+
+def image_list_filter(request, **kwargs):
+
+    def arg_or_GET(field):
+        return kwargs.get(field, request.GET.get(field))
+
+    images = Image.objects.all()
+
+    if not request.user.is_authenticated():
+        images = images.filter(public=True)
+
+    state = arg_or_GET('status')
+    if state in _image_states_dict:
+        images = images.filter(_image_states_dict[state])
+
+    images = foreign_filter(images, arg_or_GET('user'), 'user', User, 'username')
+    images = foreign_filter(images, arg_or_GET('stage'), 'stage', Image.tag_model, 'slug')
+    images = search_filter(images, arg_or_GET('title'), ['title', 'title'])
+    images = foreign_filter(images, arg_or_GET('project'), 'project', Project, 'pk')
+    return images
+
+
+@register.inclusion_tag('catalogue/image_table.html', takes_context=True)
+def image_list(context, user=None):
+    request = context['request']
+
+    if user:
+        filters = {"user": user}
+        new_context = {"viewed_user": user}
+    else:
+        filters = {}
+        new_context = {
+            "users": User.objects.annotate(
+                count=Count('image')).filter(count__gt=0).order_by(
+                '-count', 'last_name', 'first_name'),
+            "other_users": User.objects.annotate(
+                count=Count('image')).filter(count=0).order_by(
+                'last_name', 'first_name'),
+                }
+
+    new_context.update({
+        "filters": True,
+        "request": request,
+        "objects": image_list_filter(request, **filters),
+        "stages": Image.tag_model.objects.all(),
+        "states": _image_states_options,
+        "projects": Project.objects.all(),
+    })
+
+    return new_context
diff --git a/src/catalogue/templatetags/catalogue.py b/src/catalogue/templatetags/catalogue.py
new file mode 100644 (file)
index 0000000..07c5cf9
--- /dev/null
@@ -0,0 +1,46 @@
+from __future__ import absolute_import
+
+from django.core.urlresolvers import reverse
+from django import template
+from django.utils.translation import ugettext as _
+
+register = template.Library()
+
+
+class Tab(object):
+    slug = None
+    caption = None
+    url = None
+
+    def __init__(self, slug, caption, url):
+        self.slug = slug
+        self.caption = caption
+        self.url = url
+
+
+@register.inclusion_tag("catalogue/main_tabs.html", takes_context=True)
+def main_tabs(context):
+    active = getattr(context['request'], 'catalogue_active_tab', None)
+
+    tabs = []
+    user = context['user']
+    tabs.append(Tab('my', _('My page'), reverse("catalogue_user")))
+
+    tabs.append(Tab('activity', _('Activity'), reverse("catalogue_activity")))
+    tabs.append(Tab('all', _('All'), reverse("catalogue_document_list")))
+    tabs.append(Tab('images', _('Images'), reverse("catalogue_image_list")))
+    tabs.append(Tab('users', _('Users'), reverse("catalogue_users")))
+
+    if user.has_perm('catalogue.add_book'):
+        tabs.append(Tab('create', _('Add'), reverse("catalogue_create_missing")))
+        tabs.append(Tab('upload', _('Upload'), reverse("catalogue_upload")))
+
+    tabs.append(Tab('cover', _('Covers'), reverse("cover_image_list")))
+
+    return {"tabs": tabs, "active_tab": active}
+
+
+@register.filter
+def nice_name(user):
+    return user.get_full_name() or user.username
+
diff --git a/src/catalogue/templatetags/common_tags.py b/src/catalogue/templatetags/common_tags.py
new file mode 100755 (executable)
index 0000000..7f5d0e9
--- /dev/null
@@ -0,0 +1,6 @@
+from django import template
+register = template.Library()
+
+@register.filter
+def username(user):
+    return ("%s %s" % (user.first_name, user.last_name)).lstrip() or user.username
diff --git a/src/catalogue/templatetags/set_get_parameter.py b/src/catalogue/templatetags/set_get_parameter.py
new file mode 100755 (executable)
index 0000000..b3d44d7
--- /dev/null
@@ -0,0 +1,46 @@
+from re import split
+
+from django import template
+
+register = template.Library()
+
+
+"""
+In template:
+    {% set_get_paramater param1='const_value',param2=,param3=variable %}
+results with changes to query string:
+    param1 is set to `const_value' string
+    param2 is unset, if exists,
+    param3 is set to the value of variable in context
+
+Using 'django.core.context_processors.request' is required.
+
+"""
+
+
+class SetGetParameter(template.Node):
+    def __init__(self, values):
+        self.values = values
+        
+    def render(self, context):
+        request = template.Variable('request').resolve(context)
+        params = request.GET.copy()
+        for key, value in self.values.items():
+            if value == '':
+                if key in params:
+                    del(params[key])
+            else:
+                params[key] = template.Variable(value).resolve(context)
+        return '?%s' %  params.urlencode()
+
+
+@register.tag
+def set_get_parameter(parser, token):
+    parts = split(r'\s+', token.contents, 2)
+
+    values = {}
+    for pair in parts[1].split(','):
+        s = pair.split('=')
+        values[s[0]] = s[1]
+
+    return SetGetParameter(values)
diff --git a/src/catalogue/templatetags/wall.py b/src/catalogue/templatetags/wall.py
new file mode 100755 (executable)
index 0000000..d000421
--- /dev/null
@@ -0,0 +1,210 @@
+from __future__ import absolute_import
+
+from datetime import timedelta
+from django.db.models import Q
+from django.core.urlresolvers import reverse
+from django.contrib.comments.models import Comment
+from django import template
+from django.utils.translation import ugettext as _
+
+from catalogue.models import Chunk, BookPublishRecord, Image, ImagePublishRecord
+
+register = template.Library()
+
+
+class WallItem(object):
+    title = ''
+    summary = ''
+    url = ''
+    timestamp = ''
+    user = None
+    user_name = ''
+    email = ''
+
+    def __init__(self, tag):
+        self.tag = tag
+
+    def get_email(self):
+        if self.user:
+            return self.user.email
+        else:
+            return self.email
+
+
+def changes_wall(user=None, max_len=None, day=None):
+    qs = Chunk.change_model.objects.order_by('-created_at')
+    qs = qs.select_related('author', 'tree', 'tree__book__title')
+    if user is not None:
+        qs = qs.filter(Q(author=user) | Q(tree__user=user))
+    if max_len is not None:
+        qs = qs[:max_len]
+    if day is not None:
+        next_day = day + timedelta(1)
+        qs = qs.filter(created_at__gte=day, created_at__lt=next_day)
+    for item in qs:
+        tag = 'stage' if item.tags.count() else 'change'
+        chunk = item.tree
+        w = WallItem(tag)
+        if user and item.author != user:
+            w.header = _('Related edit')
+        else:
+            w.header = _('Edit')
+        w.title = chunk.pretty_name()
+        w.summary = item.description
+        w.url = reverse('wiki_editor', 
+                args=[chunk.book.slug, chunk.slug]) + '?diff=%d' % item.revision
+        w.timestamp = item.created_at
+        w.user = item.author
+        w.user_name = item.author_name
+        w.email = item.author_email
+        yield w
+
+
+def image_changes_wall(user=None, max_len=None, day=None):
+    qs = Image.change_model.objects.order_by('-created_at')
+    qs = qs.select_related('author', 'tree', 'tree__title')
+    if user is not None:
+        qs = qs.filter(Q(author=user) | Q(tree__user=user))
+    if max_len is not None:
+        qs = qs[:max_len]
+    if day is not None:
+        next_day = day + timedelta(1)
+        qs = qs.filter(created_at__gte=day, created_at__lt=next_day)
+    for item in qs:
+        tag = 'stage' if item.tags.count() else 'change'
+        image = item.tree
+        w  = WallItem(tag)
+        if user and item.author != user:
+            w.header = _('Related edit')
+        else:
+            w.header = _('Edit')
+        w.title = image.title
+        w.summary = item.description
+        w.url = reverse('wiki_img_editor', 
+                args=[image.slug]) + '?diff=%d' % item.revision
+        w.timestamp = item.created_at
+        w.user = item.author
+        w.user_name = item.author_name
+        w.email = item.author_email
+        yield w
+
+
+
+# TODO: marked for publishing
+
+
+def published_wall(user=None, max_len=None, day=None):
+    qs = BookPublishRecord.objects.select_related('book__title')
+    if user:
+        # TODO: published my book
+        qs = qs.filter(Q(user=user))
+    if max_len is not None:
+        qs = qs[:max_len]
+    if day is not None:
+        next_day = day + timedelta(1)
+        qs = qs.filter(timestamp__gte=day, timestamp__lt=next_day)
+    for item in qs:
+        w = WallItem('publish')
+        w.header = _('Publication')
+        w.title = item.book.title
+        w.timestamp = item.timestamp
+        w.url = item.book.get_absolute_url()
+        w.user = item.user
+        w.email = item.user.email
+        yield w
+
+
+def image_published_wall(user=None, max_len=None, day=None):
+    qs = ImagePublishRecord.objects.select_related('image__title')
+    if user:
+        # TODO: published my book
+        qs = qs.filter(Q(user=user))
+    if max_len is not None:
+        qs = qs[:max_len]
+    if day is not None:
+        next_day = day + timedelta(1)
+        qs = qs.filter(timestamp__gte=day, timestamp__lt=next_day)
+    for item in qs:
+        w = WallItem('publish')
+        w.header = _('Publication')
+        w.title = item.image.title
+        w.timestamp = item.timestamp
+        w.url = item.image.get_absolute_url()
+        w.user = item.user
+        w.email = item.user.email
+        yield w
+
+
+def comments_wall(user=None, max_len=None, day=None):
+    qs = Comment.objects.filter(is_public=True).select_related().order_by('-submit_date')
+    if user:
+        # TODO: comments concerning my books
+        qs = qs.filter(Q(user=user))
+    if max_len is not None:
+        qs = qs[:max_len]
+    if day is not None:
+        next_day = day + timedelta(1)
+        qs = qs.filter(submit_date__gte=day, submit_date__lt=next_day)
+    for item in qs:
+        w  = WallItem('comment')
+        w.header = _('Comment')
+        w.title = item.content_object
+        w.summary = item.comment
+        w.url = item.content_object.get_absolute_url()
+        w.timestamp = item.submit_date
+        w.user = item.user
+        ui = item.userinfo
+        w.email = item.email
+        w.user_name = item.name
+        yield w
+
+
+def big_wall(walls, max_len=None):
+    """
+        Takes some WallItem iterators and zips them into one big wall.
+        Input iterators must already be sorted by timestamp.
+    """
+    subwalls = []
+    for w in walls:
+        try:
+            subwalls.append([next(w), w])
+        except StopIteration:
+            pass
+
+    if max_len is None:
+        max_len = -1
+    while max_len and subwalls:
+        i, next_item = max(enumerate(subwalls), key=lambda x: x[1][0].timestamp)
+        yield next_item[0]
+        max_len -= 1
+        try:
+            next_item[0] = next(next_item[1])
+        except StopIteration:
+            del subwalls[i]
+
+
+@register.inclusion_tag("catalogue/wall.html", takes_context=True)
+def wall(context, user=None, max_len=100):
+    return {
+        "request": context['request'],
+        "STATIC_URL": context['STATIC_URL'],
+        "wall": big_wall([
+            changes_wall(user, max_len),
+            published_wall(user, max_len),
+            image_changes_wall(user, max_len),
+            image_published_wall(user, max_len),
+            comments_wall(user, max_len),
+        ], max_len)}
+
+@register.inclusion_tag("catalogue/wall.html", takes_context=True)
+def day_wall(context, day):
+    return {
+        "request": context['request'],
+        "STATIC_URL": context['STATIC_URL'],
+        "wall": big_wall([
+            changes_wall(day=day),
+            published_wall(day=day),
+            image_changes_wall(day=day),
+            image_published_wall(day=day),
+            comments_wall(day=day),
+        ])}
diff --git a/src/catalogue/test_utils.py b/src/catalogue/test_utils.py
new file mode 100644 (file)
index 0000000..2b08545
--- /dev/null
@@ -0,0 +1,14 @@
+# -*- coding: utf-8 -*-
+#
+# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
+"""Testing utilities."""
+
+from os.path import abspath, dirname, join
+
+
+def get_fixture(path):
+    f_path = join(dirname(abspath(__file__)), 'tests/files', path)
+    with open(f_path) as f:
+        return unicode(f.read(), 'utf-8')
diff --git a/src/catalogue/tests/__init__.py b/src/catalogue/tests/__init__.py
new file mode 100644 (file)
index 0000000..533a6c5
--- /dev/null
@@ -0,0 +1,9 @@
+# -*- coding: utf-8 -*-
+#
+# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
+from catalogue.tests.book import *
+from catalogue.tests.gallery import *
+from catalogue.tests.publish import *
+from catalogue.tests.xml_updater import *
diff --git a/src/catalogue/tests/book.py b/src/catalogue/tests/book.py
new file mode 100644 (file)
index 0000000..df6f3b4
--- /dev/null
@@ -0,0 +1,54 @@
+# -*- coding: utf-8 -*-
+#
+# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
+"""Tests for manipulating books in the catalogue."""
+
+from django.test import TestCase
+from django.contrib.auth.models import User
+from catalogue.models import Book
+
+
+class ManipulationTests(TestCase):
+
+    def setUp(self):
+        self.user = User.objects.create(username='tester')
+        self.book1 = Book.create(self.user, 'book 1', slug='book1')
+        self.book2 = Book.create(self.user, 'book 2', slug='book2')
+
+    def test_append(self):
+        self.book1.append(self.book2)
+        self.assertEqual(Book.objects.all().count(), 1)
+        self.assertEqual(len(self.book1), 2)
+
+    def test_append_to_self(self):
+        with self.assertRaises(AssertionError):
+            self.book1.append(Book.objects.get(pk=self.book1.pk))
+        self.assertEqual(Book.objects.all().count(), 2)
+        self.assertEqual(len(self.book1), 1)
+
+    def test_prepend_history(self):
+        self.book1.prepend_history(self.book2)
+        self.assertEqual(Book.objects.all().count(), 1)
+        self.assertEqual(len(self.book1), 1)
+        self.assertEqual(self.book1.materialize(), 'book 1')
+
+    def test_prepend_history_to_self(self):
+        with self.assertRaises(AssertionError):
+            self.book1.prepend_history(self.book1)
+        self.assertEqual(Book.objects.all().count(), 2)
+        self.assertEqual(self.book1.materialize(), 'book 1')
+        self.assertEqual(self.book2.materialize(), 'book 2')
+
+    def test_split_book(self):
+        self.book1.chunk_set.create(number=2, title='Second chunk',
+                slug='book3')
+        self.book1[1].commit('I survived!')
+        self.assertEqual(len(self.book1), 2)
+        self.book1.split()
+        self.assertEqual(set([b.slug for b in Book.objects.all()]),
+                set(['book2', '1', 'book3']))
+        self.assertEqual(
+                Book.objects.get(slug='book3').materialize(),
+                'I survived!')
diff --git a/src/catalogue/tests/files/chunk1.xml b/src/catalogue/tests/files/chunk1.xml
new file mode 100755 (executable)
index 0000000..6a75580
--- /dev/null
@@ -0,0 +1,42 @@
+<utwor>
+  <liryka_l>
+
+<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+<rdf:Description rdf:about="http://example.com/documents/book/test-book/">
+<dc:creator xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">Mickiewicz, Adam</dc:creator>
+<dc:title xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">Do M***</dc:title>
+<dc:publisher xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">Fundacja Nowoczesna Polska</dc:publisher>
+<dc:subject.period xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">Romantyzm</dc:subject.period>
+<dc:subject.type xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">Liryka</dc:subject.type>
+<dc:subject.genre xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">Wiersz</dc:subject.genre>
+<!--dc:description xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">Publikacja zrealizowana w ramach projektu Wolne Lektury (http://wolnelektury.pl). Reprodukcja cyfrowa wykonana przez Bibliotekę Narodową z egzemplarza pochodzącego ze zbiorów BN.</dc:description-->
+<dc:identifier.url xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">http://wolnelektury.pl/katalog/lektura/sonety-odeskie-do-m</dc:identifier.url>
+<dc:source.URL xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">http://www.polona.pl/Content/2222</dc:source.URL>
+<dc:source xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">Mickiewicz, Adam (1798-1855), Poezje, tom 1 (Wiersze młodzieńcze - Ballady i romanse - Wiersze do r. 1824), Krakowska Spółdzielnia Wydawnicza, wyd. 2 zwiększone, Kraków, 1922</dc:source>
+
+<dc:rights xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">Domena publiczna - Adam Mickiewicz zm. 1855</dc:rights>
+<dc:date.pd xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">1926</dc:date.pd>
+<dc:format xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">xml</dc:format>
+<dc:type xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">text</dc:type>
+<dc:type xml:lang="en" xmlns:dc="http://purl.org/dc/elements/1.1/">text</dc:type>
+<dc:date xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">2007-09-06</dc:date>
+<dc:language xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">pol</dc:language>
+
+</rdf:Description>
+</rdf:RDF>
+
+<autor_utworu>Adam Mickiewicz</autor_utworu>
+<dzielo_nadrzedne>Sonety odeskie</dzielo_nadrzedne>
+<nazwa_utworu>Do M***</nazwa_utworu>
+
+<nota><akap>Wiérsz napisany w roku 1822</akap></nota>
+
+
+<strofa>Precz z moich oczu!... posłucham od razu,/
+Precz z mego serca!... i serce posłucha,/
+Precz z méj pamięci!... Nie! tego rozkazu/
+Moja i twoja pamięć nie posłucha.</strofa>
+
+<!-- TRIM_END -->
+</liryka_l>
+</utwor>
diff --git a/src/catalogue/tests/files/chunk2.xml b/src/catalogue/tests/files/chunk2.xml
new file mode 100755 (executable)
index 0000000..63a243e
--- /dev/null
@@ -0,0 +1,11 @@
+<utwor><liryka_l>
+<!-- TRIM_BEGIN -->
+
+<strofa>Jak cień tém dłuższy, gdy padnie z daleka,/
+Tém szerzéj koło żałobne roztoczy,/
+Tak moja postać, im daléj ucieka,/
+Tém grubszym kirem twą pamięć pomroczy.</strofa>
+
+
+</liryka_l>
+</utwor>
diff --git a/src/catalogue/tests/files/expected.xml b/src/catalogue/tests/files/expected.xml
new file mode 100755 (executable)
index 0000000..ff225a0
--- /dev/null
@@ -0,0 +1,49 @@
+<utwor>
+  <liryka_l>
+
+<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+<rdf:Description rdf:about="http://example.com/documents/book/test-book/">
+<dc:creator xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">Mickiewicz, Adam</dc:creator>
+<dc:title xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">Do M***</dc:title>
+<dc:publisher xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">Fundacja Nowoczesna Polska</dc:publisher>
+<dc:subject.period xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">Romantyzm</dc:subject.period>
+<dc:subject.type xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">Liryka</dc:subject.type>
+<dc:subject.genre xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">Wiersz</dc:subject.genre>
+<!--dc:description xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">Publikacja zrealizowana w ramach projektu Wolne Lektury (http://wolnelektury.pl). Reprodukcja cyfrowa wykonana przez Bibliotekę Narodową z egzemplarza pochodzącego ze zbiorów BN.</dc:description-->
+<dc:identifier.url xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">http://wolnelektury.pl/katalog/lektura/sonety-odeskie-do-m</dc:identifier.url>
+<dc:source.URL xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">http://www.polona.pl/Content/2222</dc:source.URL>
+<dc:source xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">Mickiewicz, Adam (1798-1855), Poezje, tom 1 (Wiersze młodzieńcze - Ballady i romanse - Wiersze do r. 1824), Krakowska Spółdzielnia Wydawnicza, wyd. 2 zwiększone, Kraków, 1922</dc:source>
+
+<dc:rights xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">Domena publiczna - Adam Mickiewicz zm. 1855</dc:rights>
+<dc:date.pd xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">1926</dc:date.pd>
+<dc:format xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">xml</dc:format>
+<dc:type xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">text</dc:type>
+<dc:type xml:lang="en" xmlns:dc="http://purl.org/dc/elements/1.1/">text</dc:type>
+<dc:date xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">2007-09-06</dc:date>
+<dc:language xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">pol</dc:language>
+
+</rdf:Description>
+</rdf:RDF>
+
+<autor_utworu>Adam Mickiewicz</autor_utworu>
+<dzielo_nadrzedne>Sonety odeskie</dzielo_nadrzedne>
+<nazwa_utworu>Do M***</nazwa_utworu>
+
+<nota><akap>Wiérsz napisany w roku 1822</akap></nota>
+
+
+<strofa>Precz z moich oczu!... posłucham od razu,/
+Precz z mego serca!... i serce posłucha,/
+Precz z méj pamięci!... Nie! tego rozkazu/
+Moja i twoja pamięć nie posłucha.</strofa>
+
+
+
+<strofa>Jak cień tém dłuższy, gdy padnie z daleka,/
+Tém szerzéj koło żałobne roztoczy,/
+Tak moja postać, im daléj ucieka,/
+Tém grubszym kirem twą pamięć pomroczy.</strofa>
+
+
+</liryka_l>
+</utwor>
diff --git a/src/catalogue/tests/gallery.py b/src/catalogue/tests/gallery.py
new file mode 100644 (file)
index 0000000..4b8ea3f
--- /dev/null
@@ -0,0 +1,148 @@
+# -*- coding: utf-8 -*-
+#
+# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
+"""Tests for galleries of scans."""
+
+from os.path import join, basename, exists
+from os import makedirs, listdir
+from django.test import TestCase
+from django.contrib.auth.models import User
+from catalogue.models import Book
+from tempfile import mkdtemp
+from django.conf import settings
+
+
+class GalleryAppendTests(TestCase):
+    def setUp(self):
+        self.user = User.objects.create(username='tester')
+        self.book1 = Book.create(self.user, 'book 1', slug='book1')
+        self.book1.chunk_set.create(number=2, title='Second chunk',
+                slug='book1-2')
+        c=self.book1[1]
+        c.gallery_start=3
+        
+        self.scandir = join(settings.MEDIA_ROOT, settings.IMAGE_DIR)
+        if not exists(self.scandir):
+            makedirs(self.scandir)
+
+    def make_gallery(self, book, files):
+        d = mkdtemp('gallery', dir=self.scandir)
+        for named, cont in files.items():
+            f = open(join(d, named), 'w')
+            f.write(cont)
+            f.close()
+        book.gallery = basename(d)
+
+
+    def test_both_indexed(self):
+        self.book2 = Book.create(self.user, 'book 2', slug='book2')
+        self.book2.chunk_set.create(number=2, title='Second chunk of second book',
+                slug='book2-2')
+
+        c = self.book2[1]
+        c.gallery_start = 3
+        c.save()
+        
+        print "gallery starts:",self.book2[0].gallery_start, self.book2[1].gallery_start
+
+        self.make_gallery(self.book1, {
+            '1-0001_1l' : 'aa',
+            '1-0001_2r' : 'bb',
+            '1-0002_1l' : 'cc',
+            '1-0002_2r' : 'dd',
+            })
+
+        self.make_gallery(self.book2, {
+            '1-0001_1l' : 'dd', # the same, should not be moved
+            '1-0001_2r' : 'ff',
+            '2-0002_1l' : 'gg',
+            '2-0002_2r' : 'hh',
+            })
+
+        self.book1.append(self.book2)
+
+        files = listdir(join(self.scandir, self.book1.gallery))
+        files.sort()
+        print files
+        self.assertEqual(files, [
+            '1-0001_1l',
+            '1-0001_2r',
+            '1-0002_1l',
+            '1-0002_2r',
+            #            '2-0001_1l',
+            '2-0001_2r',
+            '3-0002_1l',
+            '3-0002_2r',
+            ])        
+
+        self.assertEqual((4, 6), (self.book1[2].gallery_start, self.book1[3].gallery_start))
+        
+        
+    def test_none_indexed(self):
+        self.book2 = Book.create(self.user, 'book 2', slug='book2')
+        self.make_gallery(self.book1, {
+            '0001_1l' : 'aa',
+            '0001_2r' : 'bb',
+            '0002_1l' : 'cc',
+            '0002_2r' : 'dd',
+            })
+
+        self.make_gallery(self.book2, {
+            '0001_1l' : 'ee',
+            '0001_2r' : 'ff',
+            '0002_1l' : 'gg',
+            '0002_2r' : 'hh',
+            })
+
+        self.book1.append(self.book2)
+
+        files = listdir(join(self.scandir, self.book1.gallery))
+        files.sort()
+        print files
+        self.assertEqual(files, [
+            '0-0001_1l',
+            '0-0001_2r',
+            '0-0002_1l',
+            '0-0002_2r',
+            '1-0001_1l',
+            '1-0001_2r',
+            '1-0002_1l',
+            '1-0002_2r',
+            ])        
+
+
+    def test_none_indexed(self):
+        import nose.tools
+        self.book2 = Book.create(self.user, 'book 2', slug='book2')
+        self.make_gallery(self.book1, {
+            '1-0001_1l' : 'aa',
+            '1-0001_2r' : 'bb',
+            '1002_1l' : 'cc',
+            '1002_2r' : 'dd',
+            })
+
+        self.make_gallery(self.book2, {
+            '0001_1l' : 'ee',
+            '0001_2r' : 'ff',
+            '0002_1l' : 'gg',
+            '0002_2r' : 'hh',
+            })
+
+        self.book1.append(self.book2)
+
+        files = listdir(join(self.scandir, self.book1.gallery))
+        files.sort()
+        print files
+        self.assertEqual(files, [
+            '0-1-0001_1l',
+            '0-1-0001_2r',
+            '0-1002_1l',
+            '0-1002_2r',
+            '1-0001_1l',
+            '1-0001_2r',
+            '1-0002_1l',
+            '1-0002_2r',
+            ])        
+
diff --git a/src/catalogue/tests/publish.py b/src/catalogue/tests/publish.py
new file mode 100644 (file)
index 0000000..93e02da
--- /dev/null
@@ -0,0 +1,40 @@
+# -*- coding: utf-8 -*-
+#
+# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
+"""Tests for the publishing process."""
+
+from catalogue.test_utils import get_fixture
+
+from mock import patch
+from django.test import TestCase
+from django.contrib.auth.models import User
+from catalogue.models import Book
+
+
+class PublishTests(TestCase):
+    def setUp(self):
+        self.user = User.objects.create(username='tester')
+        self.text1 = get_fixture('chunk1.xml')
+        self.book = Book.create(self.user, self.text1, slug='test-book')
+
+    @patch('apiclient.api_call')
+    def test_unpublishable(self, api_call):
+        with self.assertRaises(AssertionError):
+            self.book.publish(self.user)
+
+    @patch('apiclient.api_call')
+    def test_publish(self, api_call):
+        self.book[0].head.set_publishable(True)
+        self.book.publish(self.user)
+        api_call.assert_called_with(self.user, 'books/', {"book_xml": self.text1, "days": 0}, beta=False)
+
+    @patch('apiclient.api_call')
+    def test_publish_multiple(self, api_call):
+        self.book[0].head.set_publishable(True)
+        self.book[0].split(slug='part-2')
+        self.book[1].commit(get_fixture('chunk2.xml'))
+        self.book[1].head.set_publishable(True)
+        self.book.publish(self.user)
+        api_call.assert_called_with(self.user, 'books/', {"book_xml": get_fixture('expected.xml'), "days": 0}, beta=False)
diff --git a/src/catalogue/tests/xml_updater.py b/src/catalogue/tests/xml_updater.py
new file mode 100644 (file)
index 0000000..9fb5a4a
--- /dev/null
@@ -0,0 +1,35 @@
+# -*- coding: utf-8 -*-
+#
+# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
+"""XmlUpdater tests."""
+
+from catalogue.test_utils import get_fixture
+from django.test import TestCase
+from django.contrib.auth.models import User
+from catalogue.models import Book
+from catalogue.management import XmlUpdater
+from librarian import DCNS
+
+
+class XmlUpdaterTests(TestCase):
+    class SimpleUpdater(XmlUpdater):
+        @XmlUpdater.fixes_elements('.//' + DCNS('title'))
+        def fix_title(element, **kwargs):
+            element.text = element.text + " fixed"
+            return True
+
+    def setUp(self):
+        self.user = User.objects.create(username='tester')
+        text = get_fixture('chunk1.xml')
+        Book.create(self.user, text, slug='test-book')
+        self.title = "Do M***"
+
+    def test_xml_updater(self):
+        self.SimpleUpdater().run(self.user)
+        self.assertEqual(
+            Book.objects.get(slug='test-book').wldocument(
+                publishable=False).book_info.title,
+            self.title + " fixed"
+            )
diff --git a/src/catalogue/urls.py b/src/catalogue/urls.py
new file mode 100644 (file)
index 0000000..e81c0a3
--- /dev/null
@@ -0,0 +1,64 @@
+# -*- coding: utf-8
+from django.conf.urls import patterns, url
+from django.contrib.auth.decorators import permission_required
+from django.views.generic import RedirectView
+from catalogue.feeds import PublishTrackFeed
+from catalogue.views import GalleryView
+
+
+urlpatterns = patterns('catalogue.views',
+    url(r'^$', RedirectView.as_view(url='catalogue/')),
+
+    url(r'^images/$', 'image_list', name='catalogue_image_list'),
+    url(r'^image/(?P<slug>[^/]+)/$', 'image', name="catalogue_image"),
+    url(r'^image/(?P<slug>[^/]+)/publish$', 'publish_image',
+            name="catalogue_publish_image"),
+
+    url(r'^catalogue/$', 'document_list', name='catalogue_document_list'),
+    url(r'^user/$', 'my', name='catalogue_user'),
+    url(r'^user/(?P<username>[^/]+)/$', 'user', name='catalogue_user'),
+    url(r'^users/$', 'users', name='catalogue_users'),
+    url(r'^activity/$', 'activity', name='catalogue_activity'),
+    url(r'^activity/(?P<isodate>\d{4}-\d{2}-\d{2})/$', 
+        'activity', name='catalogue_activity'),
+
+    url(r'^upload/$',
+        'upload', name='catalogue_upload'),
+
+    url(r'^create/(?P<slug>[^/]*)/',
+        'create_missing', name='catalogue_create_missing'),
+    url(r'^create/',
+        'create_missing', name='catalogue_create_missing'),
+
+    url(r'^book/(?P<slug>[^/]+)/publish$', 'publish', name="catalogue_publish"),
+
+    url(r'^book/(?P<slug>[^/]+)/$', 'book', name="catalogue_book"),
+    url(r'^book/(?P<slug>[^/]+)/gallery/$',
+            permission_required('catalogue.change_book')(GalleryView.as_view()),
+            name="catalogue_book_gallery"),
+    url(r'^book/(?P<slug>[^/]+)/xml$', 'book_xml', name="catalogue_book_xml"),
+    url(r'^book/dc/(?P<slug>[^/]+)/xml$', 'book_xml_dc', name="catalogue_book_xml_dc"),
+    url(r'^book/(?P<slug>[^/]+)/txt$', 'book_txt', name="catalogue_book_txt"),
+    url(r'^book/(?P<slug>[^/]+)/html$', 'book_html', name="catalogue_book_html"),
+    url(r'^book/(?P<slug>[^/]+)/epub$', 'book_epub', name="catalogue_book_epub"),
+    url(r'^book/(?P<slug>[^/]+)/mobi$', 'book_mobi', name="catalogue_book_mobi"),
+    url(r'^book/(?P<slug>[^/]+)/pdf$', 'book_pdf', name="catalogue_book_pdf"),
+    url(r'^book/(?P<slug>[^/]+)/pdf-mobile$', 'book_pdf', kwargs={'mobile': True}, name="catalogue_book_pdf_mobile"),
+
+    url(r'^chunk_add/(?P<slug>[^/]+)/(?P<chunk>[^/]+)/$',
+        'chunk_add', name="catalogue_chunk_add"),
+    url(r'^chunk_edit/(?P<slug>[^/]+)/(?P<chunk>[^/]+)/$',
+        'chunk_edit', name="catalogue_chunk_edit"),
+    url(r'^book_append/(?P<slug>[^/]+)/$',
+        'book_append', name="catalogue_book_append"),
+    url(r'^chunk_mass_edit',
+        'chunk_mass_edit', name='catalogue_chunk_mass_edit'),
+    url(r'^image_mass_edit',
+        'image_mass_edit', name='catalogue_image_mass_edit'),
+
+    url(r'^track/(?P<slug>[^/]*)/$', PublishTrackFeed()),
+    url(r'^active/$', 'active_users_list', name='active_users_list'),
+
+    url(r'^mark-final/$', 'mark_final', name='mark_final'),
+    url(r'^mark-final-completed/$', 'mark_final_completed', name='mark_final_completed'),
+)
diff --git a/src/catalogue/views.py b/src/catalogue/views.py
new file mode 100644 (file)
index 0000000..f620805
--- /dev/null
@@ -0,0 +1,672 @@
+# -*- coding: utf-8 -*-
+from collections import defaultdict
+from datetime import datetime, date, timedelta
+import logging
+import os
+from StringIO import StringIO
+from urllib import unquote
+from urlparse import urlsplit, urlunsplit
+
+from django.conf import settings
+from django.contrib import auth
+from django.contrib.auth.models import User
+from django.contrib.auth.decorators import login_required, permission_required
+from django.core.urlresolvers import reverse
+from django.db.models import Count, Q
+from django.db import transaction
+from django import http
+from django.http import Http404, HttpResponse, HttpResponseForbidden
+from django.http.response import HttpResponseRedirect
+from django.shortcuts import get_object_or_404, render
+from django.utils.encoding import iri_to_uri
+from django.utils.http import urlquote_plus
+from django.utils.translation import ugettext_lazy as _
+from django.views.decorators.http import require_POST
+from django_cas.decorators import user_passes_test
+
+from apiclient import NotAuthorizedError
+from catalogue import forms
+from catalogue import helpers
+from catalogue.helpers import active_tab
+from catalogue.models import (Book, Chunk, Image, BookPublishRecord, 
+        ChunkPublishRecord, ImagePublishRecord, Project)
+from fileupload.views import UploadView
+
+#
+# Quick hack around caching problems, TODO: use ETags
+#
+from django.views.decorators.cache import never_cache
+
+logger = logging.getLogger("fnp.catalogue")
+
+
+@active_tab('all')
+@never_cache
+def document_list(request):
+    return render(request, 'catalogue/document_list.html')
+
+
+@active_tab('images')
+@never_cache
+def image_list(request, user=None):
+    return render(request, 'catalogue/image_list.html')
+
+
+@never_cache
+def user(request, username):
+    user = get_object_or_404(User, username=username)
+    return render(request, 'catalogue/user_page.html', {"viewed_user": user})
+
+
+@login_required
+@active_tab('my')
+@never_cache
+def my(request):
+    last_books = sorted(request.session.get("wiki_last_books", {}).items(),
+        key=lambda x: x[1]['time'], reverse=True)
+    for k, v in last_books:
+        v['time'] = datetime.fromtimestamp(v['time'])
+    return render(request, 'catalogue/my_page.html', {
+        'last_books': last_books,
+        "logout_to": '/',
+        })
+
+
+@active_tab('users')
+def users(request):
+    return render(request, 'catalogue/user_list.html', {
+        'users': User.objects.all().annotate(count=Count('chunk')).order_by(
+            '-count', 'last_name', 'first_name'),
+    })
+
+
+@active_tab('activity')
+def activity(request, isodate=None):
+    today = date.today()
+    try:
+        day = helpers.parse_isodate(isodate)
+    except ValueError:
+        day = today
+
+    if day > today:
+        raise Http404
+    if day != today:
+        next_day = day + timedelta(1)
+    prev_day = day - timedelta(1)
+
+    return render(request, 'catalogue/activity.html', locals())
+
+
+@never_cache
+def logout_then_redirect(request):
+    auth.logout(request)
+    return http.HttpResponseRedirect(urlquote_plus(request.GET.get('next', '/'), safe='/?='))
+
+
+@permission_required('catalogue.add_book')
+@active_tab('create')
+def create_missing(request, slug=None):
+    if slug is None:
+        slug = ''
+    slug = slug.replace(' ', '-')
+
+    if request.method == "POST":
+        form = forms.DocumentCreateForm(request.POST, request.FILES)
+        if form.is_valid():
+            
+            if request.user.is_authenticated():
+                creator = request.user
+            else:
+                creator = None
+            book = Book.create(
+                text=form.cleaned_data['text'],
+                creator=creator,
+                slug=form.cleaned_data['slug'],
+                title=form.cleaned_data['title'],
+                gallery=form.cleaned_data['gallery'],
+            )
+
+            return http.HttpResponseRedirect(reverse("catalogue_book", args=[book.slug]))
+    else:
+        form = forms.DocumentCreateForm(initial={
+                "slug": slug,
+                "title": slug.replace('-', ' ').title(),
+                "gallery": slug,
+        })
+
+    return render(request, "catalogue/document_create_missing.html", {
+        "slug": slug,
+        "form": form,
+
+        "logout_to": '/',
+    })
+
+
+@permission_required('catalogue.add_book')
+@active_tab('upload')
+def upload(request):
+    if request.method == "POST":
+        form = forms.DocumentsUploadForm(request.POST, request.FILES)
+        if form.is_valid():
+            from slugify import slugify
+
+            if request.user.is_authenticated():
+                creator = request.user
+            else:
+                creator = None
+
+            zip = form.cleaned_data['zip']
+            skipped_list = []
+            ok_list = []
+            error_list = []
+            slugs = {}
+            existing = [book.slug for book in Book.objects.all()]
+            for filename in zip.namelist():
+                if filename[-1] == '/':
+                    continue
+                title = os.path.basename(filename)[:-4]
+                slug = slugify(title)
+                if not (slug and filename.endswith('.xml')):
+                    skipped_list.append(filename)
+                elif slug in slugs:
+                    error_list.append((filename, slug, _('Slug already used for %s' % slugs[slug])))
+                elif slug in existing:
+                    error_list.append((filename, slug, _('Slug already used in repository.')))
+                else:
+                    try:
+                        zip.read(filename).decode('utf-8') # test read
+                        ok_list.append((filename, slug, title))
+                    except UnicodeDecodeError:
+                        error_list.append((filename, title, _('File should be UTF-8 encoded.')))
+                    slugs[slug] = filename
+
+            if not error_list:
+                for filename, slug, title in ok_list:
+                    book = Book.create(
+                        text=zip.read(filename).decode('utf-8'),
+                        creator=creator,
+                        slug=slug,
+                        title=title,
+                    )
+
+            return render(request, "catalogue/document_upload.html", {
+                "form": form,
+                "ok_list": ok_list,
+                "skipped_list": skipped_list,
+                "error_list": error_list,
+
+                "logout_to": '/',
+            })
+    else:
+        form = forms.DocumentsUploadForm()
+
+    return render(request, "catalogue/document_upload.html", {
+        "form": form,
+
+        "logout_to": '/',
+    })
+
+
+def serve_xml(request, book, slug):
+    if not book.accessible(request):
+        return HttpResponseForbidden("Not authorized.")
+    xml = book.materialize(publishable=True)
+    response = http.HttpResponse(xml, content_type='application/xml')
+    response['Content-Disposition'] = 'attachment; filename=%s.xml' % slug
+    return response
+
+
+@never_cache
+def book_xml(request, slug):
+    book = get_object_or_404(Book, slug=slug)
+    return serve_xml(request, book, slug)
+
+
+@never_cache
+def book_xml_dc(request, slug):
+    book = get_object_or_404(Book, dc_slug=slug)
+    return serve_xml(request, book, slug)
+
+
+@never_cache
+def book_txt(request, slug):
+    book = get_object_or_404(Book, slug=slug)
+    if not book.accessible(request):
+        return HttpResponseForbidden("Not authorized.")
+
+    doc = book.wldocument()
+    text = doc.as_text().get_bytes()
+    response = http.HttpResponse(text, content_type='text/plain')
+    response['Content-Disposition'] = 'attachment; filename=%s.txt' % slug
+    return response
+
+
+@never_cache
+def book_html(request, slug):
+    book = get_object_or_404(Book, slug=slug)
+    if not book.accessible(request):
+        return HttpResponseForbidden("Not authorized.")
+
+    doc = book.wldocument(parse_dublincore=False)
+    html = doc.as_html(options={'gallery': "'%s'" % book.gallery_url()})
+
+    html = html.get_bytes() if html is not None else ''
+    # response = http.HttpResponse(html, content_type='text/html')
+    # return response
+    # book_themes = {}
+    # for fragment in book.fragments.all().iterator():
+    #     for theme in fragment.tags.filter(category='theme').iterator():
+    #         book_themes.setdefault(theme, []).append(fragment)
+
+    # book_themes = book_themes.items()
+    # book_themes.sort(key=lambda s: s[0].sort_key)
+    return render(request, 'catalogue/book_text.html', locals())
+
+
+@never_cache
+def book_pdf(request, slug, mobile=False):
+    book = get_object_or_404(Book, slug=slug)
+    if not book.accessible(request):
+        return HttpResponseForbidden("Not authorized.")
+
+    # TODO: move to celery
+    doc = book.wldocument()
+    # TODO: error handling
+    customizations = ['26pt', 'nothemes', 'nomargins', 'notoc'] if mobile else None
+    pdf_file = doc.as_pdf(cover=True, ilustr_path=book.gallery_path(), customizations=customizations)
+    from catalogue.ebook_utils import serve_file
+    return serve_file(pdf_file.get_filename(),
+                book.slug + '.pdf', 'application/pdf')
+
+
+@never_cache
+def book_epub(request, slug):
+    book = get_object_or_404(Book, slug=slug)
+    if not book.accessible(request):
+        return HttpResponseForbidden("Not authorized.")
+
+    # TODO: move to celery
+    doc = book.wldocument()
+    # TODO: error handling
+    epub = doc.as_epub(ilustr_path=book.gallery_path()).get_bytes()
+    response = HttpResponse(content_type='application/epub+zip')
+    response['Content-Disposition'] = 'attachment; filename=%s' % book.slug + '.epub'
+    response.write(epub)
+    return response
+
+
+@never_cache
+def book_mobi(request, slug):
+    book = get_object_or_404(Book, slug=slug)
+    if not book.accessible(request):
+        return HttpResponseForbidden("Not authorized.")
+
+    # TODO: move to celery
+    doc = book.wldocument()
+    # TODO: error handling
+    mobi = doc.as_mobi(ilustr_path=book.gallery_path()).get_bytes()
+    response = HttpResponse(content_type='application/x-mobipocket-ebook')
+    response['Content-Disposition'] = 'attachment; filename=%s' % book.slug + '.mobi'
+    response.write(mobi)
+    return response
+
+
+@never_cache
+def revision(request, slug, chunk=None):
+    try:
+        doc = Chunk.get(slug, chunk)
+    except (Chunk.MultipleObjectsReturned, Chunk.DoesNotExist):
+        raise Http404
+    if not doc.book.accessible(request):
+        return HttpResponseForbidden("Not authorized.")
+    return http.HttpResponse(str(doc.revision()))
+
+
+def book(request, slug):
+    book = get_object_or_404(Book, slug=slug)
+    if not book.accessible(request):
+        return HttpResponseForbidden("Not authorized.")
+
+    if request.user.has_perm('catalogue.change_book'):
+        if request.method == "POST":
+            form = forms.BookForm(request.POST, instance=book)
+            if form.is_valid():
+                form.save()
+                return http.HttpResponseRedirect(book.get_absolute_url())
+        else:
+            form = forms.BookForm(instance=book)
+        publish_options_form = forms.PublishOptionsForm()
+        editable = True
+    else:
+        form = forms.ReadonlyBookForm(instance=book)
+        publish_options_form = forms.PublishOptionsForm()
+        editable = False
+
+    publish_error = book.publishable_error()
+    publishable = publish_error is None
+
+    return render(request, "catalogue/book_detail.html", {
+        "book": book,
+        "publishable": publishable,
+        "publishable_error": publish_error,
+        "form": form,
+        "publish_options_form": publish_options_form,
+        "editable": editable,
+    })
+
+
+def image(request, slug):
+    image = get_object_or_404(Image, slug=slug)
+    if not image.accessible(request):
+        return HttpResponseForbidden("Not authorized.")
+
+    if request.user.has_perm('catalogue.change_image'):
+        if request.method == "POST":
+            form = forms.ImageForm(request.POST, instance=image)
+            if form.is_valid():
+                form.save()
+                return http.HttpResponseRedirect(image.get_absolute_url())
+        else:
+            form = forms.ImageForm(instance=image)
+        editable = True
+    else:
+        form = forms.ReadonlyImageForm(instance=image)
+        editable = False
+
+    publish_error = image.publishable_error()
+    publishable = publish_error is None
+
+    return render(request, "catalogue/image_detail.html", {
+        "object": image,
+        "publishable": publishable,
+        "publishable_error": publish_error,
+        "form": form,
+        "editable": editable,
+    })
+
+
+@permission_required('catalogue.add_chunk')
+def chunk_add(request, slug, chunk):
+    try:
+        doc = Chunk.get(slug, chunk)
+    except (Chunk.MultipleObjectsReturned, Chunk.DoesNotExist):
+        raise Http404
+    if not doc.book.accessible(request):
+        return HttpResponseForbidden("Not authorized.")
+
+    if request.method == "POST":
+        form = forms.ChunkAddForm(request.POST, instance=doc)
+        if form.is_valid():
+            if request.user.is_authenticated():
+                creator = request.user
+            else:
+                creator = None
+            doc.split(creator=creator,
+                slug=form.cleaned_data['slug'],
+                title=form.cleaned_data['title'],
+                gallery_start=form.cleaned_data['gallery_start'],
+                user=form.cleaned_data['user'],
+                stage=form.cleaned_data['stage']
+            )
+
+            return http.HttpResponseRedirect(doc.book.get_absolute_url())
+    else:
+        form = forms.ChunkAddForm(initial={
+                "slug": str(doc.number + 1),
+                "title": "cz. %d" % (doc.number + 1, ),
+        })
+
+    return render(request, "catalogue/chunk_add.html", {
+        "chunk": doc,
+        "form": form,
+    })
+
+
+@login_required
+def chunk_edit(request, slug, chunk):
+    try:
+        doc = Chunk.get(slug, chunk)
+    except (Chunk.MultipleObjectsReturned, Chunk.DoesNotExist):
+        raise Http404
+    if not doc.book.accessible(request):
+        return HttpResponseForbidden("Not authorized.")
+
+    if request.method == "POST":
+        form = forms.ChunkForm(request.POST, instance=doc)
+        if form.is_valid():
+            form.save()
+            go_next = request.GET.get('next', None)
+            if go_next:
+                go_next = urlquote_plus(unquote(iri_to_uri(go_next)), safe='/?=&')
+            else:
+                go_next = doc.book.get_absolute_url()
+            return http.HttpResponseRedirect(go_next)
+    else:
+        form = forms.ChunkForm(instance=doc)
+
+    referer = request.META.get('HTTP_REFERER')
+    if referer:
+        parts = urlsplit(referer)
+        parts = ['', ''] + list(parts[2:])
+        go_next = urlquote_plus(urlunsplit(parts))
+    else:
+        go_next = ''
+
+    return render(request, "catalogue/chunk_edit.html", {
+        "chunk": doc,
+        "form": form,
+        "go_next": go_next,
+    })
+
+
+@transaction.atomic
+@login_required
+@require_POST
+def chunk_mass_edit(request):
+    ids = map(int, filter(lambda i: i.strip()!='', request.POST.get('ids').split(',')))
+    chunks = map(lambda i: Chunk.objects.get(id=i), ids)
+    
+    stage = request.POST.get('stage')
+    if stage:
+        try:
+            stage = Chunk.tag_model.objects.get(slug=stage)
+        except Chunk.DoesNotExist, e:
+            stage = None
+       
+        for c in chunks: c.stage = stage
+
+    username = request.POST.get('user')
+    logger.info("username: %s" % username)
+    logger.info(request.POST)
+    if username:
+        try:
+            user = User.objects.get(username=username)
+        except User.DoesNotExist, e:
+            user = None
+            
+        for c in chunks: c.user = user
+
+    project_id = request.POST.get('project')
+    if project_id:
+        try:
+            project = Project.objects.get(pk=int(project_id))
+        except (Project.DoesNotExist, ValueError), e:
+            project = None
+        for c in chunks:
+            book = c.book
+            book.project = project
+            book.save()
+
+    for c in chunks: c.save()
+
+    return HttpResponse("", content_type="text/plain")
+
+
+@transaction.atomic
+@login_required
+@require_POST
+def image_mass_edit(request):
+    ids = map(int, filter(lambda i: i.strip()!='', request.POST.get('ids').split(',')))
+    images = map(lambda i: Image.objects.get(id=i), ids)
+    
+    stage = request.POST.get('stage')
+    if stage:
+        try:
+            stage = Image.tag_model.objects.get(slug=stage)
+        except Image.DoesNotExist, e:
+            stage = None
+       
+        for c in images: c.stage = stage
+
+    username = request.POST.get('user')
+    logger.info("username: %s" % username)
+    logger.info(request.POST)
+    if username:
+        try:
+            user = User.objects.get(username=username)
+        except User.DoesNotExist, e:
+            user = None
+            
+        for c in images: c.user = user
+
+    project_id = request.POST.get('project')
+    if project_id:
+        try:
+            project = Project.objects.get(pk=int(project_id))
+        except (Project.DoesNotExist, ValueError), e:
+            project = None
+        for c in images:
+            c.project = project
+
+    for c in images: c.save()
+
+    return HttpResponse("", content_type="text/plain")
+
+
+@permission_required('catalogue.change_book')
+def book_append(request, slug):
+    book = get_object_or_404(Book, slug=slug)
+    if not book.accessible(request):
+        return HttpResponseForbidden("Not authorized.")
+
+    if request.method == "POST":
+        form = forms.BookAppendForm(book, request.POST)
+        if form.is_valid():
+            append_to = form.cleaned_data['append_to']
+            append_to.append(book)
+            return http.HttpResponseRedirect(append_to.get_absolute_url())
+    else:
+        form = forms.BookAppendForm(book)
+    return render(request, "catalogue/book_append_to.html", {
+        "book": book,
+        "form": form,
+
+        "logout_to": '/',
+    })
+
+
+@require_POST
+@login_required
+def publish(request, slug):
+    form = forms.PublishOptionsForm(request.POST)
+    if form.is_valid():
+        days = form.cleaned_data['days']
+        beta = form.cleaned_data['beta']
+    else:
+        days = 0
+        beta = False
+    book = get_object_or_404(Book, slug=slug)
+    if not book.accessible(request):
+        return HttpResponseForbidden("Not authorized.")
+
+    try:
+        protocol = 'https://' if request.is_secure() else 'http://'
+        book.publish(request.user, host=protocol + request.get_host(), days=days, beta=beta)
+    except NotAuthorizedError:
+        return http.HttpResponseRedirect(reverse('apiclient_oauth' if not beta else 'apiclient_beta_oauth'))
+    except BaseException, e:
+        return http.HttpResponse(repr(e))
+    else:
+        return http.HttpResponseRedirect(book.get_absolute_url())
+
+
+@require_POST
+@login_required
+def publish_image(request, slug):
+    image = get_object_or_404(Image, slug=slug)
+    if not image.accessible(request):
+        return HttpResponseForbidden("Not authorized.")
+
+    try:
+        image.publish(request.user)
+    except NotAuthorizedError:
+        return http.HttpResponseRedirect(reverse('apiclient_oauth'))
+    except BaseException, e:
+        return http.HttpResponse(e)
+    else:
+        return http.HttpResponseRedirect(image.get_absolute_url())
+
+
+class GalleryView(UploadView):
+    def get_object(self, request, slug):
+        book = get_object_or_404(Book, slug=slug)
+        if not book.gallery:
+            raise Http404
+        return book
+
+    def breadcrumbs(self):
+        return [
+            (_('books'), reverse('catalogue_document_list')),
+            (self.object.title, self.object.get_absolute_url()),
+            (_('scan gallery'),),
+        ]
+
+    def get_directory(self):
+        return "%s%s/" % (settings.IMAGE_DIR, self.object.gallery)
+
+
+def active_users_list(request):
+    since = date(date.today().year, 1, 1)
+    by_user = defaultdict(lambda: 0)
+    by_email = defaultdict(lambda: 0)
+    names_by_email = defaultdict(set)
+    for change_model in (Chunk.change_model, Image.change_model):
+        for c in change_model.objects.filter(
+                created_at__gte=since).order_by(
+                'author', 'author_email', 'author_name').values(
+                'author', 'author_name', 'author_email').annotate(
+                c=Count('author'), ce=Count('author_email')).distinct():
+            if c['author']:
+                by_user[c['author']] += c['c']
+            else:
+                by_email[c['author_email']] += c['ce']
+                if c['author_name'].strip():
+                    names_by_email[c['author_email']].add(c['author_name'])
+    for user in User.objects.filter(pk__in=by_user):
+        by_email[user.email] += by_user[user.pk]
+        names_by_email[user.email].add("%s %s" % (user.first_name, user.last_name))
+
+    active_users = []
+    for email, count in by_email.items():
+        active_users.append((email, names_by_email[email], count))
+    active_users.sort(key=lambda x: -x[2])
+    return render(request, 'catalogue/active_users_list.html', {
+        'users': active_users,
+        'since': since,
+    })
+
+
+@user_passes_test(lambda u: u.is_superuser)
+def mark_final(request):
+    if request.method == 'POST':
+        form = forms.MarkFinalForm(data=request.POST)
+        if form.is_valid():
+            form.save()
+            return HttpResponseRedirect(reverse('mark_final_completed'))
+    else:
+        form = forms.MarkFinalForm()
+    return render(request, 'catalogue/mark_final.html', {'form': form})
+
+
+def mark_final_completed(request):
+    return render(request, 'catalogue/mark_final_completed.html')
diff --git a/src/catalogue/xml_tools.py b/src/catalogue/xml_tools.py
new file mode 100644 (file)
index 0000000..242714b
--- /dev/null
@@ -0,0 +1,141 @@
+# -*- coding: utf-8 -*-
+from copy import deepcopy
+import re
+
+from lxml import etree
+from catalogue.constants import TRIM_BEGIN, TRIM_END, MASTERS
+
+RE_TRIM_BEGIN = re.compile("^<!--%s-->$" % TRIM_BEGIN, re.M)
+RE_TRIM_END = re.compile("^<!--%s-->$" % TRIM_END, re.M)
+
+
+class ParseError(BaseException):
+    pass
+
+
+def _trim(text, trim_begin=True, trim_end=True):
+    """ 
+        Cut off everything before RE_TRIM_BEGIN and after RE_TRIM_END, so
+        that eg. one big XML file can be compiled from many small XML files.
+    """
+    if trim_begin:
+        text = RE_TRIM_BEGIN.split(text, maxsplit=1)[-1]
+    if trim_end:
+        text = RE_TRIM_END.split(text, maxsplit=1)[0]
+    return text
+
+
+def compile_text(parts):
+    """ 
+        Compiles full text from an iterable of parts,
+        trimming where applicable.
+    """
+    texts = []
+    trim_begin = False
+    text = ''
+    for next_text in parts:
+        if not next_text:
+            continue
+        if text:
+            # trim the end, because there's more non-empty text
+            # don't trim beginning, if `text' is the first non-empty part
+            texts.append(_trim(text, trim_begin=trim_begin))
+            trim_begin = True
+        text = next_text
+    # don't trim the end, because there's no more text coming after `text'
+    # only trim beginning if it's not still the first non-empty
+    texts.append(_trim(text, trim_begin=trim_begin, trim_end=False))
+    return "".join(texts)
+
+
+def add_trim_begin(text):
+    trim_tag = etree.Comment(TRIM_BEGIN)
+    e = etree.fromstring(text)
+    for master in e[::-1]:
+        if master.tag in MASTERS:
+            break
+    if master.tag not in MASTERS:
+        raise ParseError('No master tag found!')
+
+    master.insert(0, trim_tag)
+    trim_tag.tail = '\n\n\n' + (master.text or '')
+    master.text = '\n'
+    return unicode(etree.tostring(e, encoding="utf-8"), 'utf-8')
+
+
+def add_trim_end(text):
+    trim_tag = etree.Comment(TRIM_END)
+    e = etree.fromstring(text)
+    for master in e[::-1]:
+        if master.tag in MASTERS:
+            break
+    if master.tag not in MASTERS:
+        raise ParseError('No master tag found!')
+
+    master.append(trim_tag)
+    trim_tag.tail = '\n'
+    prev = trim_tag.getprevious()
+    if prev is not None:
+        prev.tail = (prev.tail or '') + '\n\n\n'
+    else:
+        master.text = (master.text or '') + '\n\n\n'
+    return unicode(etree.tostring(e, encoding="utf-8"), 'utf-8')
+
+
+def split_xml(text):
+    """Splits text into chapters.
+
+    All this stuff really must go somewhere else.
+
+    """
+    src = etree.fromstring(text)
+    chunks = []
+
+    splitter = u'naglowek_rozdzial'
+    parts = src.findall('.//naglowek_rozdzial')
+    while parts:
+        # copy the document
+        copied = deepcopy(src)
+
+        element = parts[-1]
+
+        # find the chapter's title
+        name_elem = deepcopy(element)
+        for tag in 'extra', 'motyw', 'pa', 'pe', 'pr', 'pt', 'uwaga':
+            for a in name_elem.findall('.//' + tag):
+                a.text=''
+                del a[:]
+        name = etree.tostring(name_elem, method='text', encoding='utf-8').strip()
+
+        # in the original, remove everything from the start of the last chapter
+        parent = element.getparent()
+        del parent[parent.index(element):]
+        element, parent = parent, parent.getparent()
+        while parent is not None:
+            del parent[parent.index(element) + 1:]
+            element, parent = parent, parent.getparent()
+
+        # in the copy, remove everything before the last chapter
+        element = copied.findall('.//naglowek_rozdzial')[-1]
+        parent = element.getparent()
+        while parent is not None:
+            parent.text = None
+            while parent[0] is not element:
+                del parent[0]
+            element, parent = parent, parent.getparent()
+        chunks[:0] = [[name,
+            unicode(etree.tostring(copied, encoding='utf-8'), 'utf-8')
+            ]]
+
+        parts = src.findall('.//naglowek_rozdzial')
+
+    chunks[:0] = [[u'początek',
+        unicode(etree.tostring(src, encoding='utf-8'), 'utf-8')
+        ]]
+
+    for ch in chunks[1:]:
+        ch[1] = add_trim_begin(ch[1])
+    for ch in chunks[:-1]:
+        ch[1] = add_trim_end(ch[1])
+
+    return chunks
diff --git a/src/cover/__init__.py b/src/cover/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/cover/forms.py b/src/cover/forms.py
new file mode 100755 (executable)
index 0000000..513bdef
--- /dev/null
@@ -0,0 +1,106 @@
+# -*- coding: utf-8 -*-
+#
+# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
+from StringIO import StringIO
+
+from django import forms
+from django.conf import settings
+from django.utils.translation import ugettext_lazy as _, ugettext
+from cover.models import Image
+from django.utils.text import mark_safe
+from PIL import Image as PILImage
+
+from cover.utils import get_flickr_data, FlickrError, URLOpener
+
+
+class ImageAddForm(forms.ModelForm):
+    class Meta:
+        model = Image
+
+    def __init__(self, *args, **kwargs):
+        super(ImageAddForm, self).__init__(*args, **kwargs)
+        self.fields['file'].required = False
+
+    def clean_download_url(self):
+        cl = self.cleaned_data['download_url'] or None
+        if cl is not None:
+            try:
+                img = Image.objects.get(download_url=cl)
+            except Image.DoesNotExist:
+                pass
+            else:
+                raise forms.ValidationError(mark_safe(
+                    ugettext('Image <a href="%(url)s">already in repository</a>.')
+                    % {'url': img.get_absolute_url()}))
+        return cl
+
+    def clean_source_url(self):
+        source_url = self.cleaned_data['source_url'] or None
+        if source_url is not None:
+            same_source = Image.objects.filter(source_url=source_url)
+            if same_source:
+                raise forms.ValidationError(mark_safe(
+                    ugettext('Image <a href="%s">already in repository</a>'
+                             % same_source.first().get_absolute_url())))
+        return source_url
+
+    def clean(self):
+        cleaned_data = super(ImageAddForm, self).clean()
+        download_url = cleaned_data.get('download_url', None)
+        uploaded_file = cleaned_data.get('file', None)
+        if not download_url and not uploaded_file:
+            raise forms.ValidationError(ugettext('No image specified'))
+        if download_url:
+            image_data = URLOpener().open(download_url).read()
+            width, height = PILImage.open(StringIO(image_data)).size
+        else:
+            width, height = PILImage.open(uploaded_file.file).size
+        min_width, min_height = settings.MIN_COVER_SIZE
+        if width < min_width or height < min_height:
+            raise forms.ValidationError(ugettext('Image too small: %sx%s, minimal dimensions %sx%s') %
+                                        (width, height, min_width, min_height))
+        return cleaned_data
+
+
+class ImageEditForm(forms.ModelForm):
+    """Form used for editing a Book."""
+    class Meta:
+        model = Image
+        exclude = ['download_url']
+
+    def clean(self):
+        cleaned_data = super(ImageEditForm, self).clean()
+        uploaded_file = cleaned_data.get('file', None)
+        width, height = PILImage.open(uploaded_file.file).size
+        min_width, min_height = settings.MIN_COVER_SIZE
+        if width < min_width or height < min_height:
+            raise forms.ValidationError(ugettext('Image too small: %sx%s, minimal dimensions %sx%s') %
+                                        (width, height, min_width, min_height))
+
+
+class ReadonlyImageEditForm(ImageEditForm):
+    """Form used for not editing an Image."""
+
+    def __init__(self, *args, **kwargs):
+        super(ReadonlyImageEditForm, self).__init__(*args, **kwargs)
+        for field in self.fields.values():
+            field.widget.attrs.update({"readonly": True})
+
+    def save(self, *args, **kwargs):
+        raise AssertionError("ReadonlyImageEditForm should not be saved.")
+
+
+class FlickrForm(forms.Form):
+    source_url = forms.URLField(label=_('Flickr URL'))
+
+    def clean_source_url(self):
+        url = self.cleaned_data['source_url']
+        try:
+            flickr_data = get_flickr_data(url)
+        except FlickrError as e:
+            raise forms.ValidationError(e)
+        for field_name in ('license_url', 'license_name', 'author', 'title', 'download_url'):
+            self.cleaned_data[field_name] = flickr_data[field_name]
+        return flickr_data['source_url']
diff --git a/src/cover/locale/pl/LC_MESSAGES/django.mo b/src/cover/locale/pl/LC_MESSAGES/django.mo
new file mode 100644 (file)
index 0000000..1c297fe
Binary files /dev/null and b/src/cover/locale/pl/LC_MESSAGES/django.mo differ
diff --git a/src/cover/locale/pl/LC_MESSAGES/django.po b/src/cover/locale/pl/LC_MESSAGES/django.po
new file mode 100644 (file)
index 0000000..67547ce
--- /dev/null
@@ -0,0 +1,100 @@
+# 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: 2014-02-25 13:29+0100\n"
+"PO-Revision-Date: 2014-02-25 13:30+0100\n"
+"Last-Translator: Radek Czajka <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"
+"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
+"|| n%100>=20) ? 1 : 2);\n"
+"X-Generator: Poedit 1.5.4\n"
+
+#: forms.py:29
+#, python-format
+msgid "Image <a href=\"%(url)s\">already in repository</a>."
+msgstr "Obraz <a href=\"%(url)s\">jest już w repozytorium</a>."
+
+#: forms.py:60
+msgid "Flickr URL"
+msgstr "URL z Flickra"
+
+#: models.py:19
+msgid "title"
+msgstr "tytuł"
+
+#: models.py:20
+msgid "author"
+msgstr "autor"
+
+#: models.py:21
+msgid "license name"
+msgstr "nazwa licencji"
+
+#: models.py:22
+msgid "license URL"
+msgstr "URL licencji"
+
+#: models.py:23
+msgid "source URL"
+msgstr "URL źródła"
+
+#: models.py:24
+msgid "image download URL"
+msgstr "URL pliku do pobrania"
+
+#: models.py:25
+msgid "file"
+msgstr "plik"
+
+#: models.py:28
+msgid "cover image"
+msgstr "obrazek na okładkę"
+
+#: models.py:29
+msgid "cover images"
+msgstr "obrazki na okładki"
+
+#: templates/cover/add_image.html:33 templates/cover/add_image.html.py:62
+msgid "Add image"
+msgstr "Dodaj obrazek"
+
+#: templates/cover/add_image.html:40
+msgid "Load from Flickr"
+msgstr "Pobierz z Flickra"
+
+#: templates/cover/image_detail.html:7
+msgid "Cover image"
+msgstr "Obrazek na okładkę"
+
+#: templates/cover/image_detail.html:23
+msgid "source"
+msgstr "źródło"
+
+#: templates/cover/image_detail.html:35
+msgid "Change"
+msgstr "Zmień"
+
+#: templates/cover/image_detail.html:41
+msgid "Used in:"
+msgstr "Użyte w:"
+
+#: templates/cover/image_detail.html:49
+msgid "None"
+msgstr "Brak"
+
+#: templates/cover/image_list.html:7
+msgid "Cover images"
+msgstr "Obrazki na okładki"
+
+#: templates/cover/image_list.html:10
+msgid "Add new"
+msgstr "Dodaj nowy"
diff --git a/src/cover/management/__init__.py b/src/cover/management/__init__.py
new file mode 100644 (file)
index 0000000..d384124
--- /dev/null
@@ -0,0 +1,4 @@
+# -*- 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.
+#
diff --git a/src/cover/management/commands/__init__.py b/src/cover/management/commands/__init__.py
new file mode 100644 (file)
index 0000000..d384124
--- /dev/null
@@ -0,0 +1,4 @@
+# -*- 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.
+#
diff --git a/src/cover/management/commands/refresh_covers.py b/src/cover/management/commands/refresh_covers.py
new file mode 100644 (file)
index 0000000..cc0ef31
--- /dev/null
@@ -0,0 +1,48 @@
+# -*- coding: utf-8 -*-
+#
+# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
+import urllib2 as urllib
+from optparse import make_option
+
+from django.core.files.base import ContentFile
+from django.core.management import BaseCommand
+
+from cover.models import Image
+from cover.utils import get_flickr_data, URLOpener, FlickrError
+
+
+class Command(BaseCommand):
+    option_list = BaseCommand.option_list + (
+        make_option('--from', dest='from_id', type=int, default=1),
+    )
+
+    def handle(self, *args, **options):
+        from_id = options.get('from_id', 1)
+        images = Image.objects.filter(id__gte=from_id).exclude(book=None).order_by('id')
+        images = images.filter(source_url__contains='flickr.com').exclude(download_url__endswith='_o.jpg')
+        for image in images:
+            print image.id
+            try:
+                flickr_data = get_flickr_data(image.source_url)
+                print flickr_data
+            except FlickrError as e:
+                print 'Flickr analysis failed: %s' % e
+            else:
+                flickr_url = flickr_data['download_url']
+                if flickr_url != image.download_url:
+                    same_url = Image.objects.filter(download_url=flickr_url)
+                    if same_url:
+                        print 'Download url already present in image %s' % same_url.get().id
+                        continue
+                try:
+                    t = URLOpener().open(flickr_url).read()
+                except urllib.URLError:
+                    print 'Broken download url'
+                except IOError:
+                    print 'Connection failed'
+                else:
+                    image.download_url = flickr_url
+                    image.file.save(image.file.name, ContentFile(t))
+                    image.save()
diff --git a/src/cover/migrations/0001_initial.py b/src/cover/migrations/0001_initial.py
new file mode 100644 (file)
index 0000000..f31c405
--- /dev/null
@@ -0,0 +1,44 @@
+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+    def forwards(self, orm):
+        # Adding model 'Image'
+        db.create_table('cover_image', (
+            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('title', self.gf('django.db.models.fields.CharField')(max_length=255)),
+            ('author', self.gf('django.db.models.fields.CharField')(max_length=255)),
+            ('license_name', self.gf('django.db.models.fields.CharField')(max_length=255)),
+            ('license_url', self.gf('django.db.models.fields.URLField')(max_length=255, blank=True)),
+            ('source_url', self.gf('django.db.models.fields.URLField')(max_length=200)),
+            ('download_url', self.gf('django.db.models.fields.URLField')(unique=True, max_length=200)),
+            ('file', self.gf('django.db.models.fields.files.ImageField')(max_length=100)),
+        ))
+        db.send_create_signal('cover', ['Image'])
+
+
+    def backwards(self, orm):
+        # Deleting model 'Image'
+        db.delete_table('cover_image')
+
+
+    models = {
+        'cover.image': {
+            'Meta': {'object_name': 'Image'},
+            'author': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'download_url': ('django.db.models.fields.URLField', [], {'unique': 'True', 'max_length': '200'}),
+            'file': ('django.db.models.fields.files.ImageField', [], {'max_length': '100'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'license_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'license_url': ('django.db.models.fields.URLField', [], {'max_length': '255', 'blank': 'True'}),
+            'source_url': ('django.db.models.fields.URLField', [], {'max_length': '200'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        }
+    }
+
+    complete_apps = ['cover']
\ No newline at end of file
diff --git a/src/cover/migrations/0002_auto__chg_field_image_download_url.py b/src/cover/migrations/0002_auto__chg_field_image_download_url.py
new file mode 100644 (file)
index 0000000..8a64c39
--- /dev/null
@@ -0,0 +1,34 @@
+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+    def forwards(self, orm):
+
+        # Changing field 'Image.download_url'
+        db.alter_column(u'cover_image', 'download_url', self.gf('django.db.models.fields.URLField')(max_length=200, unique=True, null=True))
+
+    def backwards(self, orm):
+
+        # User chose to not deal with backwards NULL issues for 'Image.download_url'
+        raise RuntimeError("Cannot reverse this migration. 'Image.download_url' and its values cannot be restored.")
+
+    models = {
+        u'cover.image': {
+            'Meta': {'object_name': 'Image'},
+            'author': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'download_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'unique': 'True', 'null': 'True'}),
+            'file': ('django.db.models.fields.files.ImageField', [], {'max_length': '100'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'license_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'license_url': ('django.db.models.fields.URLField', [], {'max_length': '255', 'blank': 'True'}),
+            'source_url': ('django.db.models.fields.URLField', [], {'max_length': '200'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        }
+    }
+
+    complete_apps = ['cover']
\ No newline at end of file
diff --git a/src/cover/migrations/0003_auto__chg_field_image_source_url.py b/src/cover/migrations/0003_auto__chg_field_image_source_url.py
new file mode 100644 (file)
index 0000000..98951e3
--- /dev/null
@@ -0,0 +1,34 @@
+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+    def forwards(self, orm):
+
+        # Changing field 'Image.source_url'
+        db.alter_column(u'cover_image', 'source_url', self.gf('django.db.models.fields.URLField')(max_length=200, null=True))
+
+    def backwards(self, orm):
+
+        # User chose to not deal with backwards NULL issues for 'Image.source_url'
+        raise RuntimeError("Cannot reverse this migration. 'Image.source_url' and its values cannot be restored.")
+
+    models = {
+        u'cover.image': {
+            'Meta': {'object_name': 'Image'},
+            'author': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'download_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'unique': 'True', 'null': 'True'}),
+            'file': ('django.db.models.fields.files.ImageField', [], {'max_length': '100'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'license_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'license_url': ('django.db.models.fields.URLField', [], {'max_length': '255', 'blank': 'True'}),
+            'source_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        }
+    }
+
+    complete_apps = ['cover']
\ No newline at end of file
diff --git a/src/cover/migrations/__init__.py b/src/cover/migrations/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/cover/models.py b/src/cover/models.py
new file mode 100644 (file)
index 0000000..d83dad3
--- /dev/null
@@ -0,0 +1,52 @@
+# -*- coding: utf-8 -*-
+#
+# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
+from django.core.files.base import ContentFile
+from django.core.files.storage import FileSystemStorage
+from django.db import models
+from django.db.models.signals import post_save
+from django.dispatch import receiver
+from django.utils.translation import ugettext_lazy as _
+from django.contrib.sites.models import Site
+from cover.utils import URLOpener
+
+
+class OverwriteStorage(FileSystemStorage):
+
+    def get_available_name(self, name, max_length=None):
+        self.delete(name)
+        return name
+
+
+class Image(models.Model):
+    title = models.CharField(max_length=255, verbose_name=_('title'))
+    author = models.CharField(max_length=255, verbose_name=_('author'))
+    license_name = models.CharField(max_length=255, verbose_name=_('license name'))
+    license_url = models.URLField(max_length=255, blank=True, verbose_name=_('license URL'))
+    source_url = models.URLField(verbose_name=_('source URL'), null=True, blank=True)
+    download_url = models.URLField(unique=True, verbose_name=_('image download URL'), null=True, blank=True)
+    file = models.ImageField(
+        upload_to='cover/image', storage=OverwriteStorage(), editable=True, verbose_name=_('file'))
+
+    class Meta:
+        verbose_name = _('cover image')
+        verbose_name_plural = _('cover images')
+
+    def __unicode__(self):
+        return u"%s - %s" % (self.author, self.title)
+
+    @models.permalink
+    def get_absolute_url(self):
+        return 'cover_image', [self.id]
+
+    def get_full_url(self):
+        return "http://%s%s" % (Site.objects.get_current().domain, self.get_absolute_url())
+
+
+@receiver(post_save, sender=Image)
+def download_image(sender, instance, **kwargs):
+    if instance.pk and not instance.file:
+        t = URLOpener().open(instance.download_url).read()
+        instance.file.save("%d.jpg" % instance.pk, ContentFile(t))
diff --git a/src/cover/templates/cover/add_image.html b/src/cover/templates/cover/add_image.html
new file mode 100755 (executable)
index 0000000..293c100
--- /dev/null
@@ -0,0 +1,69 @@
+{% extends "catalogue/base.html" %}
+{% load i18n %}
+
+{% block titleextra %}{% trans "Add image" %}{% endblock %}
+
+{% block add_js %}
+    {{block.super}}
+    <script>
+        $(function() {
+            var radio_buttons = $('input[type=radio][name=upload_type]'),
+                image_fields = $('.upload_type input[type=text],input[type=file]');
+
+            var enable_image_field = function(field) {
+                field.attr('disabled', false);
+            },
+            disable_image_fields = function() {
+                image_fields.attr('disabled', true);
+            }
+
+            radio_buttons.change(function() {
+                var radio_button = $(this),
+                    related_image_field = $('#'+radio_button.attr('data-for'));
+                disable_image_fields();
+                enable_image_field(related_image_field);
+            });
+
+            /* initial state */
+            disable_image_fields();
+            enable_image_field($('#id_download_url'));
+        });
+    </script>
+{% endblock %}
+
+{% block content %}
+<h1>{% trans "Add image" %}</h1>
+
+
+<form method="post">{% csrf_token %}
+<input type="hidden" name='form_id' value="flickr" />
+<table class='editable'><tbody>
+    {{ ff.as_table }}
+    <tr><td></td><td><button type="submit">{% trans "Load from Flickr" %}</button></td></tr>
+</tbody></table>
+</form>
+
+<form method="post" enctype="multipart/form-data">{% csrf_token %}
+{{ form.non_field_errors }}
+<table class='editable'><tbody>
+    {% for field in form %}
+        {% if field.name != 'download_url' and field.name != 'file' %}
+        <tr>
+            <th>{{field.errors}} {{field.label}}</th>
+            <td>{{field}}</td>
+        </tr>
+        {% endif %}
+    {% endfor %}
+    <tr class="upload_type">
+        <th>{{ form.download_url.errors }} <input style="width: auto;" checked data-for="id_download_url" type="radio" name="upload_type" value="url"/>{{form.download_url.label}}</th>
+        <td>{{form.download_url}}</td>
+        <th>{{ form.file.errors }} <input style="width: auto;" data-for="id_file" type="radio" name="upload_type" value="file"/> Lub {{form.file.label}}</th>
+        <td>{{form.file}}</td>
+
+    </tr>
+    <tr><td></td><td><button type="submit">{% trans "Add image" %}</button></td></tr>
+</tbody></table>
+</form>
+
+
+{% endblock %}
diff --git a/src/cover/templates/cover/image_detail.html b/src/cover/templates/cover/image_detail.html
new file mode 100755 (executable)
index 0000000..db9b176
--- /dev/null
@@ -0,0 +1,59 @@
+{% extends "catalogue/base.html" %}
+{% load i18n %}
+{% load thumbnail %}
+{% load build_absolute_uri from fnp_common %}
+
+{% block titleextra %}{% trans "Cover image" %}{% endblock %}
+
+{% block content %}
+<h1>{% trans "Cover image" %}</h1>
+
+<div style="float: right; margin-bottom:1em;">
+
+<a href="{{ object.file.url }}"><img
+        src="{% thumbnail object.file "565x833" as thumb %}
+                {{ thumb.url }}
+             {% empty %}
+                {{ object.file.url }}
+             {% endthumbnail %}" />
+    </a>
+<br/><a href="{{ object.source_url }}">{{ object.title }}</a> by {{ object.author }},
+    {% if object.license_url %}<a href="{{ object.license_url }}">{% endif %}
+        {{ object.license_name }}
+    {% if object.license_url %}</a>{% endif %}
+
+<br/>{% trans "source" %}: {{ object.download_url }}
+</div>
+
+
+{% if editable %}
+    <form method="post" enctype="multipart/form-data">
+    {% csrf_token %}
+{% endif %}
+<table class='editable'><tbody>
+    {{ form.as_table }}
+    {% if editable %}
+        <tr><td></td><td><button type="submit">{% trans "Change" %}</button></td></tr>
+    {% endif %}
+</tbody></table>
+{% if editable %}</form>{% endif %}
+
+
+<h2>{% trans "Used in:" %}</h2>
+{% if object.book_set %}
+<ul>
+    {% for book in object.book_set.all %}
+        <li><a href="{{ book.get_absolute_url }}">{{ book }}</a></li>
+    {% endfor %}
+</ul>
+{% else %}
+    <p>{% trans "None" %}</p>
+{% endif %}
+
+
+<textarea style="width:100%" rows="5">
+&lt;dc:relation.coverImage.url xmlns:dc="http://purl.org/dc/elements/1.1/">{{ object.file.url|build_absolute_uri:request }}&lt;/dc:relation.coverImage.url>
+&lt;dc:relation.coverImage.attribution xmlns:dc="http://purl.org/dc/elements/1.1/">{{ object.author }}, {{ object.license_name }}&lt;/dc:relation.coverImage.attribution>
+&lt;dc:relation.coverImage.source xmlns:dc="http://purl.org/dc/elements/1.1/">{{ object.get_full_url }}&lt;/dc:relation.coverImage.source>
+</textarea>
+{% endblock %}
diff --git a/src/cover/templates/cover/image_list.html b/src/cover/templates/cover/image_list.html
new file mode 100755 (executable)
index 0000000..50443ed
--- /dev/null
@@ -0,0 +1,31 @@
+{% extends "catalogue/base.html" %}
+{% load i18n %}
+{% load thumbnail pagination_tags %}
+
+{% block titleextra %}{% trans "Cover images" %}{% endblock %}
+
+{% block content %}
+<h1>{% trans "Cover images" %}</h1>
+
+{% if can_add %}
+    <a href="{% url 'cover_add_image' %}">{% trans "Add new" %}</a>
+{% endif %}
+
+<ul>
+{% autopaginate object_list 100 %}
+{% for image in object_list %}
+    <a href="{{ image.get_absolute_url }}" style="display:inline-block; width:200px;">
+    
+    <img style="height: 100px"
+        src="{% thumbnail image.file "x100" as thumb %}
+                {{ thumb.url }}
+             {% empty %}
+                {{ image.file.url }}
+             {% endthumbnail %}" />
+        <br/>
+        {{ image }}</a>
+{% endfor %}
+{% paginate %}
+</ul>
+
+{% endblock %}
diff --git a/src/cover/tests.py b/src/cover/tests.py
new file mode 100644 (file)
index 0000000..be8d003
--- /dev/null
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+#
+# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
+from nose.tools import *
+from django.test import TestCase
+from cover.forms import FlickrForm
+
+
+class FlickrTests(TestCase):
+    def test_flickr(self):
+        form = FlickrForm({"source_url": "https://www.flickr.com/photos/rczajka/6941928577/in/photostream"})
+        self.assertTrue(form.is_valid())
+        self.assertEqual(form.cleaned_data['source_url'], "https://www.flickr.com/photos/rczajka/6941928577/")
+        self.assertEqual(form.cleaned_data['author'], "Radek Czajka@Flickr")
+        self.assertEqual(form.cleaned_data['title'], u"Pirate Stańczyk")
+        self.assertEqual(form.cleaned_data['license_name'], "CC BY 2.0")
+        self.assertEqual(form.cleaned_data['license_url'], "https://creativecommons.org/licenses/by/2.0/")
+        self.assertTrue('.staticflickr.com' in form.cleaned_data['download_url'])
diff --git a/src/cover/urls.py b/src/cover/urls.py
new file mode 100644 (file)
index 0000000..1146f62
--- /dev/null
@@ -0,0 +1,21 @@
+# -*- coding: utf-8 -*-
+#
+# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
+from django.conf.urls import patterns, url
+
+
+urlpatterns = patterns('cover.views',
+    url(r'^preview/$', 'preview_from_xml', name='cover_preview'),
+    url(r'^preview/(?P<book>[^/]+)/$', 'preview', name='cover_preview'),
+    url(r'^preview/(?P<book>[^/]+)/(?P<chunk>[^/]+)/$',
+            'preview', name='cover_preview'),
+    url(r'^preview/(?P<book>[^/]+)/(?P<chunk>[^/]+)/(?P<rev>\d+)/$',
+            'preview', name='cover_preview'),
+
+    url(r'^image/$', 'image_list', name='cover_image_list'),
+    url(r'^image/(?P<pk>\d+)/?$', 'image', name='cover_image'),
+    url(r'^image/(?P<pk>\d+)/file/', 'image_file', name='cover_file'),
+    url(r'^add_image/$', 'add_image', name='cover_add_image'),
+)
diff --git a/src/cover/utils.py b/src/cover/utils.py
new file mode 100755 (executable)
index 0000000..51aee19
--- /dev/null
@@ -0,0 +1,73 @@
+# -*- coding: utf-8 -*-
+#
+# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
+import json
+import re
+from urllib import FancyURLopener
+
+from django.contrib.sites.models import Site
+
+
+class URLOpener(FancyURLopener):
+    @property
+    def version(self):
+        return 'FNP Redakcja (http://%s)' % Site.objects.get_current()
+
+
+class FlickrError(Exception):
+    pass
+
+
+def get_flickr_data(url):
+    m = re.match(r'(https?://)?(www\.|secure\.)?flickr\.com/photos/(?P<author>[^/]+)/(?P<img>\d+)/?', url)
+    if not m:
+        raise FlickrError("It doesn't look like Flickr URL.")
+    author_slug, img_id = m.group('author'), m.group('img')
+    base_url = "https://www.flickr.com/photos/%s/%s/" % (author_slug, img_id)
+    try:
+        html = URLOpener().open(url).read().decode('utf-8')
+    except IOError:
+        raise FlickrError('Error reading page')
+    match = re.search(r'<a href="([^"]*)"[^>]* rel="license ', html)
+    if not match:
+        raise FlickrError('License not found.')
+    else:
+        license_url = match.group(1)
+        re_license = re.compile(r'https?://creativecommons.org/licenses/([^/]*)/([^/]*)/.*')
+        m = re_license.match(license_url)
+        if not m:
+            re_pd = re.compile(r'https?://creativecommons.org/publicdomain/([^/]*)/([^/]*)/.*')
+            m = re_pd.match(license_url)
+            if not m:
+                raise FlickrError('License does not look like CC: %s' % license_url)
+            if m.group(1).lower() == 'zero':
+                license_name = 'Public domain (CC0 %s)' % m.group(2)
+            else:
+                license_name = 'Public domain'
+        else:
+            license_name = 'CC %s %s' % (m.group(1).upper(), m.group(2))
+    m = re.search(r'<a[^>]* class="owner-name [^>]*>([^<]*)<', html)
+    if m:
+        author = "%s@Flickr" % m.group(1)
+    else:
+        raise FlickrError('Error reading author name.')
+    m = re.search(r'<h1[^>]*>(.*?)</h1>', html, re.S)
+    if not m:
+        raise FlickrError('Error reading image title.')
+    title = m.group(1).strip()
+    m = re.search(r'modelExport: (\{.*\})', html)
+    try:
+        assert m
+        download_url = 'https:' + json.loads(m.group(1))['main']['photo-models'][0]['sizes']['o']['url']
+    except (AssertionError, ValueError, IndexError, KeyError):
+        raise FlickrError('Error reading image URL.')
+    return {
+        'source_url': base_url,
+        'license_url': license_url,
+        'license_name': license_name,
+        'author': author,
+        'title': title,
+        'download_url': download_url,
+    }
diff --git a/src/cover/views.py b/src/cover/views.py
new file mode 100644 (file)
index 0000000..3f2c46f
--- /dev/null
@@ -0,0 +1,141 @@
+# -*- coding: utf-8 -*-
+#
+# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
+import os.path
+from django.conf import settings
+from django.contrib.auth.decorators import permission_required
+from django.http import HttpResponse, HttpResponseRedirect, Http404
+from django.shortcuts import get_object_or_404, render
+from django.views.decorators.csrf import csrf_exempt
+from django.views.decorators.http import require_POST
+from catalogue.helpers import active_tab
+from catalogue.models import Chunk
+from cover.models import Image
+from cover import forms
+
+PREVIEW_SIZE = (216, 300)
+
+
+def preview(request, book, chunk=None, rev=None):
+    """Creates a cover image.
+
+    If chunk and rev number are given, use version from given revision.
+    If rev is not given, use publishable version.
+    """
+    from PIL import Image
+    from librarian.cover import make_cover
+    from librarian.dcparser import BookInfo
+
+    chunk = Chunk.get(book, chunk)
+    if rev is not None:
+        try:
+            revision = chunk.at_revision(rev)
+        except Chunk.change_model.DoesNotExist:
+            raise Http404
+    else:
+        revision = chunk.publishable()
+        if revision is None:
+            raise Http404
+    xml = revision.materialize().encode('utf-8')
+
+    try:
+        info = BookInfo.from_bytes(xml)
+    except:
+        return HttpResponseRedirect(os.path.join(settings.STATIC_URL, "img/sample_cover.png"))
+    cover = make_cover(info)
+    response = HttpResponse(content_type=cover.mime_type())
+    img = cover.image().resize(PREVIEW_SIZE, Image.ANTIALIAS)
+    img.save(response, cover.format)
+    return response
+
+
+@csrf_exempt
+@require_POST
+def preview_from_xml(request):
+    from hashlib import sha1
+    from PIL import Image
+    from os import makedirs
+    from lxml import etree
+    from librarian.cover import make_cover
+    from librarian.dcparser import BookInfo
+
+    xml = request.POST['xml']
+    try:
+        info = BookInfo.from_bytes(xml.encode('utf-8'))
+    except:
+        return HttpResponse(os.path.join(settings.STATIC_URL, "img/sample_cover.png"))
+    coverid = sha1(etree.tostring(info.to_etree())).hexdigest()
+    cover = make_cover(info)
+
+    cover_dir = 'cover/preview'
+    try:
+        makedirs(os.path.join(settings.MEDIA_ROOT, cover_dir))
+    except OSError:
+        pass
+    fname = os.path.join(cover_dir, "%s.%s" % (coverid, cover.ext()))
+    img = cover.image().resize(PREVIEW_SIZE, Image.ANTIALIAS)
+    img.save(os.path.join(settings.MEDIA_ROOT, fname))
+    return HttpResponse(os.path.join(settings.MEDIA_URL, fname))
+
+
+@active_tab('cover')
+def image(request, pk):
+    img = get_object_or_404(Image, pk=pk)
+
+    if request.user.has_perm('cover.change_image'):
+        if request.method == "POST":
+            form = forms.ImageEditForm(request.POST, request.FILES, instance=img)
+            if form.is_valid():
+                form.save()
+                return HttpResponseRedirect(img.get_absolute_url())
+        else:
+            form = forms.ImageEditForm(instance=img)
+        editable = True
+    else:
+        form = forms.ReadonlyImageEditForm(instance=img)
+        editable = False
+
+    return render(request, "cover/image_detail.html", {
+        "object": Image.objects.get(id=img.id),
+        "form": form,
+        "editable": editable,
+    })
+
+
+def image_file(request, pk):
+    img = get_object_or_404(Image, pk=pk)
+    return HttpResponseRedirect(img.file.url)
+
+
+@active_tab('cover')
+def image_list(request):
+    return render(request, "cover/image_list.html", {
+        'object_list': Image.objects.all(),
+        'can_add': request.user.has_perm('cover.add_image'),
+    })
+
+
+@permission_required('cover.add_image')
+@active_tab('cover')
+def add_image(request):
+    form = ff = None
+    if request.method == 'POST':
+        if request.POST.get('form_id') == 'flickr':
+            ff = forms.FlickrForm(request.POST)
+            if ff.is_valid():
+                form = forms.ImageAddForm(ff.cleaned_data)
+        else:
+            form = forms.ImageAddForm(request.POST, request.FILES)
+            if form.is_valid():
+                obj = form.save()
+                return HttpResponseRedirect(obj.get_absolute_url())
+    if form is None:
+        form = forms.ImageAddForm()
+    if ff is None:
+        ff = forms.FlickrForm()
+    return render(request, 'cover/add_image.html', {
+            'form': form,
+            'ff': ff,
+        })
diff --git a/src/dvcs/__init__.py b/src/dvcs/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/dvcs/locale/pl/LC_MESSAGES/django.mo b/src/dvcs/locale/pl/LC_MESSAGES/django.mo
new file mode 100644 (file)
index 0000000..dfd85c2
Binary files /dev/null and b/src/dvcs/locale/pl/LC_MESSAGES/django.mo differ
diff --git a/src/dvcs/locale/pl/LC_MESSAGES/django.po b/src/dvcs/locale/pl/LC_MESSAGES/django.po
new file mode 100644 (file)
index 0000000..c0365d5
--- /dev/null
@@ -0,0 +1,124 @@
+# 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: 2011-12-14 15:25+0100\n"
+"PO-Revision-Date: 2011-12-14 15:27+0100\n"
+"Last-Translator: Radek Czajka <radoslaw.czajka@nowoczesnapolska.org.pl>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n"
+
+#: models.py:19
+msgid "name"
+msgstr "nazwa"
+
+#: models.py:20
+msgid "slug"
+msgstr "slug"
+
+#: models.py:22
+msgid "ordering"
+msgstr "kolejność"
+
+#: models.py:70
+msgid "author"
+msgstr "autor"
+
+#: models.py:71
+msgid "author name"
+msgstr "imię i nazwisko autora"
+
+#: models.py:73
+#: models.py:77
+msgid "Used if author is not set."
+msgstr "Używane, gdy nie jest ustawiony autor."
+
+#: models.py:75
+msgid "author email"
+msgstr "e-mail autora"
+
+#: models.py:79
+msgid "revision"
+msgstr "rewizja"
+
+#: models.py:83
+msgid "parent"
+msgstr "rodzic"
+
+#: models.py:88
+msgid "merge parent"
+msgstr "drugi rodzic"
+
+#: models.py:91
+msgid "description"
+msgstr "opis"
+
+#: models.py:94
+msgid "publishable"
+msgstr "do publikacji"
+
+#: models.py:176
+msgid "tag"
+msgstr "tag"
+
+#: models.py:176
+#: models.py:178
+#: models.py:194
+#: models.py:196
+msgid "for:"
+msgstr "dla:"
+
+#: models.py:178
+#: models.py:202
+msgid "tags"
+msgstr "tagi"
+
+#: models.py:194
+msgid "change"
+msgstr "zmiana"
+
+#: models.py:196
+msgid "changes"
+msgstr "zmiany"
+
+#: models.py:201
+msgid "document"
+msgstr "dokument"
+
+#: models.py:203
+msgid "data"
+msgstr "dane"
+
+#: models.py:217
+msgid "stage"
+msgstr "etap"
+
+#: models.py:225
+msgid "head"
+msgstr "głowica"
+
+#: models.py:226
+msgid "This document's current head."
+msgstr "Aktualna wersja dokumentu."
+
+#: models.py:230
+msgid "creator"
+msgstr "utworzył"
+
+#: models.py:245
+msgid "user"
+msgstr "użytkownik"
+
+#: models.py:245
+msgid "Work assignment."
+msgstr "Przypisanie pracy użytkownikowi."
+
diff --git a/src/dvcs/models.py b/src/dvcs/models.py
new file mode 100644 (file)
index 0000000..24bdeb3
--- /dev/null
@@ -0,0 +1,332 @@
+# -*- coding: utf-8 -*-
+#
+# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
+from datetime import datetime
+import os.path
+
+from django.contrib.auth.models import User
+from django.core.files.base import ContentFile
+from django.db import models, transaction
+from django.db.models.base import ModelBase
+from django.utils.translation import string_concat, ugettext_lazy as _
+from mercurial import simplemerge
+
+from django.conf import settings
+from dvcs.signals import post_commit, post_publishable
+from dvcs.storage import GzipFileSystemStorage
+
+
+class Tag(models.Model):
+    """A tag (e.g. document stage) which can be applied to a Change."""
+    name = models.CharField(_('name'), max_length=64)
+    slug = models.SlugField(_('slug'), unique=True, max_length=64, null=True, blank=True)
+    ordering = models.IntegerField(_('ordering'))
+
+    _object_cache = {}
+
+    class Meta:
+        abstract = True
+        ordering = ['ordering']
+
+    def __unicode__(self):
+        return self.name
+
+    @classmethod
+    def get(cls, slug):
+        if slug in cls._object_cache:
+            return cls._object_cache[slug]
+        else:
+            obj = cls.objects.get(slug=slug)
+            cls._object_cache[slug] = obj
+            return obj
+
+    @staticmethod
+    def listener_changed(sender, instance, **kwargs):
+        sender._object_cache = {}
+
+    def get_next(self):
+        """
+            Returns the next tag - stage to work on.
+            Returns None for the last stage.
+        """
+        try:
+            return type(self).objects.filter(ordering__gt=self.ordering)[0]
+        except IndexError:
+            return None
+
+models.signals.pre_save.connect(Tag.listener_changed, sender=Tag)
+
+
+def data_upload_to(instance, filename):
+    return "%d/%d" % (instance.tree.pk, instance.pk)
+
+
+class Change(models.Model):
+    """
+        Single document change related to previous change. The "parent"
+        argument points to the version against which this change has been 
+        recorded. Initial text will have a null parent.
+        
+        Data file contains a gzipped text of the document.
+    """
+    author = models.ForeignKey(User, null=True, blank=True, verbose_name=_('author'))
+    author_name = models.CharField(
+        _('author name'), max_length=128, null=True, blank=True, help_text=_("Used if author is not set."))
+    author_email = models.CharField(
+        _('author email'), max_length=128, null=True, blank=True, help_text=_("Used if author is not set."))
+    revision = models.IntegerField(_('revision'), db_index=True)
+
+    parent = models.ForeignKey(
+        'self', null=True, blank=True, default=None, verbose_name=_('parent'), related_name="children")
+
+    merge_parent = models.ForeignKey(
+        'self', null=True, blank=True, default=None, verbose_name=_('merge parent'), related_name="merge_children")
+
+    description = models.TextField(_('description'), blank=True, default='')
+    created_at = models.DateTimeField(editable=False, db_index=True, default=datetime.now)
+    publishable = models.BooleanField(_('publishable'), default=False)
+
+    class Meta:
+        abstract = True
+        ordering = ('created_at',)
+        unique_together = ['tree', 'revision']
+
+    def __unicode__(self):
+        return u"Id: %r, Tree %r, Parent %r, Data: %s" % (self.id, self.tree_id, self.parent_id, self.data)
+
+    def author_str(self):
+        if self.author:
+            return "%s %s <%s>" % (
+                self.author.first_name,
+                self.author.last_name, 
+                self.author.email)
+        else:
+            return "%s <%s>" % (
+                self.author_name,
+                self.author_email
+                )
+
+    def save(self, *args, **kwargs):
+        """
+            take the next available revision number if none yet
+        """
+        if self.revision is None:
+            tree_rev = self.tree.revision()
+            if tree_rev is None:
+                self.revision = 1
+            else:
+                self.revision = tree_rev + 1
+        return super(Change, self).save(*args, **kwargs)
+
+    def materialize(self):
+        f = self.data.storage.open(self.data)
+        text = f.read()
+        f.close()
+        return unicode(text, 'utf-8')
+
+    def merge_with(self, other, author=None, 
+            author_name=None, author_email=None, 
+            description=u"Automatic merge."):
+        """Performs an automatic merge after straying commits."""
+        assert self.tree_id == other.tree_id  # same tree
+        if other.parent_id == self.pk:
+            # immediate child - fast forward
+            return other
+
+        local = self.materialize().encode('utf-8')
+        base = other.parent.materialize().encode('utf-8')
+        remote = other.materialize().encode('utf-8')
+
+        merge = simplemerge.Merge3Text(base, local, remote)
+        result = ''.join(merge.merge_lines())
+        merge_node = self.children.create(
+                    merge_parent=other, tree=self.tree,
+                    author=author,
+                    author_name=author_name,
+                    author_email=author_email,
+                    description=description)
+        merge_node.data.save('', ContentFile(result))
+        return merge_node
+
+    def revert(self, **kwargs):
+        """ commit this version of a doc as new head """
+        self.tree.commit(text=self.materialize(), **kwargs)
+
+    def set_publishable(self, publishable):
+        self.publishable = publishable
+        self.save()
+        post_publishable.send(sender=self, publishable=publishable)
+
+
+def create_tag_model(model):
+    name = model.__name__ + 'Tag'
+
+    class Meta(Tag.Meta):
+        app_label = model._meta.app_label
+        verbose_name = string_concat(
+            _("tag"), " ", _("for:"), " ", model._meta.verbose_name)
+        verbose_name_plural = string_concat(
+            _("tags"), " ", _("for:"), " ", model._meta.verbose_name)
+
+    attrs = {
+        '__module__': model.__module__,
+        'Meta': Meta,
+    }
+    return type(name, (Tag,), attrs)
+
+
+def create_change_model(model):
+    name = model.__name__ + 'Change'
+    repo = GzipFileSystemStorage(location=model.REPO_PATH)
+
+    class Meta(Change.Meta):
+        app_label = model._meta.app_label
+        verbose_name = string_concat(
+            _("change"), " ", _("for:"), " ", model._meta.verbose_name)
+        verbose_name_plural = string_concat(
+            _("changes"), " ", _("for:"), " ", model._meta.verbose_name)
+
+    attrs = {
+        '__module__': model.__module__,
+        'tree': models.ForeignKey(model, related_name='change_set', verbose_name=_('document')),
+        'tags': models.ManyToManyField(model.tag_model, verbose_name=_('tags'), related_name='change_set'),
+        'data': models.FileField(_('data'), upload_to=data_upload_to, storage=repo),
+        'Meta': Meta,
+    }
+    return type(name, (Change,), attrs)
+
+
+class DocumentMeta(ModelBase):
+    """Metaclass for Document models."""
+    def __new__(cls, name, bases, attrs):
+
+        model = super(DocumentMeta, cls).__new__(cls, name, bases, attrs)
+        if not model._meta.abstract:
+            # create a real Tag object and `stage' fk
+            model.tag_model = create_tag_model(model)
+            models.ForeignKey(model.tag_model, verbose_name=_('stage'),
+                null=True, blank=True).contribute_to_class(model, 'stage')
+
+            # create real Change model and `head' fk
+            model.change_model = create_change_model(model)
+
+            models.ForeignKey(
+                model.change_model, null=True, blank=True, default=None,
+                verbose_name=_('head'), help_text=_("This document's current head."),
+                editable=False).contribute_to_class(model, 'head')
+
+            models.ForeignKey(
+                User, null=True, blank=True, editable=False,
+                verbose_name=_('creator'), related_name="created_%s" % name.lower()
+                ).contribute_to_class(model, 'creator')
+
+        return model
+
+
+class Document(models.Model):
+    """File in repository. Subclass it to use version control in your app."""
+
+    __metaclass__ = DocumentMeta
+
+    # default repository path
+    REPO_PATH = os.path.join(settings.MEDIA_ROOT, 'dvcs')
+
+    user = models.ForeignKey(User, null=True, blank=True, verbose_name=_('user'), help_text=_('Work assignment.'))
+
+    class Meta:
+        abstract = True
+
+    def __unicode__(self):
+        return u"{0}, HEAD: {1}".format(self.id, self.head_id)
+
+    def materialize(self, change=None):
+        if self.head is None:
+            return u''
+        if change is None:
+            change = self.head
+        elif not isinstance(change, Change):
+            change = self.change_set.get(pk=change)
+        return change.materialize()
+
+    def commit(self, text, author=None, author_name=None, author_email=None, publishable=False, **kwargs):
+        """Commits a new revision.
+
+        This will automatically merge the commit into the main branch,
+        if parent is not document's head.
+
+        :param unicode text: new version of the document
+        :param parent: parent revision (head, if not specified)
+        :type parent: Change or None
+        :param User author: the commiter
+        :param unicode author_name: commiter name (if ``author`` not specified)
+        :param unicode author_email: commiter e-mail (if ``author`` not specified)
+        :param Tag[] tags: list of tags to apply to the new commit
+        :param bool publishable: set new commit as ready to publish
+        :returns: new head
+        """
+        if 'parent' not in kwargs:
+            parent = self.head
+        else:
+            parent = kwargs['parent']
+            if parent is not None and not isinstance(parent, Change):
+                parent = self.change_set.objects.get(pk=kwargs['parent'])
+
+        tags = kwargs.get('tags', [])
+        if tags:
+            # set stage to next tag after the commited one
+            self.stage = max(tags, key=lambda t: t.ordering).get_next()
+
+        change = self.change_set.create(
+            author=author, author_name=author_name, author_email=author_email,
+            description=kwargs.get('description', ''), publishable=publishable, parent=parent)
+
+        change.tags = tags
+        change.data.save('', ContentFile(text.encode('utf-8')))
+        change.save()
+
+        if self.head:
+            # merge new change as new head
+            self.head = self.head.merge_with(change, author=author,
+                    author_name=author_name,
+                    author_email=author_email)
+        else:
+            self.head = change
+        self.save()
+
+        post_commit.send(sender=self.head)
+
+        return self.head
+
+    def history(self):
+        return self.change_set.all().order_by('revision')
+
+    def revision(self):
+        rev = self.change_set.aggregate(
+                models.Max('revision'))['revision__max']
+        return rev
+
+    def at_revision(self, rev):
+        """Returns a Change with given revision number."""
+        return self.change_set.get(revision=rev)
+
+    def publishable(self):
+        changes = self.history().filter(publishable=True)
+        if changes.exists():
+            return changes.order_by('-revision')[0]
+        else:
+            return None
+
+    @transaction.atomic
+    def prepend_history(self, other):
+        """Takes over the the other document's history and prepends to own."""
+
+        assert self != other
+        other_revs = other.change_set.all().count()
+        # workaround for a non-atomic UPDATE in SQLITE
+        self.change_set.all().update(revision=0-models.F('revision'))
+        self.change_set.all().update(revision=other_revs - models.F('revision'))
+        other.change_set.all().update(tree=self)
+        assert not other.change_set.exists()
+        other.delete()
diff --git a/src/dvcs/signals.py b/src/dvcs/signals.py
new file mode 100755 (executable)
index 0000000..5da075b
--- /dev/null
@@ -0,0 +1,4 @@
+from django.dispatch import Signal
+
+post_commit = Signal()
+post_publishable = Signal(providing_args=['publishable'])
diff --git a/src/dvcs/storage.py b/src/dvcs/storage.py
new file mode 100755 (executable)
index 0000000..6bb5b59
--- /dev/null
@@ -0,0 +1,18 @@
+from zlib import compress, decompress
+
+from django.core.files.base import ContentFile, File
+from django.core.files.storage import FileSystemStorage
+
+
+class GzipFileSystemStorage(FileSystemStorage):
+    def _open(self, name, mode='rb'):
+        """TODO: This is good for reading; what about writing?"""
+        f = open(self.path(name), 'rb')
+        text = f.read()
+        f.close()
+        return ContentFile(decompress(text))
+
+    def _save(self, name, content):
+        content = ContentFile(compress(content.read()))
+
+        return super(GzipFileSystemStorage, self)._save(name, content)
diff --git a/src/dvcs/tests/__init__.py b/src/dvcs/tests/__init__.py
new file mode 100755 (executable)
index 0000000..868f00a
--- /dev/null
@@ -0,0 +1,178 @@
+from nose.tools import *
+from django.test import TestCase
+from dvcs.models import Document
+
+
+class ADocument(Document):
+    class Meta:
+        app_label = 'dvcs'
+
+
+class DocumentModelTests(TestCase):
+
+    def assertTextEqual(self, given, expected):
+        return self.assertEqual(given, expected,
+            "Expected '''%s'''\n differs from text: '''%s'''" % (expected, given)
+        )
+
+    def test_empty_file(self):
+        doc = ADocument.objects.create()
+        self.assertTextEqual(doc.materialize(), u"")
+
+    def test_single_commit(self):
+        doc = ADocument.objects.create()
+        doc.commit(text=u"Ala ma kota", description="Commit #1")
+        self.assertTextEqual(doc.materialize(), u"Ala ma kota")
+
+    def test_chained_commits(self):
+        doc = ADocument.objects.create()
+        text1 = u"""
+            Line #1
+            Line #2 is cool
+        """
+        text2 = u"""
+            Line #1
+            Line #2 is hot
+        """
+        text3 = u"""
+            Line #1
+            ... is hot
+            Line #3 ate Line #2
+        """
+
+        c1 = doc.commit(description="Commit #1", text=text1)
+        c2 = doc.commit(description="Commit #2", text=text2)
+        c3 = doc.commit(description="Commit #3", text=text3)
+
+        self.assertTextEqual(doc.materialize(), text3)
+        self.assertTextEqual(doc.materialize(change=c3), text3)
+        self.assertTextEqual(doc.materialize(change=c2), text2)
+        self.assertTextEqual(doc.materialize(change=c1), text1)
+
+    def test_parallel_commit_noconflict(self):
+        doc = ADocument.objects.create()
+        text1 = u"""
+            Line #1
+            Line #2
+        """
+        text2 = u"""
+            Line #1 is hot
+            Line #2
+        """
+        text3 = u"""
+            Line #1
+            Line #2
+            Line #3
+        """
+        text_merged = u"""
+            Line #1 is hot
+            Line #2
+            Line #3
+        """
+
+        base = doc.commit(description="Commit #1", text=text1)
+        c1 = doc.commit(description="Commit #2", text=text2)
+        commits = doc.change_set.count()
+        c2 = doc.commit(description="Commit #3", text=text3, parent=base)
+        self.assertEqual(doc.change_set.count(), commits + 2,
+            u"Parallel commits should create an additional merge commit")
+        self.assertTextEqual(doc.materialize(), text_merged)
+
+    def test_parallel_commit_conflict(self):
+        doc = ADocument.objects.create()
+        text1 = u"""
+            Line #1
+            Line #2
+            Line #3
+        """
+        text2 = u"""
+            Line #1
+            Line #2 is hot
+            Line #3
+        """
+        text3 = u"""
+            Line #1
+            Line #2 is cool
+            Line #3
+        """
+        text_merged = u"""
+            Line #1
+<<<<<<<
+            Line #2 is hot
+=======
+            Line #2 is cool
+>>>>>>>
+            Line #3
+        """
+        base = doc.commit(description="Commit #1", text=text1)
+        c1 = doc.commit(description="Commit #2", text=text2)
+        commits = doc.change_set.count()
+        c2 = doc.commit(description="Commit #3", text=text3, parent=base)
+        self.assertEqual(doc.change_set.count(), commits + 2,
+            u"Parallel commits should create an additional merge commit")
+        self.assertTextEqual(doc.materialize(), text_merged)
+
+
+    def test_multiple_parallel_commits(self):
+        text_a1 = u"""
+            Line #1
+
+            Line #2
+
+            Line #3
+            """
+        text_a2 = u"""
+            Line #1 *
+
+            Line #2
+
+            Line #3
+            """
+        text_b1 = u"""
+            Line #1
+
+            Line #2 **
+
+            Line #3
+            """
+        text_c1 = u"""
+            Line #1
+
+            Line #2
+
+            Line #3 ***
+            """
+        text_merged = u"""
+            Line #1 *
+
+            Line #2 **
+
+            Line #3 ***
+            """
+
+
+        doc = ADocument.objects.create()
+        c1 = doc.commit(description="Commit A1", text=text_a1)
+        c2 = doc.commit(description="Commit A2", text=text_a2, parent=c1)
+        c3 = doc.commit(description="Commit B1", text=text_b1, parent=c1)
+        c4 = doc.commit(description="Commit C1", text=text_c1, parent=c1)
+        self.assertTextEqual(doc.materialize(), text_merged)
+
+
+    def test_prepend_history(self):
+        doc1 = ADocument.objects.create()
+        doc2 = ADocument.objects.create()
+        doc1.commit(text='Commit 1')
+        doc2.commit(text='Commit 2')
+        doc2.prepend_history(doc1)
+        self.assertEqual(ADocument.objects.all().count(), 1)
+        self.assertTextEqual(doc2.at_revision(1).materialize(), 'Commit 1')
+        self.assertTextEqual(doc2.materialize(), 'Commit 2')
+
+    def test_prepend_to_self(self):
+        doc = ADocument.objects.create()
+        doc.commit(text='Commit 1')
+        with self.assertRaises(AssertionError):
+            doc.prepend_history(doc)
+        self.assertTextEqual(doc.materialize(), 'Commit 1')
+
diff --git a/src/email_mangler/__init__.py b/src/email_mangler/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/email_mangler/locale/pl/LC_MESSAGES/django.mo b/src/email_mangler/locale/pl/LC_MESSAGES/django.mo
new file mode 100644 (file)
index 0000000..ed20bfb
Binary files /dev/null and b/src/email_mangler/locale/pl/LC_MESSAGES/django.mo differ
diff --git a/src/email_mangler/locale/pl/LC_MESSAGES/django.po b/src/email_mangler/locale/pl/LC_MESSAGES/django.po
new file mode 100644 (file)
index 0000000..046b883
--- /dev/null
@@ -0,0 +1,27 @@
+# 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: 2011-11-30 14:27+0100\n"
+"PO-Revision-Date: 2011-11-30 14:27+0100\n"
+"Last-Translator: Radek Czajka <radoslaw.czajka@nowoczesnapolska.org.pl>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n"
+
+#: templatetags/email.py:17
+msgid "at"
+msgstr "na"
+
+#: templatetags/email.py:18
+msgid "dot"
+msgstr "kropka"
+
diff --git a/src/email_mangler/models.py b/src/email_mangler/models.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/email_mangler/templatetags/__init__.py b/src/email_mangler/templatetags/__init__.py
new file mode 100755 (executable)
index 0000000..e69de29
diff --git a/src/email_mangler/templatetags/email.py b/src/email_mangler/templatetags/email.py
new file mode 100755 (executable)
index 0000000..376117a
--- /dev/null
@@ -0,0 +1,25 @@
+from django.utils.html import escape
+from django.utils.safestring import mark_safe
+from django.utils.translation import ugettext as _
+from django import template
+
+register = template.Library()
+
+
+@register.filter
+def email_link(email):
+    email_safe = escape(email)
+    try:
+        name, domain = email_safe.split('@', 1)
+    except ValueError:
+        return email
+
+    at = escape(_('at'))
+    dot = escape(_('dot'))
+    mangled = "%s %s %s" % (name, at, (' %s ' % dot).join(domain.split('.')))
+    return mark_safe("<a class='mangled' data-addr1='%(name)s' "
+        "data-addr2='%(domain)s'>%(mangled)s</a>" % {
+            'name': name.encode('rot13'),
+            'domain': domain.encode('rot13'),
+            'mangled': mangled,
+        })
diff --git a/src/fileupload/__init__.py b/src/fileupload/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/fileupload/forms.py b/src/fileupload/forms.py
new file mode 100644 (file)
index 0000000..f5e1069
--- /dev/null
@@ -0,0 +1,4 @@
+from django import forms
+
+class UploadForm(forms.Form):
+    files = forms.FileField()
diff --git a/src/fileupload/locale/pl/LC_MESSAGES/django.mo b/src/fileupload/locale/pl/LC_MESSAGES/django.mo
new file mode 100644 (file)
index 0000000..8fdb9c4
Binary files /dev/null and b/src/fileupload/locale/pl/LC_MESSAGES/django.mo differ
diff --git a/src/fileupload/locale/pl/LC_MESSAGES/django.po b/src/fileupload/locale/pl/LC_MESSAGES/django.po
new file mode 100644 (file)
index 0000000..a4b6099
--- /dev/null
@@ -0,0 +1,39 @@
+# 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: 2013-03-07 16:27+0100\n"
+"PO-Revision-Date: 2013-03-07 16:27+0100\n"
+"Last-Translator: Radek Czajka <radoslaw.czajka@nowoczesnapolska.org.pl>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
+
+#: templates/fileupload/picture_form.html:18
+msgid "Browse:"
+msgstr "Przeglądanie:"
+
+#: templates/fileupload/picture_form.html:35
+msgid "Add files..."
+msgstr "Dodaj pliki..."
+
+#: templates/fileupload/picture_form.html:40
+msgid "Start upload"
+msgstr "Zacznij wysyłać"
+
+#: templates/fileupload/picture_form.html:44
+msgid "Cancel upload"
+msgstr "Anuluj wysyłanie"
+
+#: templates/fileupload/picture_form.html:48
+msgid "Delete"
+msgstr "Usuń"
+
diff --git a/src/fileupload/models.py b/src/fileupload/models.py
new file mode 100644 (file)
index 0000000..8b13789
--- /dev/null
@@ -0,0 +1 @@
+
diff --git a/src/fileupload/static/fileupload/css/bootstrap-image-gallery.min.css b/src/fileupload/static/fileupload/css/bootstrap-image-gallery.min.css
new file mode 100644 (file)
index 0000000..a2bffbf
--- /dev/null
@@ -0,0 +1,21 @@
+@charset 'UTF-8';
+.modal-gallery{width:auto;max-height:none;}
+.modal-gallery .modal-body{max-height:none;}
+.modal-gallery .modal-title{display:inline-block;max-height:54px;overflow:hidden;}
+.modal-gallery .modal-image{position:relative;margin:auto;min-width:128px;min-height:128px;overflow:hidden;cursor:pointer;}
+.modal-gallery .modal-image:hover:before,.modal-gallery .modal-image:hover:after{content:'‹';position:absolute;top:50%;left:15px;width:40px;height:40px;margin-top:-20px;font-size:60px;font-weight:100;line-height:30px;color:#ffffff;text-align:center;background:#222222;border:3px solid #ffffff;-webkit-border-radius:23px;-moz-border-radius:23px;border-radius:23px;opacity:0.5;filter:alpha(opacity=50);z-index:1;}
+.modal-gallery .modal-image:hover:after{content:'›';left:auto;right:15px;}
+.modal-single .modal-image:hover:before,.modal-single .modal-image:hover:after{display:none;}
+.modal-loading .modal-image{background:url(../img/loading.gif) center no-repeat;}
+.modal-gallery.fade .modal-image{-webkit-transition:width 0.15s ease, height 0.15s ease;-moz-transition:width 0.15s ease, height 0.15s ease;-ms-transition:width 0.15s ease, height 0.15s ease;-o-transition:width 0.15s ease, height 0.15s ease;transition:width 0.15s ease, height 0.15s ease;}
+.modal-gallery .modal-image *{position:absolute;top:0;opacity:0;filter:alpha(opacity=0);}
+.modal-gallery.fade .modal-image *{-webkit-transition:opacity 0.5s linear;-moz-transition:opacity 0.5s linear;-ms-transition:opacity 0.5s linear;-o-transition:opacity 0.5s linear;transition:opacity 0.5s linear;}
+.modal-gallery .modal-image *.in{opacity:1;filter:alpha(opacity=100);}
+.modal-fullscreen{border:none;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;background:transparent;overflow:hidden;}
+.modal-fullscreen.modal-loading{border:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;}
+.modal-fullscreen .modal-body{padding:0;}
+.modal-fullscreen .modal-header,.modal-fullscreen .modal-footer{position:absolute;top:0;right:0;left:0;background:transparent;border:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;opacity:0;z-index:2000;}
+.modal-fullscreen .modal-footer{top:auto;bottom:0;}
+.modal-fullscreen .close,.modal-fullscreen .modal-title{color:#fff;text-shadow:0 0 2px rgba(33, 33, 33, 0.8);}
+.modal-fullscreen .modal-header:hover,.modal-fullscreen .modal-footer:hover{opacity:1;}
+@media (max-width:480px){.modal-gallery .btn span{display:none;}}
diff --git a/src/fileupload/static/fileupload/css/bootstrap.min.css b/src/fileupload/static/fileupload/css/bootstrap.min.css
new file mode 100644 (file)
index 0000000..9999524
--- /dev/null
@@ -0,0 +1,766 @@
+article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block;}
+audio,canvas,video{display:inline-block;*display:inline;*zoom:1;}
+audio:not([controls]){display:none;}
+a:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;}
+a:hover,a:active{outline:0;}
+sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline;}
+sup{top:-0.5em;}
+sub{bottom:-0.25em;}
+img{max-width:100%;vertical-align:middle;border:0;-ms-interpolation-mode:bicubic;}
+button,input,select,textarea{margin:0;font-size:100%;vertical-align:middle;}
+button,input{*overflow:visible;line-height:normal;}
+button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0;}
+button,input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button;}
+input[type="search"]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield;}
+input[type="search"]::-webkit-search-decoration,input[type="search"]::-webkit-search-cancel-button{-webkit-appearance:none;}
+textarea{overflow:auto;vertical-align:top;}
+.clearfix{*zoom:1;}
+.clearfix:before,.clearfix:after{display:table;content:"";}
+.clearfix:after{clear:both;}
+.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0;}
+.input-block-level{display:block;width:100%;min-height:28px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;}
+.row{margin-left:-20px;*zoom:1;}
+.row:before,.row:after{display:table;content:"";}
+.row:after{clear:both;}
+[class*="span"]{float:left;margin-left:20px;}
+.container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px;}
+.span12{width:940px;}
+.span11{width:860px;}
+.span10{width:780px;}
+.span9{width:700px;}
+.span8{width:620px;}
+.span7{width:540px;}
+.span6{width:460px;}
+.span5{width:380px;}
+.span4{width:300px;}
+.span3{width:220px;}
+.span2{width:140px;}
+.span1{width:60px;}
+.offset12{margin-left:980px;}
+.offset11{margin-left:900px;}
+.offset10{margin-left:820px;}
+.offset9{margin-left:740px;}
+.offset8{margin-left:660px;}
+.offset7{margin-left:580px;}
+.offset6{margin-left:500px;}
+.offset5{margin-left:420px;}
+.offset4{margin-left:340px;}
+.offset3{margin-left:260px;}
+.offset2{margin-left:180px;}
+.offset1{margin-left:100px;}
+.row-fluid{width:100%;*zoom:1;}
+.row-fluid:before,.row-fluid:after{display:table;content:"";}
+.row-fluid:after{clear:both;}
+.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:28px;margin-left:2.127659574%;*margin-left:2.0744680846382977%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;}
+.row-fluid [class*="span"]:first-child{margin-left:0;}
+.row-fluid .span12{width:99.99999998999999%;*width:99.94680850063828%;}
+.row-fluid .span11{width:91.489361693%;*width:91.4361702036383%;}
+.row-fluid .span10{width:82.97872339599999%;*width:82.92553190663828%;}
+.row-fluid .span9{width:74.468085099%;*width:74.4148936096383%;}
+.row-fluid .span8{width:65.95744680199999%;*width:65.90425531263828%;}
+.row-fluid .span7{width:57.446808505%;*width:57.3936170156383%;}
+.row-fluid .span6{width:48.93617020799999%;*width:48.88297871863829%;}
+.row-fluid .span5{width:40.425531911%;*width:40.3723404216383%;}
+.row-fluid .span4{width:31.914893614%;*width:31.8617021246383%;}
+.row-fluid .span3{width:23.404255317%;*width:23.3510638276383%;}
+.row-fluid .span2{width:14.89361702%;*width:14.8404255306383%;}
+.row-fluid .span1{width:6.382978723%;*width:6.329787233638298%;}
+.container{margin-right:auto;margin-left:auto;*zoom:1;}
+.container:before,.container:after{display:table;content:"";}
+.container:after{clear:both;}
+.container-fluid{padding-right:20px;padding-left:20px;*zoom:1;}
+.container-fluid:before,.container-fluid:after{display:table;content:"";}
+.container-fluid:after{clear:both;}
+p{margin:0 0 9px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;line-height:18px;}
+p small{font-size:11px;color:#999999;}
+.lead{margin-bottom:18px;font-size:20px;font-weight:200;line-height:27px;}
+.page-header{padding-bottom:17px;margin:18px 0;border-bottom:1px solid #eeeeee;}
+.page-header h1{line-height:1;}
+ul,ol{padding:0;margin:0 0 9px 25px;}
+ul ul,ul ol,ol ol,ol ul{margin-bottom:0;}
+ul{list-style:disc;}
+ol{list-style:decimal;}
+li{line-height:18px;}
+ul.unstyled,ol.unstyled{margin-left:0;list-style:none;}
+dl{margin-bottom:18px;}
+dt,dd{line-height:18px;}
+dt{font-weight:bold;line-height:17px;}
+dd{margin-left:9px;}
+.dl-horizontal dt{float:left;width:120px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap;}
+.dl-horizontal dd{margin-left:130px;}
+hr{margin:18px 0;border:0;border-top:1px solid #eeeeee;border-bottom:1px solid #ffffff;}
+strong{font-weight:bold;}
+em{font-style:italic;}
+.muted{color:#999999;}
+abbr[title]{cursor:help;border-bottom:1px dotted #ddd;}
+abbr.initialism{font-size:90%;text-transform:uppercase;}
+blockquote{padding:0 0 0 15px;margin:0 0 18px;border-left:5px solid #eeeeee;}
+blockquote p{margin-bottom:0;font-size:16px;font-weight:300;line-height:22.5px;}
+blockquote small{display:block;line-height:18px;color:#999999;}
+blockquote small:before{content:'\2014 \00A0';}
+blockquote.pull-right{float:right;padding-right:15px;padding-left:0;border-right:5px solid #eeeeee;border-left:0;}
+blockquote.pull-right p,blockquote.pull-right small{text-align:right;}
+q:before,q:after,blockquote:before,blockquote:after{content:"";}
+address{display:block;margin-bottom:18px;font-style:normal;line-height:18px;}
+small{font-size:100%;}
+cite{font-style:normal;}
+code,pre{padding:0 3px 2px;font-family:Menlo,Monaco,Consolas,"Courier New",monospace;font-size:12px;color:#333333;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;}
+code{padding:2px 4px;color:#d14;background-color:#f7f7f9;border:1px solid #e1e1e8;}
+pre{display:block;padding:8.5px;margin:0 0 9px;font-size:12.025px;line-height:18px;word-break:break-all;word-wrap:break-word;white-space:pre;white-space:pre-wrap;background-color:#f5f5f5;border:1px solid #ccc;border:1px solid rgba(0, 0, 0, 0.15);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}
+pre.prettyprint{margin-bottom:18px;}
+pre code{padding:0;color:inherit;background-color:transparent;border:0;}
+.pre-scrollable{max-height:340px;overflow-y:scroll;}
+form{margin:0 0 18px;}
+fieldset{padding:0;margin:0;border:0;}
+legend{display:block;width:100%;padding:0;margin-bottom:27px;font-size:19.5px;line-height:36px;color:#333333;border:0;border-bottom:1px solid #eee;}
+legend small{font-size:13.5px;color:#999999;}
+label,input,button,select,textarea{font-size:13px;font-weight:normal;line-height:18px;}
+input,button,select,textarea{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;}
+label{display:block;margin-bottom:5px;color:#333333;}
+input,textarea,select,.uneditable-input{display:inline-block;width:210px;height:18px;padding:4px;margin-bottom:9px;font-size:13px;line-height:18px;color:#555555;background-color:#ffffff;border:1px solid #cccccc;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;}
+.uneditable-textarea{width:auto;height:auto;}
+label input,label textarea,label select{display:block;}
+input[type="image"],input[type="checkbox"],input[type="radio"]{width:auto;height:auto;padding:0;margin:3px 0;*margin-top:0;line-height:normal;cursor:pointer;background-color:transparent;border:0 \9;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}
+input[type="image"]{border:0;}
+input[type="file"]{width:auto;padding:initial;line-height:initial;background-color:#ffffff;background-color:initial;border:initial;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;}
+input[type="button"],input[type="reset"],input[type="submit"]{width:auto;height:auto;}
+select,input[type="file"]{height:28px;*margin-top:4px;line-height:28px;}
+input[type="file"]{line-height:18px \9;}
+select{width:220px;background-color:#ffffff;}
+select[multiple],select[size]{height:auto;}
+input[type="image"]{-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;}
+textarea{height:auto;}
+input[type="hidden"]{display:none;}
+.radio,.checkbox{min-height:18px;padding-left:18px;}
+.radio input[type="radio"],.checkbox input[type="checkbox"]{float:left;margin-left:-18px;}
+.controls>.radio:first-child,.controls>.checkbox:first-child{padding-top:5px;}
+.radio.inline,.checkbox.inline{display:inline-block;padding-top:5px;margin-bottom:0;vertical-align:middle;}
+.radio.inline+.radio.inline,.checkbox.inline+.checkbox.inline{margin-left:10px;}
+input,textarea{-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);-webkit-transition:border linear 0.2s,box-shadow linear 0.2s;-moz-transition:border linear 0.2s,box-shadow linear 0.2s;-ms-transition:border linear 0.2s,box-shadow linear 0.2s;-o-transition:border linear 0.2s,box-shadow linear 0.2s;transition:border linear 0.2s,box-shadow linear 0.2s;}
+input:focus,textarea:focus{border-color:rgba(82, 168, 236, 0.8);outline:0;outline:thin dotted \9;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075),0 0 8px rgba(82, 168, 236, 0.6);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075),0 0 8px rgba(82, 168, 236, 0.6);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075),0 0 8px rgba(82, 168, 236, 0.6);}
+input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus,select:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;}
+.input-mini{width:60px;}
+.input-small{width:90px;}
+.input-medium{width:150px;}
+.input-large{width:210px;}
+.input-xlarge{width:270px;}
+.input-xxlarge{width:530px;}
+input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input[class*="span"],.row-fluid input[class*="span"],.row-fluid select[class*="span"],.row-fluid textarea[class*="span"],.row-fluid .uneditable-input[class*="span"]{float:none;margin-left:0;}
+input,textarea,.uneditable-input{margin-left:0;}
+input.span12,textarea.span12,.uneditable-input.span12{width:930px;}
+input.span11,textarea.span11,.uneditable-input.span11{width:850px;}
+input.span10,textarea.span10,.uneditable-input.span10{width:770px;}
+input.span9,textarea.span9,.uneditable-input.span9{width:690px;}
+input.span8,textarea.span8,.uneditable-input.span8{width:610px;}
+input.span7,textarea.span7,.uneditable-input.span7{width:530px;}
+input.span6,textarea.span6,.uneditable-input.span6{width:450px;}
+input.span5,textarea.span5,.uneditable-input.span5{width:370px;}
+input.span4,textarea.span4,.uneditable-input.span4{width:290px;}
+input.span3,textarea.span3,.uneditable-input.span3{width:210px;}
+input.span2,textarea.span2,.uneditable-input.span2{width:130px;}
+input.span1,textarea.span1,.uneditable-input.span1{width:50px;}
+input[disabled],select[disabled],textarea[disabled],input[readonly],select[readonly],textarea[readonly]{cursor:not-allowed;background-color:#eeeeee;border-color:#ddd;}
+input[type="radio"][disabled],input[type="checkbox"][disabled],input[type="radio"][readonly],input[type="checkbox"][readonly]{background-color:transparent;}
+.control-group.warning>label,.control-group.warning .help-block,.control-group.warning .help-inline{color:#c09853;}
+.control-group.warning input,.control-group.warning select,.control-group.warning textarea{color:#c09853;border-color:#c09853;}
+.control-group.warning input:focus,.control-group.warning select:focus,.control-group.warning textarea:focus{border-color:#a47e3c;-webkit-box-shadow:0 0 6px #dbc59e;-moz-box-shadow:0 0 6px #dbc59e;box-shadow:0 0 6px #dbc59e;}
+.control-group.warning .input-prepend .add-on,.control-group.warning .input-append .add-on{color:#c09853;background-color:#fcf8e3;border-color:#c09853;}
+.control-group.error>label,.control-group.error .help-block,.control-group.error .help-inline{color:#b94a48;}
+.control-group.error input,.control-group.error select,.control-group.error textarea{color:#b94a48;border-color:#b94a48;}
+.control-group.error input:focus,.control-group.error select:focus,.control-group.error textarea:focus{border-color:#953b39;-webkit-box-shadow:0 0 6px #d59392;-moz-box-shadow:0 0 6px #d59392;box-shadow:0 0 6px #d59392;}
+.control-group.error .input-prepend .add-on,.control-group.error .input-append .add-on{color:#b94a48;background-color:#f2dede;border-color:#b94a48;}
+.control-group.success>label,.control-group.success .help-block,.control-group.success .help-inline{color:#468847;}
+.control-group.success input,.control-group.success select,.control-group.success textarea{color:#468847;border-color:#468847;}
+.control-group.success input:focus,.control-group.success select:focus,.control-group.success textarea:focus{border-color:#356635;-webkit-box-shadow:0 0 6px #7aba7b;-moz-box-shadow:0 0 6px #7aba7b;box-shadow:0 0 6px #7aba7b;}
+.control-group.success .input-prepend .add-on,.control-group.success .input-append .add-on{color:#468847;background-color:#dff0d8;border-color:#468847;}
+input:focus:required:invalid,textarea:focus:required:invalid,select:focus:required:invalid{color:#b94a48;border-color:#ee5f5b;}
+input:focus:required:invalid:focus,textarea:focus:required:invalid:focus,select:focus:required:invalid:focus{border-color:#e9322d;-webkit-box-shadow:0 0 6px #f8b9b7;-moz-box-shadow:0 0 6px #f8b9b7;box-shadow:0 0 6px #f8b9b7;}
+.form-actions{padding:17px 20px 18px;margin-top:18px;margin-bottom:18px;background-color:#f5f5f5;border-top:1px solid #ddd;*zoom:1;}
+.form-actions:before,.form-actions:after{display:table;content:"";}
+.form-actions:after{clear:both;}
+.uneditable-input{overflow:hidden;white-space:nowrap;cursor:not-allowed;background-color:#ffffff;border-color:#eee;-webkit-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.025);-moz-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.025);box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.025);}
+:-moz-placeholder{color:#999999;}
+::-webkit-input-placeholder{color:#999999;}
+.help-block,.help-inline{color:#555555;}
+.help-block{display:block;margin-bottom:9px;}
+.help-inline{display:inline-block;*display:inline;padding-left:5px;vertical-align:middle;*zoom:1;}
+.input-prepend,.input-append{margin-bottom:5px;}
+.input-prepend input,.input-append input,.input-prepend select,.input-append select,.input-prepend .uneditable-input,.input-append .uneditable-input{position:relative;margin-bottom:0;*margin-left:0;vertical-align:middle;-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0;}
+.input-prepend input:focus,.input-append input:focus,.input-prepend select:focus,.input-append select:focus,.input-prepend .uneditable-input:focus,.input-append .uneditable-input:focus{z-index:2;}
+.input-prepend .uneditable-input,.input-append .uneditable-input{border-left-color:#ccc;}
+.input-prepend .add-on,.input-append .add-on{display:inline-block;width:auto;height:18px;min-width:16px;padding:4px 5px;font-weight:normal;line-height:18px;text-align:center;text-shadow:0 1px 0 #ffffff;vertical-align:middle;background-color:#eeeeee;border:1px solid #ccc;}
+.input-prepend .add-on,.input-append .add-on,.input-prepend .btn,.input-append .btn{margin-left:-1px;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}
+.input-prepend .active,.input-append .active{background-color:#a9dba9;border-color:#46a546;}
+.input-prepend .add-on,.input-prepend .btn{margin-right:-1px;}
+.input-prepend .add-on:first-child,.input-prepend .btn:first-child{-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;}
+.input-append input,.input-append select,.input-append .uneditable-input{-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;}
+.input-append .uneditable-input{border-right-color:#ccc;border-left-color:#eee;}
+.input-append .add-on:last-child,.input-append .btn:last-child{-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0;}
+.input-prepend.input-append input,.input-prepend.input-append select,.input-prepend.input-append .uneditable-input{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}
+.input-prepend.input-append .add-on:first-child,.input-prepend.input-append .btn:first-child{margin-right:-1px;-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;}
+.input-prepend.input-append .add-on:last-child,.input-prepend.input-append .btn:last-child{margin-left:-1px;-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0;}
+.search-query{padding-right:14px;padding-right:4px \9;padding-left:14px;padding-left:4px \9;margin-bottom:0;-webkit-border-radius:14px;-moz-border-radius:14px;border-radius:14px;}
+.form-search input,.form-inline input,.form-horizontal input,.form-search textarea,.form-inline textarea,.form-horizontal textarea,.form-search select,.form-inline select,.form-horizontal select,.form-search .help-inline,.form-inline .help-inline,.form-horizontal .help-inline,.form-search .uneditable-input,.form-inline .uneditable-input,.form-horizontal .uneditable-input,.form-search .input-prepend,.form-inline .input-prepend,.form-horizontal .input-prepend,.form-search .input-append,.form-inline .input-append,.form-horizontal .input-append{display:inline-block;*display:inline;margin-bottom:0;*zoom:1;}
+.form-search .hide,.form-inline .hide,.form-horizontal .hide{display:none;}
+.form-search label,.form-inline label{display:inline-block;}
+.form-search .input-append,.form-inline .input-append,.form-search .input-prepend,.form-inline .input-prepend{margin-bottom:0;}
+.form-search .radio,.form-search .checkbox,.form-inline .radio,.form-inline .checkbox{padding-left:0;margin-bottom:0;vertical-align:middle;}
+.form-search .radio input[type="radio"],.form-search .checkbox input[type="checkbox"],.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{float:left;margin-right:3px;margin-left:0;}
+.control-group{margin-bottom:9px;}
+legend+.control-group{margin-top:18px;-webkit-margin-top-collapse:separate;}
+.form-horizontal .control-group{margin-bottom:18px;*zoom:1;}
+.form-horizontal .control-group:before,.form-horizontal .control-group:after{display:table;content:"";}
+.form-horizontal .control-group:after{clear:both;}
+.form-horizontal .control-label{float:left;width:140px;padding-top:5px;text-align:right;}
+.form-horizontal .controls{*display:inline-block;*padding-left:20px;margin-left:160px;*margin-left:0;}
+.form-horizontal .controls:first-child{*padding-left:160px;}
+.form-horizontal .help-block{margin-top:9px;margin-bottom:0;}
+.form-horizontal .form-actions{padding-left:160px;}
+table{max-width:100%;background-color:transparent;border-collapse:collapse;border-spacing:0;}
+.table{width:100%;margin-bottom:18px;}
+.table th,.table td{padding:8px;line-height:18px;text-align:left;vertical-align:top;border-top:1px solid #dddddd;}
+.table th{font-weight:bold;}
+.table thead th{vertical-align:bottom;}
+.table caption+thead tr:first-child th,.table caption+thead tr:first-child td,.table colgroup+thead tr:first-child th,.table colgroup+thead tr:first-child td,.table thead:first-child tr:first-child th,.table thead:first-child tr:first-child td{border-top:0;}
+.table tbody+tbody{border-top:2px solid #dddddd;}
+.table-condensed th,.table-condensed td{padding:4px 5px;}
+.table-bordered{border:1px solid #dddddd;border-collapse:separate;*border-collapse:collapsed;border-left:0;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}
+.table-bordered th,.table-bordered td{border-left:1px solid #dddddd;}
+.table-bordered caption+thead tr:first-child th,.table-bordered caption+tbody tr:first-child th,.table-bordered caption+tbody tr:first-child td,.table-bordered colgroup+thead tr:first-child th,.table-bordered colgroup+tbody tr:first-child th,.table-bordered colgroup+tbody tr:first-child td,.table-bordered thead:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child td{border-top:0;}
+.table-bordered thead:first-child tr:first-child th:first-child,.table-bordered tbody:first-child tr:first-child td:first-child{-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topleft:4px;}
+.table-bordered thead:first-child tr:first-child th:last-child,.table-bordered tbody:first-child tr:first-child td:last-child{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-topright:4px;}
+.table-bordered thead:last-child tr:last-child th:first-child,.table-bordered tbody:last-child tr:last-child td:first-child{-webkit-border-radius:0 0 0 4px;-moz-border-radius:0 0 0 4px;border-radius:0 0 0 4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-moz-border-radius-bottomleft:4px;}
+.table-bordered thead:last-child tr:last-child th:last-child,.table-bordered tbody:last-child tr:last-child td:last-child{-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-bottomright:4px;}
+.table-striped tbody tr:nth-child(odd) td,.table-striped tbody tr:nth-child(odd) th{background-color:#f9f9f9;}
+.table tbody tr:hover td,.table tbody tr:hover th{background-color:#f5f5f5;}
+table .span1{float:none;width:44px;margin-left:0;}
+table .span2{float:none;width:124px;margin-left:0;}
+table .span3{float:none;width:204px;margin-left:0;}
+table .span4{float:none;width:284px;margin-left:0;}
+table .span5{float:none;width:364px;margin-left:0;}
+table .span6{float:none;width:444px;margin-left:0;}
+table .span7{float:none;width:524px;margin-left:0;}
+table .span8{float:none;width:604px;margin-left:0;}
+table .span9{float:none;width:684px;margin-left:0;}
+table .span10{float:none;width:764px;margin-left:0;}
+table .span11{float:none;width:844px;margin-left:0;}
+table .span12{float:none;width:924px;margin-left:0;}
+table .span13{float:none;width:1004px;margin-left:0;}
+table .span14{float:none;width:1084px;margin-left:0;}
+table .span15{float:none;width:1164px;margin-left:0;}
+table .span16{float:none;width:1244px;margin-left:0;}
+table .span17{float:none;width:1324px;margin-left:0;}
+table .span18{float:none;width:1404px;margin-left:0;}
+table .span19{float:none;width:1484px;margin-left:0;}
+table .span20{float:none;width:1564px;margin-left:0;}
+table .span21{float:none;width:1644px;margin-left:0;}
+table .span22{float:none;width:1724px;margin-left:0;}
+table .span23{float:none;width:1804px;margin-left:0;}
+table .span24{float:none;width:1884px;margin-left:0;}
+[class^="icon-"],[class*=" icon-"]{display:inline-block;width:14px;height:14px;*margin-right:.3em;line-height:14px;vertical-align:text-top;background-image:url("../img/glyphicons-halflings.png");background-position:14px 14px;background-repeat:no-repeat;}
+[class^="icon-"]:last-child,[class*=" icon-"]:last-child{*margin-left:0;}
+.icon-white{background-image:url("../img/glyphicons-halflings-white.png");}
+.icon-glass{background-position:0 0;}
+.icon-music{background-position:-24px 0;}
+.icon-search{background-position:-48px 0;}
+.icon-envelope{background-position:-72px 0;}
+.icon-heart{background-position:-96px 0;}
+.icon-star{background-position:-120px 0;}
+.icon-star-empty{background-position:-144px 0;}
+.icon-user{background-position:-168px 0;}
+.icon-film{background-position:-192px 0;}
+.icon-th-large{background-position:-216px 0;}
+.icon-th{background-position:-240px 0;}
+.icon-th-list{background-position:-264px 0;}
+.icon-ok{background-position:-288px 0;}
+.icon-remove{background-position:-312px 0;}
+.icon-zoom-in{background-position:-336px 0;}
+.icon-zoom-out{background-position:-360px 0;}
+.icon-off{background-position:-384px 0;}
+.icon-signal{background-position:-408px 0;}
+.icon-cog{background-position:-432px 0;}
+.icon-trash{background-position:-456px 0;}
+.icon-home{background-position:0 -24px;}
+.icon-file{background-position:-24px -24px;}
+.icon-time{background-position:-48px -24px;}
+.icon-road{background-position:-72px -24px;}
+.icon-download-alt{background-position:-96px -24px;}
+.icon-download{background-position:-120px -24px;}
+.icon-upload{background-position:-144px -24px;}
+.icon-inbox{background-position:-168px -24px;}
+.icon-play-circle{background-position:-192px -24px;}
+.icon-repeat{background-position:-216px -24px;}
+.icon-refresh{background-position:-240px -24px;}
+.icon-list-alt{background-position:-264px -24px;}
+.icon-lock{background-position:-287px -24px;}
+.icon-flag{background-position:-312px -24px;}
+.icon-headphones{background-position:-336px -24px;}
+.icon-volume-off{background-position:-360px -24px;}
+.icon-volume-down{background-position:-384px -24px;}
+.icon-volume-up{background-position:-408px -24px;}
+.icon-qrcode{background-position:-432px -24px;}
+.icon-barcode{background-position:-456px -24px;}
+.icon-tag{background-position:0 -48px;}
+.icon-tags{background-position:-25px -48px;}
+.icon-book{background-position:-48px -48px;}
+.icon-bookmark{background-position:-72px -48px;}
+.icon-print{background-position:-96px -48px;}
+.icon-camera{background-position:-120px -48px;}
+.icon-font{background-position:-144px -48px;}
+.icon-bold{background-position:-167px -48px;}
+.icon-italic{background-position:-192px -48px;}
+.icon-text-height{background-position:-216px -48px;}
+.icon-text-width{background-position:-240px -48px;}
+.icon-align-left{background-position:-264px -48px;}
+.icon-align-center{background-position:-288px -48px;}
+.icon-align-right{background-position:-312px -48px;}
+.icon-align-justify{background-position:-336px -48px;}
+.icon-list{background-position:-360px -48px;}
+.icon-indent-left{background-position:-384px -48px;}
+.icon-indent-right{background-position:-408px -48px;}
+.icon-facetime-video{background-position:-432px -48px;}
+.icon-picture{background-position:-456px -48px;}
+.icon-pencil{background-position:0 -72px;}
+.icon-map-marker{background-position:-24px -72px;}
+.icon-adjust{background-position:-48px -72px;}
+.icon-tint{background-position:-72px -72px;}
+.icon-edit{background-position:-96px -72px;}
+.icon-share{background-position:-120px -72px;}
+.icon-check{background-position:-144px -72px;}
+.icon-move{background-position:-168px -72px;}
+.icon-step-backward{background-position:-192px -72px;}
+.icon-fast-backward{background-position:-216px -72px;}
+.icon-backward{background-position:-240px -72px;}
+.icon-play{background-position:-264px -72px;}
+.icon-pause{background-position:-288px -72px;}
+.icon-stop{background-position:-312px -72px;}
+.icon-forward{background-position:-336px -72px;}
+.icon-fast-forward{background-position:-360px -72px;}
+.icon-step-forward{background-position:-384px -72px;}
+.icon-eject{background-position:-408px -72px;}
+.icon-chevron-left{background-position:-432px -72px;}
+.icon-chevron-right{background-position:-456px -72px;}
+.icon-plus-sign{background-position:0 -96px;}
+.icon-minus-sign{background-position:-24px -96px;}
+.icon-remove-sign{background-position:-48px -96px;}
+.icon-ok-sign{background-position:-72px -96px;}
+.icon-question-sign{background-position:-96px -96px;}
+.icon-info-sign{background-position:-120px -96px;}
+.icon-screenshot{background-position:-144px -96px;}
+.icon-remove-circle{background-position:-168px -96px;}
+.icon-ok-circle{background-position:-192px -96px;}
+.icon-ban-circle{background-position:-216px -96px;}
+.icon-arrow-left{background-position:-240px -96px;}
+.icon-arrow-right{background-position:-264px -96px;}
+.icon-arrow-up{background-position:-289px -96px;}
+.icon-arrow-down{background-position:-312px -96px;}
+.icon-share-alt{background-position:-336px -96px;}
+.icon-resize-full{background-position:-360px -96px;}
+.icon-resize-small{background-position:-384px -96px;}
+.icon-plus{background-position:-408px -96px;}
+.icon-minus{background-position:-433px -96px;}
+.icon-asterisk{background-position:-456px -96px;}
+.icon-exclamation-sign{background-position:0 -120px;}
+.icon-gift{background-position:-24px -120px;}
+.icon-leaf{background-position:-48px -120px;}
+.icon-fire{background-position:-72px -120px;}
+.icon-eye-open{background-position:-96px -120px;}
+.icon-eye-close{background-position:-120px -120px;}
+.icon-warning-sign{background-position:-144px -120px;}
+.icon-plane{background-position:-168px -120px;}
+.icon-calendar{background-position:-192px -120px;}
+.icon-random{background-position:-216px -120px;}
+.icon-comment{background-position:-240px -120px;}
+.icon-magnet{background-position:-264px -120px;}
+.icon-chevron-up{background-position:-288px -120px;}
+.icon-chevron-down{background-position:-313px -119px;}
+.icon-retweet{background-position:-336px -120px;}
+.icon-shopping-cart{background-position:-360px -120px;}
+.icon-folder-close{background-position:-384px -120px;}
+.icon-folder-open{background-position:-408px -120px;}
+.icon-resize-vertical{background-position:-432px -119px;}
+.icon-resize-horizontal{background-position:-456px -118px;}
+.icon-hdd{background-position:0 -144px;}
+.icon-bullhorn{background-position:-24px -144px;}
+.icon-bell{background-position:-48px -144px;}
+.icon-certificate{background-position:-72px -144px;}
+.icon-thumbs-up{background-position:-96px -144px;}
+.icon-thumbs-down{background-position:-120px -144px;}
+.icon-hand-right{background-position:-144px -144px;}
+.icon-hand-left{background-position:-168px -144px;}
+.icon-hand-up{background-position:-192px -144px;}
+.icon-hand-down{background-position:-216px -144px;}
+.icon-circle-arrow-right{background-position:-240px -144px;}
+.icon-circle-arrow-left{background-position:-264px -144px;}
+.icon-circle-arrow-up{background-position:-288px -144px;}
+.icon-circle-arrow-down{background-position:-312px -144px;}
+.icon-globe{background-position:-336px -144px;}
+.icon-wrench{background-position:-360px -144px;}
+.icon-tasks{background-position:-384px -144px;}
+.icon-filter{background-position:-408px -144px;}
+.icon-briefcase{background-position:-432px -144px;}
+.icon-fullscreen{background-position:-456px -144px;}
+.dropup,.dropdown{position:relative;}
+.dropdown-toggle{*margin-bottom:-3px;}
+.dropdown-toggle:active,.open .dropdown-toggle{outline:0;}
+.caret{display:inline-block;width:0;height:0;vertical-align:top;border-top:4px solid #000000;border-right:4px solid transparent;border-left:4px solid transparent;content:"";opacity:0.3;filter:alpha(opacity=30);}
+.dropdown .caret{margin-top:8px;margin-left:2px;}
+.dropdown:hover .caret,.open .caret{opacity:1;filter:alpha(opacity=100);}
+.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:4px 0;margin:1px 0 0;list-style:none;background-color:#ffffff;border:1px solid #ccc;border:1px solid rgba(0, 0, 0, 0.2);*border-right-width:2px;*border-bottom-width:2px;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;-webkit-box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);-moz-box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box;}
+.dropdown-menu.pull-right{right:0;left:auto;}
+.dropdown-menu .divider{*width:100%;height:1px;margin:8px 1px;*margin:-5px 0 5px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #ffffff;}
+.dropdown-menu a{display:block;padding:3px 15px;clear:both;font-weight:normal;line-height:18px;color:#333333;white-space:nowrap;}
+.dropdown-menu li>a:hover,.dropdown-menu .active>a,.dropdown-menu .active>a:hover{color:#ffffff;text-decoration:none;background-color:#0088cc;}
+.open{*z-index:1000;}
+.open .dropdown-menu{display:block;}
+.pull-right .dropdown-menu{right:0;left:auto;}
+.dropup .caret,.navbar-fixed-bottom .dropdown .caret{border-top:0;border-bottom:4px solid #000000;content:"\2191";}
+.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:1px;}
+.typeahead{margin-top:2px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}
+.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #eee;border:1px solid rgba(0, 0, 0, 0.05);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05);}
+.well blockquote{border-color:#ddd;border-color:rgba(0, 0, 0, 0.15);}
+.well-large{padding:24px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;}
+.well-small{padding:9px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;}
+.fade{opacity:0;-webkit-transition:opacity 0.15s linear;-moz-transition:opacity 0.15s linear;-ms-transition:opacity 0.15s linear;-o-transition:opacity 0.15s linear;transition:opacity 0.15s linear;}
+.fade.in{opacity:1;}
+.collapse{position:relative;height:0;overflow:hidden;-webkit-transition:height 0.35s ease;-moz-transition:height 0.35s ease;-ms-transition:height 0.35s ease;-o-transition:height 0.35s ease;transition:height 0.35s ease;}
+.collapse.in{height:auto;}
+.close{float:right;font-size:20px;font-weight:bold;line-height:18px;color:#000000;text-shadow:0 1px 0 #ffffff;opacity:0.2;filter:alpha(opacity=20);}
+.close:hover{color:#000000;text-decoration:none;cursor:pointer;opacity:0.4;filter:alpha(opacity=40);}
+button.close{padding:0;cursor:pointer;background-color:transparent;border:0;-webkit-appearance:none;}
+.btn{display:inline-block;*display:inline;padding:4px 10px 4px;margin-bottom:0;*margin-left:.3em;font-size:13px;line-height:18px;*line-height:20px;color:#333333;text-align:center;text-shadow:0 1px 1px rgba(255, 255, 255, 0.75);vertical-align:middle;cursor:pointer;background-color:#f5f5f5;*background-color:#e6e6e6;background-image:-ms-linear-gradient(top, #ffffff, #e6e6e6);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#e6e6e6));background-image:-webkit-linear-gradient(top, #ffffff, #e6e6e6);background-image:-o-linear-gradient(top, #ffffff, #e6e6e6);background-image:linear-gradient(top, #ffffff, #e6e6e6);background-image:-moz-linear-gradient(top, #ffffff, #e6e6e6);background-repeat:repeat-x;border:1px solid #cccccc;*border:0;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);border-color:#e6e6e6 #e6e6e6 #bfbfbf;border-bottom-color:#b3b3b3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ffffff', endColorstr='#e6e6e6', GradientType=0);filter:progid:dximagetransform.microsoft.gradient(enabled=false);*zoom:1;-webkit-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);}
+.btn:hover,.btn:active,.btn.active,.btn.disabled,.btn[disabled]{background-color:#e6e6e6;*background-color:#d9d9d9;}
+.btn:active,.btn.active{background-color:#cccccc \9;}
+.btn:first-child{*margin-left:0;}
+.btn:hover{color:#333333;text-decoration:none;background-color:#e6e6e6;*background-color:#d9d9d9;background-position:0 -15px;-webkit-transition:background-position 0.1s linear;-moz-transition:background-position 0.1s linear;-ms-transition:background-position 0.1s linear;-o-transition:background-position 0.1s linear;transition:background-position 0.1s linear;}
+.btn:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;}
+.btn.active,.btn:active{background-color:#e6e6e6;background-color:#d9d9d9 \9;background-image:none;outline:0;-webkit-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);}
+.btn.disabled,.btn[disabled]{cursor:default;background-color:#e6e6e6;background-image:none;opacity:0.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;}
+.btn-large{padding:9px 14px;font-size:15px;line-height:normal;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;}
+.btn-large [class^="icon-"]{margin-top:1px;}
+.btn-small{padding:5px 9px;font-size:11px;line-height:16px;}
+.btn-small [class^="icon-"]{margin-top:-1px;}
+.btn-mini{padding:2px 6px;font-size:11px;line-height:14px;}
+.btn-primary,.btn-primary:hover,.btn-warning,.btn-warning:hover,.btn-danger,.btn-danger:hover,.btn-success,.btn-success:hover,.btn-info,.btn-info:hover,.btn-inverse,.btn-inverse:hover{color:#ffffff;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);}
+.btn-primary.active,.btn-warning.active,.btn-danger.active,.btn-success.active,.btn-info.active,.btn-inverse.active{color:rgba(255, 255, 255, 0.75);}
+.btn{border-color:#ccc;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);}
+.btn-primary{background-color:#0074cc;*background-color:#0055cc;background-image:-ms-linear-gradient(top, #0088cc, #0055cc);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0055cc));background-image:-webkit-linear-gradient(top, #0088cc, #0055cc);background-image:-o-linear-gradient(top, #0088cc, #0055cc);background-image:-moz-linear-gradient(top, #0088cc, #0055cc);background-image:linear-gradient(top, #0088cc, #0055cc);background-repeat:repeat-x;border-color:#0055cc #0055cc #003580;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#0088cc', endColorstr='#0055cc', GradientType=0);filter:progid:dximagetransform.microsoft.gradient(enabled=false);}
+.btn-primary:hover,.btn-primary:active,.btn-primary.active,.btn-primary.disabled,.btn-primary[disabled]{background-color:#0055cc;*background-color:#004ab3;}
+.btn-primary:active,.btn-primary.active{background-color:#004099 \9;}
+.btn-warning{background-color:#faa732;*background-color:#f89406;background-image:-ms-linear-gradient(top, #fbb450, #f89406);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406));background-image:-webkit-linear-gradient(top, #fbb450, #f89406);background-image:-o-linear-gradient(top, #fbb450, #f89406);background-image:-moz-linear-gradient(top, #fbb450, #f89406);background-image:linear-gradient(top, #fbb450, #f89406);background-repeat:repeat-x;border-color:#f89406 #f89406 #ad6704;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#fbb450', endColorstr='#f89406', GradientType=0);filter:progid:dximagetransform.microsoft.gradient(enabled=false);}
+.btn-warning:hover,.btn-warning:active,.btn-warning.active,.btn-warning.disabled,.btn-warning[disabled]{background-color:#f89406;*background-color:#df8505;}
+.btn-warning:active,.btn-warning.active{background-color:#c67605 \9;}
+.btn-danger{background-color:#da4f49;*background-color:#bd362f;background-image:-ms-linear-gradient(top, #ee5f5b, #bd362f);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#bd362f));background-image:-webkit-linear-gradient(top, #ee5f5b, #bd362f);background-image:-o-linear-gradient(top, #ee5f5b, #bd362f);background-image:-moz-linear-gradient(top, #ee5f5b, #bd362f);background-image:linear-gradient(top, #ee5f5b, #bd362f);background-repeat:repeat-x;border-color:#bd362f #bd362f #802420;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ee5f5b', endColorstr='#bd362f', GradientType=0);filter:progid:dximagetransform.microsoft.gradient(enabled=false);}
+.btn-danger:hover,.btn-danger:active,.btn-danger.active,.btn-danger.disabled,.btn-danger[disabled]{background-color:#bd362f;*background-color:#a9302a;}
+.btn-danger:active,.btn-danger.active{background-color:#942a25 \9;}
+.btn-success{background-color:#5bb75b;*background-color:#51a351;background-image:-ms-linear-gradient(top, #62c462, #51a351);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#51a351));background-image:-webkit-linear-gradient(top, #62c462, #51a351);background-image:-o-linear-gradient(top, #62c462, #51a351);background-image:-moz-linear-gradient(top, #62c462, #51a351);background-image:linear-gradient(top, #62c462, #51a351);background-repeat:repeat-x;border-color:#51a351 #51a351 #387038;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#62c462', endColorstr='#51a351', GradientType=0);filter:progid:dximagetransform.microsoft.gradient(enabled=false);}
+.btn-success:hover,.btn-success:active,.btn-success.active,.btn-success.disabled,.btn-success[disabled]{background-color:#51a351;*background-color:#499249;}
+.btn-success:active,.btn-success.active{background-color:#408140 \9;}
+.btn-info{background-color:#49afcd;*background-color:#2f96b4;background-image:-ms-linear-gradient(top, #5bc0de, #2f96b4);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#2f96b4));background-image:-webkit-linear-gradient(top, #5bc0de, #2f96b4);background-image:-o-linear-gradient(top, #5bc0de, #2f96b4);background-image:-moz-linear-gradient(top, #5bc0de, #2f96b4);background-image:linear-gradient(top, #5bc0de, #2f96b4);background-repeat:repeat-x;border-color:#2f96b4 #2f96b4 #1f6377;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#5bc0de', endColorstr='#2f96b4', GradientType=0);filter:progid:dximagetransform.microsoft.gradient(enabled=false);}
+.btn-info:hover,.btn-info:active,.btn-info.active,.btn-info.disabled,.btn-info[disabled]{background-color:#2f96b4;*background-color:#2a85a0;}
+.btn-info:active,.btn-info.active{background-color:#24748c \9;}
+.btn-inverse{background-color:#414141;*background-color:#222222;background-image:-ms-linear-gradient(top, #555555, #222222);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#555555), to(#222222));background-image:-webkit-linear-gradient(top, #555555, #222222);background-image:-o-linear-gradient(top, #555555, #222222);background-image:-moz-linear-gradient(top, #555555, #222222);background-image:linear-gradient(top, #555555, #222222);background-repeat:repeat-x;border-color:#222222 #222222 #000000;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#555555', endColorstr='#222222', GradientType=0);filter:progid:dximagetransform.microsoft.gradient(enabled=false);}
+.btn-inverse:hover,.btn-inverse:active,.btn-inverse.active,.btn-inverse.disabled,.btn-inverse[disabled]{background-color:#222222;*background-color:#151515;}
+.btn-inverse:active,.btn-inverse.active{background-color:#080808 \9;}
+button.btn,input[type="submit"].btn{*padding-top:2px;*padding-bottom:2px;}
+button.btn::-moz-focus-inner,input[type="submit"].btn::-moz-focus-inner{padding:0;border:0;}
+button.btn.btn-large,input[type="submit"].btn.btn-large{*padding-top:7px;*padding-bottom:7px;}
+button.btn.btn-small,input[type="submit"].btn.btn-small{*padding-top:3px;*padding-bottom:3px;}
+button.btn.btn-mini,input[type="submit"].btn.btn-mini{*padding-top:1px;*padding-bottom:1px;}
+.btn-group{position:relative;*margin-left:.3em;*zoom:1;}
+.btn-group:before,.btn-group:after{display:table;content:"";}
+.btn-group:after{clear:both;}
+.btn-group:first-child{*margin-left:0;}
+.btn-group+.btn-group{margin-left:5px;}
+.btn-toolbar{margin-top:9px;margin-bottom:9px;}
+.btn-toolbar .btn-group{display:inline-block;*display:inline;*zoom:1;}
+.btn-group>.btn{position:relative;float:left;margin-left:-1px;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}
+.btn-group>.btn:first-child{margin-left:0;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-bottomleft:4px;-moz-border-radius-topleft:4px;}
+.btn-group>.btn:last-child,.btn-group>.dropdown-toggle{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-topright:4px;-moz-border-radius-bottomright:4px;}
+.btn-group>.btn.large:first-child{margin-left:0;-webkit-border-bottom-left-radius:6px;border-bottom-left-radius:6px;-webkit-border-top-left-radius:6px;border-top-left-radius:6px;-moz-border-radius-bottomleft:6px;-moz-border-radius-topleft:6px;}
+.btn-group>.btn.large:last-child,.btn-group>.large.dropdown-toggle{-webkit-border-top-right-radius:6px;border-top-right-radius:6px;-webkit-border-bottom-right-radius:6px;border-bottom-right-radius:6px;-moz-border-radius-topright:6px;-moz-border-radius-bottomright:6px;}
+.btn-group>.btn:hover,.btn-group>.btn:focus,.btn-group>.btn:active,.btn-group>.btn.active{z-index:2;}
+.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0;}
+.btn-group>.dropdown-toggle{*padding-top:4px;padding-right:8px;*padding-bottom:4px;padding-left:8px;-webkit-box-shadow:inset 1px 0 0 rgba(255, 255, 255, 0.125),inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 1px 0 0 rgba(255, 255, 255, 0.125),inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 1px 0 0 rgba(255, 255, 255, 0.125),inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);}
+.btn-group>.btn-mini.dropdown-toggle{padding-right:5px;padding-left:5px;}
+.btn-group>.btn-small.dropdown-toggle{*padding-top:4px;*padding-bottom:4px;}
+.btn-group>.btn-large.dropdown-toggle{padding-right:12px;padding-left:12px;}
+.btn-group.open .dropdown-toggle{background-image:none;-webkit-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);}
+.btn-group.open .btn.dropdown-toggle{background-color:#e6e6e6;}
+.btn-group.open .btn-primary.dropdown-toggle{background-color:#0055cc;}
+.btn-group.open .btn-warning.dropdown-toggle{background-color:#f89406;}
+.btn-group.open .btn-danger.dropdown-toggle{background-color:#bd362f;}
+.btn-group.open .btn-success.dropdown-toggle{background-color:#51a351;}
+.btn-group.open .btn-info.dropdown-toggle{background-color:#2f96b4;}
+.btn-group.open .btn-inverse.dropdown-toggle{background-color:#222222;}
+.btn .caret{margin-top:7px;margin-left:0;}
+.btn:hover .caret,.open.btn-group .caret{opacity:1;filter:alpha(opacity=100);}
+.btn-mini .caret{margin-top:5px;}
+.btn-small .caret{margin-top:6px;}
+.btn-large .caret{margin-top:6px;border-top-width:5px;border-right-width:5px;border-left-width:5px;}
+.dropup .btn-large .caret{border-top:0;border-bottom:5px solid #000000;}
+.btn-primary .caret,.btn-warning .caret,.btn-danger .caret,.btn-info .caret,.btn-success .caret,.btn-inverse .caret{border-top-color:#ffffff;border-bottom-color:#ffffff;opacity:0.75;filter:alpha(opacity=75);}
+.alert{padding:8px 35px 8px 14px;margin-bottom:18px;color:#c09853;text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);background-color:#fcf8e3;border:1px solid #fbeed5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}
+.alert-heading{color:inherit;}
+.alert .close{position:relative;top:-2px;right:-21px;line-height:18px;}
+.alert-success{color:#468847;background-color:#dff0d8;border-color:#d6e9c6;}
+.alert-danger,.alert-error{color:#b94a48;background-color:#f2dede;border-color:#eed3d7;}
+.alert-info{color:#3a87ad;background-color:#d9edf7;border-color:#bce8f1;}
+.alert-block{padding-top:14px;padding-bottom:14px;}
+.alert-block>p,.alert-block>ul{margin-bottom:0;}
+.alert-block p+p{margin-top:5px;}
+.nav{margin-bottom:18px;margin-left:0;list-style:none;}
+.nav>li>a{display:block;}
+.nav>li>a:hover{text-decoration:none;background-color:#eeeeee;}
+.nav>.pull-right{float:right;}
+.nav .nav-header{display:block;padding:3px 15px;font-size:11px;font-weight:bold;line-height:18px;color:#999999;text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);text-transform:uppercase;}
+.nav li+.nav-header{margin-top:9px;}
+.nav-list{padding-right:15px;padding-left:15px;margin-bottom:0;}
+.nav-list>li>a,.nav-list .nav-header{margin-right:-15px;margin-left:-15px;text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);}
+.nav-list>li>a{padding:3px 15px;}
+.nav-list>.active>a,.nav-list>.active>a:hover{color:#ffffff;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.2);background-color:#0088cc;}
+.nav-list [class^="icon-"]{margin-right:2px;}
+.nav-list .divider{*width:100%;height:1px;margin:8px 1px;*margin:-5px 0 5px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #ffffff;}
+.nav-tabs,.nav-pills{*zoom:1;}
+.nav-tabs:before,.nav-pills:before,.nav-tabs:after,.nav-pills:after{display:table;content:"";}
+.nav-tabs:after,.nav-pills:after{clear:both;}
+.nav-tabs>li,.nav-pills>li{float:left;}
+.nav-tabs>li>a,.nav-pills>li>a{padding-right:12px;padding-left:12px;margin-right:2px;line-height:14px;}
+.nav-tabs{border-bottom:1px solid #ddd;}
+.nav-tabs>li{margin-bottom:-1px;}
+.nav-tabs>li>a{padding-top:8px;padding-bottom:8px;line-height:18px;border:1px solid transparent;-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0;}
+.nav-tabs>li>a:hover{border-color:#eeeeee #eeeeee #dddddd;}
+.nav-tabs>.active>a,.nav-tabs>.active>a:hover{color:#555555;cursor:default;background-color:#ffffff;border:1px solid #ddd;border-bottom-color:transparent;}
+.nav-pills>li>a{padding-top:8px;padding-bottom:8px;margin-top:2px;margin-bottom:2px;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;}
+.nav-pills>.active>a,.nav-pills>.active>a:hover{color:#ffffff;background-color:#0088cc;}
+.nav-stacked>li{float:none;}
+.nav-stacked>li>a{margin-right:0;}
+.nav-tabs.nav-stacked{border-bottom:0;}
+.nav-tabs.nav-stacked>li>a{border:1px solid #ddd;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}
+.nav-tabs.nav-stacked>li:first-child>a{-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0;}
+.nav-tabs.nav-stacked>li:last-child>a{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px;}
+.nav-tabs.nav-stacked>li>a:hover{z-index:2;border-color:#ddd;}
+.nav-pills.nav-stacked>li>a{margin-bottom:3px;}
+.nav-pills.nav-stacked>li:last-child>a{margin-bottom:1px;}
+.nav-tabs .dropdown-menu{-webkit-border-radius:0 0 5px 5px;-moz-border-radius:0 0 5px 5px;border-radius:0 0 5px 5px;}
+.nav-pills .dropdown-menu{-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}
+.nav-tabs .dropdown-toggle .caret,.nav-pills .dropdown-toggle .caret{margin-top:6px;border-top-color:#0088cc;border-bottom-color:#0088cc;}
+.nav-tabs .dropdown-toggle:hover .caret,.nav-pills .dropdown-toggle:hover .caret{border-top-color:#005580;border-bottom-color:#005580;}
+.nav-tabs .active .dropdown-toggle .caret,.nav-pills .active .dropdown-toggle .caret{border-top-color:#333333;border-bottom-color:#333333;}
+.nav>.dropdown.active>a:hover{color:#000000;cursor:pointer;}
+.nav-tabs .open .dropdown-toggle,.nav-pills .open .dropdown-toggle,.nav>li.dropdown.open.active>a:hover{color:#ffffff;background-color:#999999;border-color:#999999;}
+.nav li.dropdown.open .caret,.nav li.dropdown.open.active .caret,.nav li.dropdown.open a:hover .caret{border-top-color:#ffffff;border-bottom-color:#ffffff;opacity:1;filter:alpha(opacity=100);}
+.tabs-stacked .open>a:hover{border-color:#999999;}
+.tabbable{*zoom:1;}
+.tabbable:before,.tabbable:after{display:table;content:"";}
+.tabbable:after{clear:both;}
+.tab-content{overflow:auto;}
+.tabs-below>.nav-tabs,.tabs-right>.nav-tabs,.tabs-left>.nav-tabs{border-bottom:0;}
+.tab-content>.tab-pane,.pill-content>.pill-pane{display:none;}
+.tab-content>.active,.pill-content>.active{display:block;}
+.tabs-below>.nav-tabs{border-top:1px solid #ddd;}
+.tabs-below>.nav-tabs>li{margin-top:-1px;margin-bottom:0;}
+.tabs-below>.nav-tabs>li>a{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px;}
+.tabs-below>.nav-tabs>li>a:hover{border-top-color:#ddd;border-bottom-color:transparent;}
+.tabs-below>.nav-tabs>.active>a,.tabs-below>.nav-tabs>.active>a:hover{border-color:transparent #ddd #ddd #ddd;}
+.tabs-left>.nav-tabs>li,.tabs-right>.nav-tabs>li{float:none;}
+.tabs-left>.nav-tabs>li>a,.tabs-right>.nav-tabs>li>a{min-width:74px;margin-right:0;margin-bottom:3px;}
+.tabs-left>.nav-tabs{float:left;margin-right:19px;border-right:1px solid #ddd;}
+.tabs-left>.nav-tabs>li>a{margin-right:-1px;-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px;}
+.tabs-left>.nav-tabs>li>a:hover{border-color:#eeeeee #dddddd #eeeeee #eeeeee;}
+.tabs-left>.nav-tabs .active>a,.tabs-left>.nav-tabs .active>a:hover{border-color:#ddd transparent #ddd #ddd;*border-right-color:#ffffff;}
+.tabs-right>.nav-tabs{float:right;margin-left:19px;border-left:1px solid #ddd;}
+.tabs-right>.nav-tabs>li>a{margin-left:-1px;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0;}
+.tabs-right>.nav-tabs>li>a:hover{border-color:#eeeeee #eeeeee #eeeeee #dddddd;}
+.tabs-right>.nav-tabs .active>a,.tabs-right>.nav-tabs .active>a:hover{border-color:#ddd #ddd #ddd transparent;*border-left-color:#ffffff;}
+.navbar{*position:relative;*z-index:2;margin-bottom:18px;overflow:visible;}
+.navbar-inner{min-height:40px;padding-right:20px;padding-left:20px;background-color:#2c2c2c;background-image:-moz-linear-gradient(top, #333333, #222222);background-image:-ms-linear-gradient(top, #333333, #222222);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#333333), to(#222222));background-image:-webkit-linear-gradient(top, #333333, #222222);background-image:-o-linear-gradient(top, #333333, #222222);background-image:linear-gradient(top, #333333, #222222);background-repeat:repeat-x;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#333333', endColorstr='#222222', GradientType=0);-webkit-box-shadow:0 1px 3px rgba(0, 0, 0, 0.25),inset 0 -1px 0 rgba(0, 0, 0, 0.1);-moz-box-shadow:0 1px 3px rgba(0, 0, 0, 0.25),inset 0 -1px 0 rgba(0, 0, 0, 0.1);box-shadow:0 1px 3px rgba(0, 0, 0, 0.25),inset 0 -1px 0 rgba(0, 0, 0, 0.1);}
+.navbar .container{width:auto;}
+.nav-collapse.collapse{height:auto;}
+.navbar{color:#999999;}
+.navbar .brand:hover{text-decoration:none;}
+.navbar .brand{display:block;float:left;padding:8px 20px 12px;margin-left:-20px;font-size:20px;font-weight:200;line-height:1;color:#999999;}
+.navbar .navbar-text{margin-bottom:0;line-height:40px;}
+.navbar .navbar-link{color:#999999;}
+.navbar .navbar-link:hover{color:#ffffff;}
+.navbar .btn,.navbar .btn-group{margin-top:5px;}
+.navbar .btn-group .btn{margin:0;}
+.navbar-form{margin-bottom:0;*zoom:1;}
+.navbar-form:before,.navbar-form:after{display:table;content:"";}
+.navbar-form:after{clear:both;}
+.navbar-form input,.navbar-form select,.navbar-form .radio,.navbar-form .checkbox{margin-top:5px;}
+.navbar-form input,.navbar-form select{display:inline-block;margin-bottom:0;}
+.navbar-form input[type="image"],.navbar-form input[type="checkbox"],.navbar-form input[type="radio"]{margin-top:3px;}
+.navbar-form .input-append,.navbar-form .input-prepend{margin-top:6px;white-space:nowrap;}
+.navbar-form .input-append input,.navbar-form .input-prepend input{margin-top:0;}
+.navbar-search{position:relative;float:left;margin-top:6px;margin-bottom:0;}
+.navbar-search .search-query{padding:4px 9px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;font-weight:normal;line-height:1;color:#ffffff;background-color:#626262;border:1px solid #151515;-webkit-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1),0 1px 0 rgba(255, 255, 255, 0.15);-moz-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1),0 1px 0 rgba(255, 255, 255, 0.15);box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1),0 1px 0 rgba(255, 255, 255, 0.15);-webkit-transition:none;-moz-transition:none;-ms-transition:none;-o-transition:none;transition:none;}
+.navbar-search .search-query:-moz-placeholder{color:#cccccc;}
+.navbar-search .search-query::-webkit-input-placeholder{color:#cccccc;}
+.navbar-search .search-query:focus,.navbar-search .search-query.focused{padding:5px 10px;color:#333333;text-shadow:0 1px 0 #ffffff;background-color:#ffffff;border:0;outline:0;-webkit-box-shadow:0 0 3px rgba(0, 0, 0, 0.15);-moz-box-shadow:0 0 3px rgba(0, 0, 0, 0.15);box-shadow:0 0 3px rgba(0, 0, 0, 0.15);}
+.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030;margin-bottom:0;}
+.navbar-fixed-top .navbar-inner,.navbar-fixed-bottom .navbar-inner{padding-right:0;padding-left:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}
+.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px;}
+.navbar-fixed-top{top:0;}
+.navbar-fixed-bottom{bottom:0;}
+.navbar .nav{position:relative;left:0;display:block;float:left;margin:0 10px 0 0;}
+.navbar .nav.pull-right{float:right;}
+.navbar .nav>li{display:block;float:left;}
+.navbar .nav>li>a{float:none;padding:9px 10px 11px;line-height:19px;color:#999999;text-decoration:none;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);}
+.navbar .btn{display:inline-block;padding:4px 10px 4px;margin:5px 5px 6px;line-height:18px;}
+.navbar .btn-group{padding:5px 5px 6px;margin:0;}
+.navbar .nav>li>a:hover{color:#ffffff;text-decoration:none;background-color:transparent;}
+.navbar .nav .active>a,.navbar .nav .active>a:hover{color:#ffffff;text-decoration:none;background-color:#222222;}
+.navbar .divider-vertical{width:1px;height:40px;margin:0 9px;overflow:hidden;background-color:#222222;border-right:1px solid #333333;}
+.navbar .nav.pull-right{margin-right:0;margin-left:10px;}
+.navbar .btn-navbar{display:none;float:right;padding:7px 10px;margin-right:5px;margin-left:5px;background-color:#2c2c2c;*background-color:#222222;background-image:-ms-linear-gradient(top, #333333, #222222);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#333333), to(#222222));background-image:-webkit-linear-gradient(top, #333333, #222222);background-image:-o-linear-gradient(top, #333333, #222222);background-image:linear-gradient(top, #333333, #222222);background-image:-moz-linear-gradient(top, #333333, #222222);background-repeat:repeat-x;border-color:#222222 #222222 #000000;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#333333', endColorstr='#222222', GradientType=0);filter:progid:dximagetransform.microsoft.gradient(enabled=false);-webkit-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.075);-moz-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.075);box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.075);}
+.navbar .btn-navbar:hover,.navbar .btn-navbar:active,.navbar .btn-navbar.active,.navbar .btn-navbar.disabled,.navbar .btn-navbar[disabled]{background-color:#222222;*background-color:#151515;}
+.navbar .btn-navbar:active,.navbar .btn-navbar.active{background-color:#080808 \9;}
+.navbar .btn-navbar .icon-bar{display:block;width:18px;height:2px;background-color:#f5f5f5;-webkit-border-radius:1px;-moz-border-radius:1px;border-radius:1px;-webkit-box-shadow:0 1px 0 rgba(0, 0, 0, 0.25);-moz-box-shadow:0 1px 0 rgba(0, 0, 0, 0.25);box-shadow:0 1px 0 rgba(0, 0, 0, 0.25);}
+.btn-navbar .icon-bar+.icon-bar{margin-top:3px;}
+.navbar .dropdown-menu:before{position:absolute;top:-7px;left:9px;display:inline-block;border-right:7px solid transparent;border-bottom:7px solid #ccc;border-left:7px solid transparent;border-bottom-color:rgba(0, 0, 0, 0.2);content:'';}
+.navbar .dropdown-menu:after{position:absolute;top:-6px;left:10px;display:inline-block;border-right:6px solid transparent;border-bottom:6px solid #ffffff;border-left:6px solid transparent;content:'';}
+.navbar-fixed-bottom .dropdown-menu:before{top:auto;bottom:-7px;border-top:7px solid #ccc;border-bottom:0;border-top-color:rgba(0, 0, 0, 0.2);}
+.navbar-fixed-bottom .dropdown-menu:after{top:auto;bottom:-6px;border-top:6px solid #ffffff;border-bottom:0;}
+.navbar .nav li.dropdown .dropdown-toggle .caret,.navbar .nav li.dropdown.open .caret{border-top-color:#ffffff;border-bottom-color:#ffffff;}
+.navbar .nav li.dropdown.active .caret{opacity:1;filter:alpha(opacity=100);}
+.navbar .nav li.dropdown.open>.dropdown-toggle,.navbar .nav li.dropdown.active>.dropdown-toggle,.navbar .nav li.dropdown.open.active>.dropdown-toggle{background-color:transparent;}
+.navbar .nav li.dropdown.active>.dropdown-toggle:hover{color:#ffffff;}
+.navbar .pull-right .dropdown-menu,.navbar .dropdown-menu.pull-right{right:0;left:auto;}
+.navbar .pull-right .dropdown-menu:before,.navbar .dropdown-menu.pull-right:before{right:12px;left:auto;}
+.navbar .pull-right .dropdown-menu:after,.navbar .dropdown-menu.pull-right:after{right:13px;left:auto;}
+.breadcrumb{padding:7px 14px;margin:0 0 18px;list-style:none;background-color:#fbfbfb;background-image:-moz-linear-gradient(top, #ffffff, #f5f5f5);background-image:-ms-linear-gradient(top, #ffffff, #f5f5f5);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#f5f5f5));background-image:-webkit-linear-gradient(top, #ffffff, #f5f5f5);background-image:-o-linear-gradient(top, #ffffff, #f5f5f5);background-image:linear-gradient(top, #ffffff, #f5f5f5);background-repeat:repeat-x;border:1px solid #ddd;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ffffff', endColorstr='#f5f5f5', GradientType=0);-webkit-box-shadow:inset 0 1px 0 #ffffff;-moz-box-shadow:inset 0 1px 0 #ffffff;box-shadow:inset 0 1px 0 #ffffff;}
+.breadcrumb li{display:inline-block;*display:inline;text-shadow:0 1px 0 #ffffff;*zoom:1;}
+.breadcrumb .divider{padding:0 5px;color:#999999;}
+.breadcrumb .active a{color:#333333;}
+.pagination{height:36px;margin:18px 0;}
+.pagination ul{display:inline-block;*display:inline;margin-bottom:0;margin-left:0;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;*zoom:1;-webkit-box-shadow:0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:0 1px 2px rgba(0, 0, 0, 0.05);}
+.pagination li{display:inline;}
+.pagination a{float:left;padding:0 14px;line-height:34px;text-decoration:none;border:1px solid #ddd;border-left-width:0;}
+.pagination a:hover,.pagination .active a{background-color:#f5f5f5;}
+.pagination .active a{color:#999999;cursor:default;}
+.pagination .disabled span,.pagination .disabled a,.pagination .disabled a:hover{color:#999999;cursor:default;background-color:transparent;}
+.pagination li:first-child a{border-left-width:1px;-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;}
+.pagination li:last-child a{-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0;}
+.pagination-centered{text-align:center;}
+.pagination-right{text-align:right;}
+.pager{margin-bottom:18px;margin-left:0;text-align:center;list-style:none;*zoom:1;}
+.pager:before,.pager:after{display:table;content:"";}
+.pager:after{clear:both;}
+.pager li{display:inline;}
+.pager a{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px;}
+.pager a:hover{text-decoration:none;background-color:#f5f5f5;}
+.pager .next a{float:right;}
+.pager .previous a{float:left;}
+.pager .disabled a,.pager .disabled a:hover{color:#999999;cursor:default;background-color:#fff;}
+.modal-open .dropdown-menu{z-index:2050;}
+.modal-open .dropdown.open{*z-index:2050;}
+.modal-open .popover{z-index:2060;}
+.modal-open .tooltip{z-index:2070;}
+.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000000;}
+.modal-backdrop.fade{opacity:0;}
+.modal-backdrop,.modal-backdrop.fade.in{opacity:0.8;filter:alpha(opacity=80);}
+.modal{position:fixed;top:50%;left:50%;z-index:1050;width:560px;margin:-250px 0 0 -280px;overflow:auto;background-color:#ffffff;border:1px solid #999;border:1px solid rgba(0, 0, 0, 0.3);*border:1px solid #999;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);-moz-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box;}
+.modal.fade{top:-25%;-webkit-transition:opacity 0.3s linear,top 0.3s ease-out;-moz-transition:opacity 0.3s linear,top 0.3s ease-out;-ms-transition:opacity 0.3s linear,top 0.3s ease-out;-o-transition:opacity 0.3s linear,top 0.3s ease-out;transition:opacity 0.3s linear,top 0.3s ease-out;}
+.modal.fade.in{top:50%;}
+.modal-header{padding:9px 15px;border-bottom:1px solid #eee;}
+.modal-header .close{margin-top:2px;}
+.modal-body{max-height:400px;padding:15px;overflow-y:auto;}
+.modal-form{margin-bottom:0;}
+.modal-footer{padding:14px 15px 15px;margin-bottom:0;text-align:right;background-color:#f5f5f5;border-top:1px solid #ddd;-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px;*zoom:1;-webkit-box-shadow:inset 0 1px 0 #ffffff;-moz-box-shadow:inset 0 1px 0 #ffffff;box-shadow:inset 0 1px 0 #ffffff;}
+.modal-footer:before,.modal-footer:after{display:table;content:"";}
+.modal-footer:after{clear:both;}
+.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px;}
+.modal-footer .btn-group .btn+.btn{margin-left:-1px;}
+.tooltip{position:absolute;z-index:1020;display:block;padding:5px;font-size:11px;opacity:0;filter:alpha(opacity=0);visibility:visible;}
+.tooltip.in{opacity:0.8;filter:alpha(opacity=80);}
+.tooltip.top{margin-top:-2px;}
+.tooltip.right{margin-left:2px;}
+.tooltip.bottom{margin-top:2px;}
+.tooltip.left{margin-left:-2px;}
+.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-top:5px solid #000000;border-right:5px solid transparent;border-left:5px solid transparent;}
+.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-left:5px solid #000000;}
+.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-right:5px solid transparent;border-bottom:5px solid #000000;border-left:5px solid transparent;}
+.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-top:5px solid transparent;border-right:5px solid #000000;border-bottom:5px solid transparent;}
+.tooltip-inner{max-width:200px;padding:3px 8px;color:#ffffff;text-align:center;text-decoration:none;background-color:#000000;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}
+.tooltip-arrow{position:absolute;width:0;height:0;}
+.popover{position:absolute;top:0;left:0;z-index:1010;display:none;padding:5px;}
+.popover.top{margin-top:-5px;}
+.popover.right{margin-left:5px;}
+.popover.bottom{margin-top:5px;}
+.popover.left{margin-left:-5px;}
+.popover.top .arrow{bottom:0;left:50%;margin-left:-5px;border-top:5px solid #000000;border-right:5px solid transparent;border-left:5px solid transparent;}
+.popover.right .arrow{top:50%;left:0;margin-top:-5px;border-top:5px solid transparent;border-right:5px solid #000000;border-bottom:5px solid transparent;}
+.popover.bottom .arrow{top:0;left:50%;margin-left:-5px;border-right:5px solid transparent;border-bottom:5px solid #000000;border-left:5px solid transparent;}
+.popover.left .arrow{top:50%;right:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-left:5px solid #000000;}
+.popover .arrow{position:absolute;width:0;height:0;}
+.popover-inner{width:280px;padding:3px;overflow:hidden;background:#000000;background:rgba(0, 0, 0, 0.8);-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);-moz-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);}
+.popover-title{padding:9px 15px;line-height:1;background-color:#f5f5f5;border-bottom:1px solid #eee;-webkit-border-radius:3px 3px 0 0;-moz-border-radius:3px 3px 0 0;border-radius:3px 3px 0 0;}
+.popover-content{padding:14px;background-color:#ffffff;-webkit-border-radius:0 0 3px 3px;-moz-border-radius:0 0 3px 3px;border-radius:0 0 3px 3px;-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box;}
+.popover-content p,.popover-content ul,.popover-content ol{margin-bottom:0;}
+.thumbnails{margin-left:-20px;list-style:none;*zoom:1;}
+.thumbnails:before,.thumbnails:after{display:table;content:"";}
+.thumbnails:after{clear:both;}
+.row-fluid .thumbnails{margin-left:0;}
+.thumbnails>li{margin-bottom:18px;}
+.thumbnail{display:block;padding:4px;line-height:1;border:1px solid #ddd;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0, 0, 0, 0.075);-moz-box-shadow:0 1px 1px rgba(0, 0, 0, 0.075);box-shadow:0 1px 1px rgba(0, 0, 0, 0.075);}
+a.thumbnail:hover{border-color:#0088cc;-webkit-box-shadow:0 1px 4px rgba(0, 105, 214, 0.25);-moz-box-shadow:0 1px 4px rgba(0, 105, 214, 0.25);box-shadow:0 1px 4px rgba(0, 105, 214, 0.25);}
+.thumbnail>img{display:block;max-width:100%;margin-right:auto;margin-left:auto;}
+.thumbnail .caption{padding:9px;}
+.label,.badge{font-size:10.998px;font-weight:bold;line-height:14px;color:#ffffff;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);white-space:nowrap;vertical-align:baseline;background-color:#999999;}
+.label{padding:1px 4px 2px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;}
+.badge{padding:1px 9px 2px;-webkit-border-radius:9px;-moz-border-radius:9px;border-radius:9px;}
+a.label:hover,a.badge:hover{color:#ffffff;text-decoration:none;cursor:pointer;}
+.label-important,.badge-important{background-color:#b94a48;}
+.label-important[href],.badge-important[href]{background-color:#953b39;}
+.label-warning,.badge-warning{background-color:#f89406;}
+.label-warning[href],.badge-warning[href]{background-color:#c67605;}
+.label-success,.badge-success{background-color:#468847;}
+.label-success[href],.badge-success[href]{background-color:#356635;}
+.label-info,.badge-info{background-color:#3a87ad;}
+.label-info[href],.badge-info[href]{background-color:#2d6987;}
+.label-inverse,.badge-inverse{background-color:#333333;}
+.label-inverse[href],.badge-inverse[href]{background-color:#1a1a1a;}
+@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0;} to{background-position:0 0;}}@-moz-keyframes progress-bar-stripes{from{background-position:40px 0;} to{background-position:0 0;}}@-ms-keyframes progress-bar-stripes{from{background-position:40px 0;} to{background-position:0 0;}}@-o-keyframes progress-bar-stripes{from{background-position:0 0;} to{background-position:40px 0;}}@keyframes progress-bar-stripes{from{background-position:40px 0;} to{background-position:0 0;}}.progress{height:18px;margin-bottom:18px;overflow:hidden;background-color:#f7f7f7;background-image:-moz-linear-gradient(top, #f5f5f5, #f9f9f9);background-image:-ms-linear-gradient(top, #f5f5f5, #f9f9f9);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#f5f5f5), to(#f9f9f9));background-image:-webkit-linear-gradient(top, #f5f5f5, #f9f9f9);background-image:-o-linear-gradient(top, #f5f5f5, #f9f9f9);background-image:linear-gradient(top, #f5f5f5, #f9f9f9);background-repeat:repeat-x;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#f5f5f5', endColorstr='#f9f9f9', GradientType=0);-webkit-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1);-moz-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1);box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1);}
+.progress .bar{width:0;height:18px;font-size:12px;color:#ffffff;text-align:center;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);background-color:#0e90d2;background-image:-moz-linear-gradient(top, #149bdf, #0480be);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#149bdf), to(#0480be));background-image:-webkit-linear-gradient(top, #149bdf, #0480be);background-image:-o-linear-gradient(top, #149bdf, #0480be);background-image:linear-gradient(top, #149bdf, #0480be);background-image:-ms-linear-gradient(top, #149bdf, #0480be);background-repeat:repeat-x;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#149bdf', endColorstr='#0480be', GradientType=0);-webkit-box-shadow:inset 0 -1px 0 rgba(0, 0, 0, 0.15);-moz-box-shadow:inset 0 -1px 0 rgba(0, 0, 0, 0.15);box-shadow:inset 0 -1px 0 rgba(0, 0, 0, 0.15);-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;-webkit-transition:width 0.6s ease;-moz-transition:width 0.6s ease;-ms-transition:width 0.6s ease;-o-transition:width 0.6s ease;transition:width 0.6s ease;}
+.progress-striped .bar{background-color:#149bdf;background-image:-o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);-webkit-background-size:40px 40px;-moz-background-size:40px 40px;-o-background-size:40px 40px;background-size:40px 40px;}
+.progress.active .bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-moz-animation:progress-bar-stripes 2s linear infinite;-ms-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite;}
+.progress-danger .bar{background-color:#dd514c;background-image:-moz-linear-gradient(top, #ee5f5b, #c43c35);background-image:-ms-linear-gradient(top, #ee5f5b, #c43c35);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#c43c35));background-image:-webkit-linear-gradient(top, #ee5f5b, #c43c35);background-image:-o-linear-gradient(top, #ee5f5b, #c43c35);background-image:linear-gradient(top, #ee5f5b, #c43c35);background-repeat:repeat-x;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ee5f5b', endColorstr='#c43c35', GradientType=0);}
+.progress-danger.progress-striped .bar{background-color:#ee5f5b;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);}
+.progress-success .bar{background-color:#5eb95e;background-image:-moz-linear-gradient(top, #62c462, #57a957);background-image:-ms-linear-gradient(top, #62c462, #57a957);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#57a957));background-image:-webkit-linear-gradient(top, #62c462, #57a957);background-image:-o-linear-gradient(top, #62c462, #57a957);background-image:linear-gradient(top, #62c462, #57a957);background-repeat:repeat-x;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#62c462', endColorstr='#57a957', GradientType=0);}
+.progress-success.progress-striped .bar{background-color:#62c462;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);}
+.progress-info .bar{background-color:#4bb1cf;background-image:-moz-linear-gradient(top, #5bc0de, #339bb9);background-image:-ms-linear-gradient(top, #5bc0de, #339bb9);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#339bb9));background-image:-webkit-linear-gradient(top, #5bc0de, #339bb9);background-image:-o-linear-gradient(top, #5bc0de, #339bb9);background-image:linear-gradient(top, #5bc0de, #339bb9);background-repeat:repeat-x;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#5bc0de', endColorstr='#339bb9', GradientType=0);}
+.progress-info.progress-striped .bar{background-color:#5bc0de;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);}
+.progress-warning .bar{background-color:#faa732;background-image:-moz-linear-gradient(top, #fbb450, #f89406);background-image:-ms-linear-gradient(top, #fbb450, #f89406);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406));background-image:-webkit-linear-gradient(top, #fbb450, #f89406);background-image:-o-linear-gradient(top, #fbb450, #f89406);background-image:linear-gradient(top, #fbb450, #f89406);background-repeat:repeat-x;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#fbb450', endColorstr='#f89406', GradientType=0);}
+.progress-warning.progress-striped .bar{background-color:#fbb450;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);}
+.accordion{margin-bottom:18px;}
+.accordion-group{margin-bottom:2px;border:1px solid #e5e5e5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}
+.accordion-heading{border-bottom:0;}
+.accordion-heading .accordion-toggle{display:block;padding:8px 15px;}
+.accordion-toggle{cursor:pointer;}
+.accordion-inner{padding:9px 15px;border-top:1px solid #e5e5e5;}
+.carousel{position:relative;margin-bottom:18px;line-height:1;}
+.carousel-inner{position:relative;width:100%;overflow:hidden;}
+.carousel .item{position:relative;display:none;-webkit-transition:0.6s ease-in-out left;-moz-transition:0.6s ease-in-out left;-ms-transition:0.6s ease-in-out left;-o-transition:0.6s ease-in-out left;transition:0.6s ease-in-out left;}
+.carousel .item>img{display:block;line-height:1;}
+.carousel .active,.carousel .next,.carousel .prev{display:block;}
+.carousel .active{left:0;}
+.carousel .next,.carousel .prev{position:absolute;top:0;width:100%;}
+.carousel .next{left:100%;}
+.carousel .prev{left:-100%;}
+.carousel .next.left,.carousel .prev.right{left:0;}
+.carousel .active.left{left:-100%;}
+.carousel .active.right{left:100%;}
+.carousel-control{position:absolute;top:40%;left:15px;width:40px;height:40px;margin-top:-20px;font-size:60px;font-weight:100;line-height:30px;color:#ffffff;text-align:center;background:#222222;border:3px solid #ffffff;-webkit-border-radius:23px;-moz-border-radius:23px;border-radius:23px;opacity:0.5;filter:alpha(opacity=50);}
+.carousel-control.right{right:15px;left:auto;}
+.carousel-control:hover{color:#ffffff;text-decoration:none;opacity:0.9;filter:alpha(opacity=90);}
+.carousel-caption{position:absolute;right:0;bottom:0;left:0;padding:10px 15px 5px;background:#333333;background:rgba(0, 0, 0, 0.75);}
+.carousel-caption h4,.carousel-caption p{color:#ffffff;}
+.hero-unit{padding:60px;margin-bottom:30px;background-color:#eeeeee;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;}
+.hero-unit h1{margin-bottom:0;font-size:60px;line-height:1;letter-spacing:-1px;color:inherit;}
+.hero-unit p{font-size:18px;font-weight:200;line-height:27px;color:inherit;}
+.pull-right{float:right;}
+.pull-left{float:left;}
+.hide{display:none;}
+.show{display:block;}
+.invisible{visibility:hidden;}
diff --git a/src/fileupload/static/fileupload/css/jquery.fileupload-ui.css b/src/fileupload/static/fileupload/css/jquery.fileupload-ui.css
new file mode 100644 (file)
index 0000000..e36a93d
--- /dev/null
@@ -0,0 +1,84 @@
+@charset 'UTF-8';
+/*
+ * jQuery File Upload UI Plugin CSS 6.3
+ * https://github.com/blueimp/jQuery-File-Upload
+ *
+ * Copyright 2010, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
+
+.fileinput-button {
+  position: relative;
+  overflow: hidden;
+  float: left;
+  margin-right: 4px;
+}
+.fileinput-button input {
+  position: absolute;
+  top: 0;
+  right: 0;
+  margin: 0;
+  border: solid transparent;
+  border-width: 0 0 100px 200px;
+  opacity: 0;
+  filter: alpha(opacity=0);
+  -moz-transform: translate(-300px, 0) scale(4);
+  direction: ltr;
+  cursor: pointer;
+}
+.fileupload-buttonbar .btn,
+.fileupload-buttonbar .toggle {
+  margin-bottom: 5px;
+}
+.files .progress {
+  width: 200px;
+}
+.progress-animated .bar {
+  background: url(../img/progressbar.gif) !important;
+  filter: none;
+}
+.fileupload-loading {
+  position: absolute;
+  left: 50%;
+  width: 128px;
+  height: 128px;
+  background: url(../img/loading.gif) center no-repeat;
+  display: none;
+}
+.fileupload-processing .fileupload-loading {
+  display: block;
+}
+
+/* Fix for IE 6: */
+*html .fileinput-button {
+  line-height: 22px;
+  margin: 1px -3px 0 0;
+}
+
+/* Fix for IE 7: */
+*+html .fileinput-button {
+  margin: 1px 0 0 0;
+}
+
+@media (max-width: 480px) {
+  .files .btn span {
+    display: none;
+  }
+  .files .preview * {
+    width: 40px;
+  }
+  .files .name * {
+    width: 80px;
+    display: inline-block;
+    word-wrap: break-word;
+  }
+  .files .progress {
+    width: 20px;
+  }
+  .files .delete {
+    width: 60px;
+  }
+}
diff --git a/src/fileupload/static/fileupload/css/style.css b/src/fileupload/static/fileupload/css/style.css
new file mode 100644 (file)
index 0000000..e45d81d
--- /dev/null
@@ -0,0 +1,10 @@
+.preview img {
+    max-height: 50px;
+}
+
+.delete button[data-type=""] {
+    display: none;
+}
+.delete button[data-type=""] + input {
+    display: none;
+}
diff --git a/src/fileupload/static/fileupload/img/glyphicons-halflings-white.png b/src/fileupload/static/fileupload/img/glyphicons-halflings-white.png
new file mode 100644 (file)
index 0000000..3bf6484
Binary files /dev/null and b/src/fileupload/static/fileupload/img/glyphicons-halflings-white.png differ
diff --git a/src/fileupload/static/fileupload/img/glyphicons-halflings.png b/src/fileupload/static/fileupload/img/glyphicons-halflings.png
new file mode 100644 (file)
index 0000000..79bc568
Binary files /dev/null and b/src/fileupload/static/fileupload/img/glyphicons-halflings.png differ
diff --git a/src/fileupload/static/fileupload/img/loading.gif b/src/fileupload/static/fileupload/img/loading.gif
new file mode 100644 (file)
index 0000000..90f28cb
Binary files /dev/null and b/src/fileupload/static/fileupload/img/loading.gif differ
diff --git a/src/fileupload/static/fileupload/img/progressbar.gif b/src/fileupload/static/fileupload/img/progressbar.gif
new file mode 100644 (file)
index 0000000..fbcce6b
Binary files /dev/null and b/src/fileupload/static/fileupload/img/progressbar.gif differ
diff --git a/src/fileupload/static/fileupload/js/bootstrap-image-gallery.min.js b/src/fileupload/static/fileupload/js/bootstrap-image-gallery.min.js
new file mode 100644 (file)
index 0000000..a749f55
--- /dev/null
@@ -0,0 +1 @@
+(function(a){"use strict",typeof define=="function"&&define.amd?define(["jquery","load-image","bootstrap"],a):a(window.jQuery,window.loadImage)})(function(a,b){"use strict",a.extend(a.fn.modal.defaults,{delegate:document,selector:null,filter:"*",index:0,href:null,preloadRange:2,offsetWidth:100,offsetHeight:200,canvas:!1,slideshow:0,imageClickDivision:.5});var c=a.fn.modal.Constructor.prototype.show,d=a.fn.modal.Constructor.prototype.hide;a.extend(a.fn.modal.Constructor.prototype,{initLinks:function(){var b=this,c=this.options,d=c.selector||"a[data-target="+c.target+"]";this.$links=a(c.delegate).find(d).filter(c.filter).each(function(a){b.getUrl(this)===c.href&&(c.index=a)}),this.$links[c.index]||(c.index=0)},getUrl:function(b){return b.href||a(b).data("href")},startSlideShow:function(){var a=this;this.options.slideshow&&(this._slideShow=window.setTimeout(function(){a.next()},this.options.slideshow))},stopSlideShow:function(){window.clearTimeout(this._slideShow)},toggleSlideShow:function(){var a=this.$element.find(".modal-slideshow");this.options.slideshow?(this.options.slideshow=0,this.stopSlideShow()):(this.options.slideshow=a.data("slideshow")||5e3,this.startSlideShow()),a.find("i").toggleClass("icon-play icon-pause")},preloadImages:function(){var b=this.options,c=b.index+b.preloadRange+1,d,e;for(e=b.index-b.preloadRange;e<c;e+=1)d=this.$links[e],d&&e!==b.index&&a("<img>").prop("src",this.getUrl(d))},loadImage:function(){var a=this,c=this.$element,d=this.options.index,e=this.getUrl(this.$links[d]),f;this.abortLoad(),this.stopSlideShow(),c.trigger("beforeLoad"),this._loadingTimeout=window.setTimeout(function(){c.addClass("modal-loading")},100),f=c.find(".modal-image").children().removeClass("in"),window.setTimeout(function(){f.remove()},3e3),c.find(".modal-title").text(this.$links[d].title),c.find(".modal-download").prop("href",e),this._loadingImage=b(e,function(b){a.img=b,window.clearTimeout(a._loadingTimeout),c.removeClass("modal-loading"),c.trigger("load"),a.showImage(b),a.startSlideShow()},this._loadImageOptions),this.preloadImages()},showImage:function(b){var c=this.$element,d=a.support.transition&&c.hasClass("fade"),e=d?c.animate:c.css,f=c.find(".modal-image"),g,h;f.css({width:b.width,height:b.height}),c.find(".modal-title").css({width:Math.max(b.width,380)}),a(window).width()>480&&(d&&(g=c.clone().hide().appendTo(document.body)),e.call(c.stop(),{"margin-top":-((g||c).outerHeight()/2),"margin-left":-((g||c).outerWidth()/2)}),g&&g.remove()),f.append(b),h=b.offsetWidth,c.trigger("display"),d?c.is(":visible")?a(b).on(a.support.transition.end,function(d){d.target===b&&(a(b).off(a.support.transition.end),c.trigger("displayed"))}).addClass("in"):(a(b).addClass("in"),c.one("shown",function(){c.trigger("displayed")})):(a(b).addClass("in"),c.trigger("displayed"))},abortLoad:function(){this._loadingImage&&(this._loadingImage.onload=this._loadingImage.onerror=null),window.clearTimeout(this._loadingTimeout)},prev:function(){var a=this.options;a.index-=1,a.index<0&&(a.index=this.$links.length-1),this.loadImage()},next:function(){var a=this.options;a.index+=1,a.index>this.$links.length-1&&(a.index=0),this.loadImage()},keyHandler:function(a){switch(a.which){case 37:case 38:a.preventDefault(),this.prev();break;case 39:case 40:a.preventDefault(),this.next()}},wheelHandler:function(a){a.preventDefault(),a=a.originalEvent,this._wheelCounter=this._wheelCounter||0,this._wheelCounter+=a.wheelDelta||a.detail||0;if(a.wheelDelta&&this._wheelCounter>=120||!a.wheelDelta&&this._wheelCounter<0)this.prev(),this._wheelCounter=0;else if(a.wheelDelta&&this._wheelCounter<=-120||!a.wheelDelta&&this._wheelCounter>0)this.next(),this._wheelCounter=0},initGalleryEvents:function(){var b=this,c=this.$element;c.find(".modal-image").on("click.modal-gallery",function(c){var d=a(this);b.$links.length===1?b.hide():(c.pageX-d.offset().left)/d.width()<b.options.imageClickDivision?b.prev(c):b.next(c)}),c.find(".modal-prev").on("click.modal-gallery",function(a){b.prev(a)}),c.find(".modal-next").on("click.modal-gallery",function(a){b.next(a)}),c.find(".modal-slideshow").on("click.modal-gallery",function(a){b.toggleSlideShow(a)}),a(document).on("keydown.modal-gallery",function(a){b.keyHandler(a)}).on("mousewheel.modal-gallery, DOMMouseScroll.modal-gallery",function(a){b.wheelHandler(a)})},destroyGalleryEvents:function(){var b=this.$element;this.abortLoad(),this.stopSlideShow(),b.find(".modal-image, .modal-prev, .modal-next, .modal-slideshow").off("click.modal-gallery"),a(document).off("keydown.modal-gallery").off("mousewheel.modal-gallery, DOMMouseScroll.modal-gallery")},show:function(){if(!this.isShown&&this.$element.hasClass("modal-gallery")){var b=this.$element,d=this.options,e=a(window).width(),f=a(window).height();b.hasClass("modal-fullscreen")?(this._loadImageOptions={maxWidth:e,maxHeight:f,canvas:d.canvas},b.hasClass("modal-fullscreen-stretch")&&(this._loadImageOptions.minWidth=e,this._loadImageOptions.minHeight=f)):this._loadImageOptions={maxWidth:e-d.offsetWidth,maxHeight:f-d.offsetHeight,canvas:d.canvas},e>480&&b.css({"margin-top":-(b.outerHeight()/2),"margin-left":-(b.outerWidth()/2)}),this.initGalleryEvents(),this.initLinks(),this.$links.length&&(b.find(".modal-slideshow, .modal-prev, .modal-next").toggle(this.$links.length!==1),b.toggleClass("modal-single",this.$links.length===1),this.loadImage())}c.apply(this,arguments)},hide:function(){this.isShown&&this.$element.hasClass("modal-gallery")&&(this.options.delegate=document,this.options.href=null,this.destroyGalleryEvents()),d.apply(this,arguments)}}),a(function(){a(document.body).on("click.modal-gallery.data-api",'[data-toggle="modal-gallery"]',function(b){var c=a(this),d=c.data(),e=a(d.target),f=e.data("modal"),g;f||(d=a.extend(e.data(),d)),d.selector||(d.selector="a[rel=gallery]"),g=a(b.target).closest(d.selector),g.length&&e.length&&(b.preventDefault(),d.href=g.prop("href")||g.data("href"),d.delegate=g[0]!==this?this:document,f&&a.extend(f.options,d),e.modal(d))})})});
diff --git a/src/fileupload/static/fileupload/js/bootstrap.min.js b/src/fileupload/static/fileupload/js/bootstrap.min.js
new file mode 100644 (file)
index 0000000..fcfb38b
--- /dev/null
@@ -0,0 +1,6 @@
+/**
+* Bootstrap.js by @fat & @mdo
+* Copyright 2012 Twitter, Inc.
+* http://www.apache.org/licenses/LICENSE-2.0.txt
+*/
+!function(a){a(function(){"use strict",a.support.transition=function(){var a=function(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd",msTransition:"MSTransitionEnd",transition:"transitionend"},c;for(c in b)if(a.style[c]!==undefined)return b[c]}();return a&&{end:a}}()})}(window.jQuery),!function(a){"use strict";var b='[data-dismiss="alert"]',c=function(c){a(c).on("click",b,this.close)};c.prototype.close=function(b){function f(){e.trigger("closed").remove()}var c=a(this),d=c.attr("data-target"),e;d||(d=c.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,"")),e=a(d),b&&b.preventDefault(),e.length||(e=c.hasClass("alert")?c:c.parent()),e.trigger(b=a.Event("close"));if(b.isDefaultPrevented())return;e.removeClass("in"),a.support.transition&&e.hasClass("fade")?e.on(a.support.transition.end,f):f()},a.fn.alert=function(b){return this.each(function(){var d=a(this),e=d.data("alert");e||d.data("alert",e=new c(this)),typeof b=="string"&&e[b].call(d)})},a.fn.alert.Constructor=c,a(function(){a("body").on("click.alert.data-api",b,c.prototype.close)})}(window.jQuery),!function(a){"use strict";var b=function(b,c){this.$element=a(b),this.options=a.extend({},a.fn.button.defaults,c)};b.prototype.setState=function(a){var b="disabled",c=this.$element,d=c.data(),e=c.is("input")?"val":"html";a+="Text",d.resetText||c.data("resetText",c[e]()),c[e](d[a]||this.options[a]),setTimeout(function(){a=="loadingText"?c.addClass(b).attr(b,b):c.removeClass(b).removeAttr(b)},0)},b.prototype.toggle=function(){var a=this.$element.parent('[data-toggle="buttons-radio"]');a&&a.find(".active").removeClass("active"),this.$element.toggleClass("active")},a.fn.button=function(c){return this.each(function(){var d=a(this),e=d.data("button"),f=typeof c=="object"&&c;e||d.data("button",e=new b(this,f)),c=="toggle"?e.toggle():c&&e.setState(c)})},a.fn.button.defaults={loadingText:"loading..."},a.fn.button.Constructor=b,a(function(){a("body").on("click.button.data-api","[data-toggle^=button]",function(b){var c=a(b.target);c.hasClass("btn")||(c=c.closest(".btn")),c.button("toggle")})})}(window.jQuery),!function(a){"use strict";var b=function(b,c){this.$element=a(b),this.options=c,this.options.slide&&this.slide(this.options.slide),this.options.pause=="hover"&&this.$element.on("mouseenter",a.proxy(this.pause,this)).on("mouseleave",a.proxy(this.cycle,this))};b.prototype={cycle:function(b){return b||(this.paused=!1),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},to:function(b){var c=this.$element.find(".active"),d=c.parent().children(),e=d.index(c),f=this;if(b>d.length-1||b<0)return;return this.sliding?this.$element.one("slid",function(){f.to(b)}):e==b?this.pause().cycle():this.slide(b>e?"next":"prev",a(d[b]))},pause:function(a){return a||(this.paused=!0),clearInterval(this.interval),this.interval=null,this},next:function(){if(this.sliding)return;return this.slide("next")},prev:function(){if(this.sliding)return;return this.slide("prev")},slide:function(b,c){var d=this.$element.find(".active"),e=c||d[b](),f=this.interval,g=b=="next"?"left":"right",h=b=="next"?"first":"last",i=this,j=a.Event("slide");this.sliding=!0,f&&this.pause(),e=e.length?e:this.$element.find(".item")[h]();if(e.hasClass("active"))return;if(a.support.transition&&this.$element.hasClass("slide")){this.$element.trigger(j);if(j.isDefaultPrevented())return;e.addClass(b),e[0].offsetWidth,d.addClass(g),e.addClass(g),this.$element.one(a.support.transition.end,function(){e.removeClass([b,g].join(" ")).addClass("active"),d.removeClass(["active",g].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger("slid")},0)})}else{this.$element.trigger(j);if(j.isDefaultPrevented())return;d.removeClass("active"),e.addClass("active"),this.sliding=!1,this.$element.trigger("slid")}return f&&this.cycle(),this}},a.fn.carousel=function(c){return this.each(function(){var d=a(this),e=d.data("carousel"),f=a.extend({},a.fn.carousel.defaults,typeof c=="object"&&c);e||d.data("carousel",e=new b(this,f)),typeof c=="number"?e.to(c):typeof c=="string"||(c=f.slide)?e[c]():f.interval&&e.cycle()})},a.fn.carousel.defaults={interval:5e3,pause:"hover"},a.fn.carousel.Constructor=b,a(function(){a("body").on("click.carousel.data-api","[data-slide]",function(b){var c=a(this),d,e=a(c.attr("data-target")||(d=c.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,"")),f=!e.data("modal")&&a.extend({},e.data(),c.data());e.carousel(f),b.preventDefault()})})}(window.jQuery),!function(a){"use strict";var b=function(b,c){this.$element=a(b),this.options=a.extend({},a.fn.collapse.defaults,c),this.options.parent&&(this.$parent=a(this.options.parent)),this.options.toggle&&this.toggle()};b.prototype={constructor:b,dimension:function(){var a=this.$element.hasClass("width");return a?"width":"height"},show:function(){var b,c,d,e;if(this.transitioning)return;b=this.dimension(),c=a.camelCase(["scroll",b].join("-")),d=this.$parent&&this.$parent.find("> .accordion-group > .in");if(d&&d.length){e=d.data("collapse");if(e&&e.transitioning)return;d.collapse("hide"),e||d.data("collapse",null)}this.$element[b](0),this.transition("addClass",a.Event("show"),"shown"),this.$element[b](this.$element[0][c])},hide:function(){var b;if(this.transitioning)return;b=this.dimension(),this.reset(this.$element[b]()),this.transition("removeClass",a.Event("hide"),"hidden"),this.$element[b](0)},reset:function(a){var b=this.dimension();return this.$element.removeClass("collapse")[b](a||"auto")[0].offsetWidth,this.$element[a!==null?"addClass":"removeClass"]("collapse"),this},transition:function(b,c,d){var e=this,f=function(){c=="show"&&e.reset(),e.transitioning=0,e.$element.trigger(d)};this.$element.trigger(c);if(c.isDefaultPrevented())return;this.transitioning=1,this.$element[b]("in"),a.support.transition&&this.$element.hasClass("collapse")?this.$element.one(a.support.transition.end,f):f()},toggle:function(){this[this.$element.hasClass("in")?"hide":"show"]()}},a.fn.collapse=function(c){return this.each(function(){var d=a(this),e=d.data("collapse"),f=typeof c=="object"&&c;e||d.data("collapse",e=new b(this,f)),typeof c=="string"&&e[c]()})},a.fn.collapse.defaults={toggle:!0},a.fn.collapse.Constructor=b,a(function(){a("body").on("click.collapse.data-api","[data-toggle=collapse]",function(b){var c=a(this),d,e=c.attr("data-target")||b.preventDefault()||(d=c.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""),f=a(e).data("collapse")?"toggle":c.data();a(e).collapse(f)})})}(window.jQuery),!function(a){function d(){a(b).parent().removeClass("open")}"use strict";var b='[data-toggle="dropdown"]',c=function(b){var c=a(b).on("click.dropdown.data-api",this.toggle);a("html").on("click.dropdown.data-api",function(){c.parent().removeClass("open")})};c.prototype={constructor:c,toggle:function(b){var c=a(this),e,f,g;if(c.is(".disabled, :disabled"))return;return f=c.attr("data-target"),f||(f=c.attr("href"),f=f&&f.replace(/.*(?=#[^\s]*$)/,"")),e=a(f),e.length||(e=c.parent()),g=e.hasClass("open"),d(),g||e.toggleClass("open"),!1}},a.fn.dropdown=function(b){return this.each(function(){var d=a(this),e=d.data("dropdown");e||d.data("dropdown",e=new c(this)),typeof b=="string"&&e[b].call(d)})},a.fn.dropdown.Constructor=c,a(function(){a("html").on("click.dropdown.data-api",d),a("body").on("click.dropdown",".dropdown form",function(a){a.stopPropagation()}).on("click.dropdown.data-api",b,c.prototype.toggle)})}(window.jQuery),!function(a){function c(){var b=this,c=setTimeout(function(){b.$element.off(a.support.transition.end),d.call(b)},500);this.$element.one(a.support.transition.end,function(){clearTimeout(c),d.call(b)})}function d(a){this.$element.hide().trigger("hidden"),e.call(this)}function e(b){var c=this,d=this.$element.hasClass("fade")?"fade":"";if(this.isShown&&this.options.backdrop){var e=a.support.transition&&d;this.$backdrop=a('<div class="modal-backdrop '+d+'" />').appendTo(document.body),this.options.backdrop!="static"&&this.$backdrop.click(a.proxy(this.hide,this)),e&&this.$backdrop[0].offsetWidth,this.$backdrop.addClass("in"),e?this.$backdrop.one(a.support.transition.end,b):b()}else!this.isShown&&this.$backdrop?(this.$backdrop.removeClass("in"),a.support.transition&&this.$element.hasClass("fade")?this.$backdrop.one(a.support.transition.end,a.proxy(f,this)):f.call(this)):b&&b()}function f(){this.$backdrop.remove(),this.$backdrop=null}function g(){var b=this;this.isShown&&this.options.keyboard?a(document).on("keyup.dismiss.modal",function(a){a.which==27&&b.hide()}):this.isShown||a(document).off("keyup.dismiss.modal")}"use strict";var b=function(b,c){this.options=c,this.$element=a(b).delegate('[data-dismiss="modal"]',"click.dismiss.modal",a.proxy(this.hide,this))};b.prototype={constructor:b,toggle:function(){return this[this.isShown?"hide":"show"]()},show:function(){var b=this,c=a.Event("show");this.$element.trigger(c);if(this.isShown||c.isDefaultPrevented())return;a("body").addClass("modal-open"),this.isShown=!0,g.call(this),e.call(this,function(){var c=a.support.transition&&b.$element.hasClass("fade");b.$element.parent().length||b.$element.appendTo(document.body),b.$element.show(),c&&b.$element[0].offsetWidth,b.$element.addClass("in"),c?b.$element.one(a.support.transition.end,function(){b.$element.trigger("shown")}):b.$element.trigger("shown")})},hide:function(b){b&&b.preventDefault();var e=this;b=a.Event("hide"),this.$element.trigger(b);if(!this.isShown||b.isDefaultPrevented())return;this.isShown=!1,a("body").removeClass("modal-open"),g.call(this),this.$element.removeClass("in"),a.support.transition&&this.$element.hasClass("fade")?c.call(this):d.call(this)}},a.fn.modal=function(c){return this.each(function(){var d=a(this),e=d.data("modal"),f=a.extend({},a.fn.modal.defaults,d.data(),typeof c=="object"&&c);e||d.data("modal",e=new b(this,f)),typeof c=="string"?e[c]():f.show&&e.show()})},a.fn.modal.defaults={backdrop:!0,keyboard:!0,show:!0},a.fn.modal.Constructor=b,a(function(){a("body").on("click.modal.data-api",'[data-toggle="modal"]',function(b){var c=a(this),d,e=a(c.attr("data-target")||(d=c.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,"")),f=e.data("modal")?"toggle":a.extend({},e.data(),c.data());b.preventDefault(),e.modal(f)})})}(window.jQuery),!function(a){"use strict";var b=function(a,b){this.init("tooltip",a,b)};b.prototype={constructor:b,init:function(b,c,d){var e,f;this.type=b,this.$element=a(c),this.options=this.getOptions(d),this.enabled=!0,this.options.trigger!="manual"&&(e=this.options.trigger=="hover"?"mouseenter":"focus",f=this.options.trigger=="hover"?"mouseleave":"blur",this.$element.on(e,this.options.selector,a.proxy(this.enter,this)),this.$element.on(f,this.options.selector,a.proxy(this.leave,this))),this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},getOptions:function(b){return b=a.extend({},a.fn[this.type].defaults,b,this.$element.data()),b.delay&&typeof b.delay=="number"&&(b.delay={show:b.delay,hide:b.delay}),b},enter:function(b){var c=a(b.currentTarget)[this.type](this._options).data(this.type);if(!c.options.delay||!c.options.delay.show)return c.show();clearTimeout(this.timeout),c.hoverState="in",this.timeout=setTimeout(function(){c.hoverState=="in"&&c.show()},c.options.delay.show)},leave:function(b){var c=a(b.currentTarget)[this.type](this._options).data(this.type);if(!c.options.delay||!c.options.delay.hide)return c.hide();clearTimeout(this.timeout),c.hoverState="out",this.timeout=setTimeout(function(){c.hoverState=="out"&&c.hide()},c.options.delay.hide)},show:function(){var a,b,c,d,e,f,g;if(this.hasContent()&&this.enabled){a=this.tip(),this.setContent(),this.options.animation&&a.addClass("fade"),f=typeof this.options.placement=="function"?this.options.placement.call(this,a[0],this.$element[0]):this.options.placement,b=/in/.test(f),a.remove().css({top:0,left:0,display:"block"}).appendTo(b?this.$element:document.body),c=this.getPosition(b),d=a[0].offsetWidth,e=a[0].offsetHeight;switch(b?f.split(" ")[1]:f){case"bottom":g={top:c.top+c.height,left:c.left+c.width/2-d/2};break;case"top":g={top:c.top-e,left:c.left+c.width/2-d/2};break;case"left":g={top:c.top+c.height/2-e/2,left:c.left-d};break;case"right":g={top:c.top+c.height/2-e/2,left:c.left+c.width}}a.css(g).addClass(f).addClass("in")}},isHTML:function(a){return typeof a!="string"||a.charAt(0)==="<"&&a.charAt(a.length-1)===">"&&a.length>=3||/^(?:[^<]*<[\w\W]+>[^>]*$)/.exec(a)},setContent:function(){var a=this.tip(),b=this.getTitle();a.find(".tooltip-inner")[this.isHTML(b)?"html":"text"](b),a.removeClass("fade in top bottom left right")},hide:function(){function d(){var b=setTimeout(function(){c.off(a.support.transition.end).remove()},500);c.one(a.support.transition.end,function(){clearTimeout(b),c.remove()})}var b=this,c=this.tip();c.removeClass("in"),a.support.transition&&this.$tip.hasClass("fade")?d():c.remove()},fixTitle:function(){var a=this.$element;(a.attr("title")||typeof a.attr("data-original-title")!="string")&&a.attr("data-original-title",a.attr("title")||"").removeAttr("title")},hasContent:function(){return this.getTitle()},getPosition:function(b){return a.extend({},b?{top:0,left:0}:this.$element.offset(),{width:this.$element[0].offsetWidth,height:this.$element[0].offsetHeight})},getTitle:function(){var a,b=this.$element,c=this.options;return a=b.attr("data-original-title")||(typeof c.title=="function"?c.title.call(b[0]):c.title),a},tip:function(){return this.$tip=this.$tip||a(this.options.template)},validate:function(){this.$element[0].parentNode||(this.hide(),this.$element=null,this.options=null)},enable:function(){this.enabled=!0},disable:function(){this.enabled=!1},toggleEnabled:function(){this.enabled=!this.enabled},toggle:function(){this[this.tip().hasClass("in")?"hide":"show"]()}},a.fn.tooltip=function(c){return this.each(function(){var d=a(this),e=d.data("tooltip"),f=typeof c=="object"&&c;e||d.data("tooltip",e=new b(this,f)),typeof c=="string"&&e[c]()})},a.fn.tooltip.Constructor=b,a.fn.tooltip.defaults={animation:!0,placement:"top",selector:!1,template:'<div class="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',trigger:"hover",title:"",delay:0}}(window.jQuery),!function(a){"use strict";var b=function(a,b){this.init("popover",a,b)};b.prototype=a.extend({},a.fn.tooltip.Constructor.prototype,{constructor:b,setContent:function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.isHTML(b)?"html":"text"](b),a.find(".popover-content > *")[this.isHTML(c)?"html":"text"](c),a.removeClass("fade top bottom left right in")},hasContent:function(){return this.getTitle()||this.getContent()},getContent:function(){var a,b=this.$element,c=this.options;return a=b.attr("data-content")||(typeof c.content=="function"?c.content.call(b[0]):c.content),a},tip:function(){return this.$tip||(this.$tip=a(this.options.template)),this.$tip}}),a.fn.popover=function(c){return this.each(function(){var d=a(this),e=d.data("popover"),f=typeof c=="object"&&c;e||d.data("popover",e=new b(this,f)),typeof c=="string"&&e[c]()})},a.fn.popover.Constructor=b,a.fn.popover.defaults=a.extend({},a.fn.tooltip.defaults,{placement:"right",content:"",template:'<div class="popover"><div class="arrow"></div><div class="popover-inner"><h3 class="popover-title"></h3><div class="popover-content"><p></p></div></div></div>'})}(window.jQuery),!function(a){function b(b,c){var d=a.proxy(this.process,this),e=a(b).is("body")?a(window):a(b),f;this.options=a.extend({},a.fn.scrollspy.defaults,c),this.$scrollElement=e.on("scroll.scroll.data-api",d),this.selector=(this.options.target||(f=a(b).attr("href"))&&f.replace(/.*(?=#[^\s]+$)/,"")||"")+" .nav li > a",this.$body=a("body").on("click.scroll.data-api",this.selector,d),this.refresh(),this.process()}"use strict",b.prototype={constructor:b,refresh:function(){var b=this,c;this.offsets=a([]),this.targets=a([]),c=this.$body.find(this.selector).map(function(){var b=a(this),c=b.data("target")||b.attr("href"),d=/^#\w/.test(c)&&a(c);return d&&c.length&&[[d.position().top,c]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){b.offsets.push(this[0]),b.targets.push(this[1])})},process:function(){var a=this.$scrollElement.scrollTop()+this.options.offset,b=this.$scrollElement[0].scrollHeight||this.$body[0].scrollHeight,c=b-this.$scrollElement.height(),d=this.offsets,e=this.targets,f=this.activeTarget,g;if(a>=c)return f!=(g=e.last()[0])&&this.activate(g);for(g=d.length;g--;)f!=e[g]&&a>=d[g]&&(!d[g+1]||a<=d[g+1])&&this.activate(e[g])},activate:function(b){var c,d;this.activeTarget=b,a(this.selector).parent(".active").removeClass("active"),d=this.selector+'[data-target="'+b+'"],'+this.selector+'[href="'+b+'"]',c=a(d).parent("li").addClass("active"),c.parent(".dropdown-menu")&&(c=c.closest("li.dropdown").addClass("active")),c.trigger("activate")}},a.fn.scrollspy=function(c){return this.each(function(){var d=a(this),e=d.data("scrollspy"),f=typeof c=="object"&&c;e||d.data("scrollspy",e=new b(this,f)),typeof c=="string"&&e[c]()})},a.fn.scrollspy.Constructor=b,a.fn.scrollspy.defaults={offset:10},a(function(){a('[data-spy="scroll"]').each(function(){var b=a(this);b.scrollspy(b.data())})})}(window.jQuery),!function(a){"use strict";var b=function(b){this.element=a(b)};b.prototype={constructor:b,show:function(){var b=this.element,c=b.closest("ul:not(.dropdown-menu)"),d=b.attr("data-target"),e,f,g;d||(d=b.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,""));if(b.parent("li").hasClass("active"))return;e=c.find(".active a").last()[0],g=a.Event("show",{relatedTarget:e}),b.trigger(g);if(g.isDefaultPrevented())return;f=a(d),this.activate(b.parent("li"),c),this.activate(f,f.parent(),function(){b.trigger({type:"shown",relatedTarget:e})})},activate:function(b,c,d){function g(){e.removeClass("active").find("> .dropdown-menu > .active").removeClass("active"),b.addClass("active"),f?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu")&&b.closest("li.dropdown").addClass("active"),d&&d()}var e=c.find("> .active"),f=d&&a.support.transition&&e.hasClass("fade");f?e.one(a.support.transition.end,g):g(),e.removeClass("in")}},a.fn.tab=function(c){return this.each(function(){var d=a(this),e=d.data("tab");e||d.data("tab",e=new b(this)),typeof c=="string"&&e[c]()})},a.fn.tab.Constructor=b,a(function(){a("body").on("click.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"]',function(b){b.preventDefault(),a(this).tab("show")})})}(window.jQuery),!function(a){"use strict";var b=function(b,c){this.$element=a(b),this.options=a.extend({},a.fn.typeahead.defaults,c),this.matcher=this.options.matcher||this.matcher,this.sorter=this.options.sorter||this.sorter,this.highlighter=this.options.highlighter||this.highlighter,this.updater=this.options.updater||this.updater,this.$menu=a(this.options.menu).appendTo("body"),this.source=this.options.source,this.shown=!1,this.listen()};b.prototype={constructor:b,select:function(){var a=this.$menu.find(".active").attr("data-value");return this.$element.val(this.updater(a)).change(),this.hide()},updater:function(a){return a},show:function(){var b=a.extend({},this.$element.position(),{height:this.$element[0].offsetHeight});return this.$menu.css({top:b.top+b.height,left:b.left}),this.$menu.show(),this.shown=!0,this},hide:function(){return this.$menu.hide(),this.shown=!1,this},lookup:function(b){var c=this,d,e;return this.query=this.$element.val(),this.query?(d=a.grep(this.source,function(a){return c.matcher(a)}),d=this.sorter(d),d.length?this.render(d.slice(0,this.options.items)).show():this.shown?this.hide():this):this.shown?this.hide():this},matcher:function(a){return~a.toLowerCase().indexOf(this.query.toLowerCase())},sorter:function(a){var b=[],c=[],d=[],e;while(e=a.shift())e.toLowerCase().indexOf(this.query.toLowerCase())?~e.indexOf(this.query)?c.push(e):d.push(e):b.push(e);return b.concat(c,d)},highlighter:function(a){var b=this.query.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&");return a.replace(new RegExp("("+b+")","ig"),function(a,b){return"<strong>"+b+"</strong>"})},render:function(b){var c=this;return b=a(b).map(function(b,d){return b=a(c.options.item).attr("data-value",d),b.find("a").html(c.highlighter(d)),b[0]}),b.first().addClass("active"),this.$menu.html(b),this},next:function(b){var c=this.$menu.find(".active").removeClass("active"),d=c.next();d.length||(d=a(this.$menu.find("li")[0])),d.addClass("active")},prev:function(a){var b=this.$menu.find(".active").removeClass("active"),c=b.prev();c.length||(c=this.$menu.find("li").last()),c.addClass("active")},listen:function(){this.$element.on("blur",a.proxy(this.blur,this)).on("keypress",a.proxy(this.keypress,this)).on("keyup",a.proxy(this.keyup,this)),(a.browser.webkit||a.browser.msie)&&this.$element.on("keydown",a.proxy(this.keypress,this)),this.$menu.on("click",a.proxy(this.click,this)).on("mouseenter","li",a.proxy(this.mouseenter,this))},keyup:function(a){switch(a.keyCode){case 40:case 38:break;case 9:case 13:if(!this.shown)return;this.select();break;case 27:if(!this.shown)return;this.hide();break;default:this.lookup()}a.stopPropagation(),a.preventDefault()},keypress:function(a){if(!this.shown)return;switch(a.keyCode){case 9:case 13:case 27:a.preventDefault();break;case 38:if(a.type!="keydown")break;a.preventDefault(),this.prev();break;case 40:if(a.type!="keydown")break;a.preventDefault(),this.next()}a.stopPropagation()},blur:function(a){var b=this;setTimeout(function(){b.hide()},150)},click:function(a){a.stopPropagation(),a.preventDefault(),this.select()},mouseenter:function(b){this.$menu.find(".active").removeClass("active"),a(b.currentTarget).addClass("active")}},a.fn.typeahead=function(c){return this.each(function(){var d=a(this),e=d.data("typeahead"),f=typeof c=="object"&&c;e||d.data("typeahead",e=new b(this,f)),typeof c=="string"&&e[c]()})},a.fn.typeahead.defaults={source:[],items:8,menu:'<ul class="typeahead dropdown-menu"></ul>',item:'<li><a href="#"></a></li>'},a.fn.typeahead.Constructor=b,a(function(){a("body").on("focus.typeahead.data-api",'[data-provide="typeahead"]',function(b){var c=a(this);if(c.data("typeahead"))return;b.preventDefault(),c.typeahead(c.data())})})}(window.jQuery);
\ No newline at end of file
diff --git a/src/fileupload/static/fileupload/js/canvas-to-blob.min.js b/src/fileupload/static/fileupload/js/canvas-to-blob.min.js
new file mode 100644 (file)
index 0000000..ab9e668
--- /dev/null
@@ -0,0 +1 @@
+(function(a){"use strict";var b=a.HTMLCanvasElement&&a.HTMLCanvasElement.prototype,c=a.BlobBuilder||a.WebKitBlobBuilder||a.MozBlobBuilder,d=c&&a.atob&&a.ArrayBuffer&&a.Uint8Array&&function(a){var b,d,e,f,g,h;a.split(",")[0].indexOf("base64")>=0?b=atob(a.split(",")[1]):b=decodeURIComponent(a.split(",")[1]),d=new ArrayBuffer(b.length),e=new Uint8Array(d);for(f=0;f<b.length;f+=1)e[f]=b.charCodeAt(f);return g=new c,g.append(d),h=a.split(",")[0].split(":")[1].split(";")[0],g.getBlob(h)};a.HTMLCanvasElement&&!b.toBlob&&(b.mozGetAsFile?b.toBlob=function(a,b){a(this.mozGetAsFile("blob",b))}:b.toDataURL&&d&&(b.toBlob=function(a,b){a(d(this.toDataURL(b)))})),typeof define!="undefined"&&define.amd?define(function(){return d}):a.dataURLtoBlob=d})(this);
diff --git a/src/fileupload/static/fileupload/js/csrf.js b/src/fileupload/static/fileupload/js/csrf.js
new file mode 100644 (file)
index 0000000..aec99cc
--- /dev/null
@@ -0,0 +1,22 @@
+// modify jquery ajax to add csrtoken when doing "local" requests
+$('html').ajaxSend(function(event, xhr, settings) {
+    function getCookie(name) {
+        var cookieValue = null;
+        if (document.cookie && document.cookie != '') {
+            var cookies = document.cookie.split(';');
+            for (var i = 0; i < cookies.length; i++) {
+                var cookie = jQuery.trim(cookies[i]);
+                // Does this cookie string begin with the name we want?
+                if (cookie.substring(0, name.length + 1) == (name + '=')) {
+                    cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
+                    break;
+                }
+            }
+        }
+        return cookieValue;
+    }
+    if (!(/^http:.*/.test(settings.url) || /^https:.*/.test(settings.url))) {
+        // Only send the token to relative URLs i.e. locally.
+        xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
+    }
+});
diff --git a/src/fileupload/static/fileupload/js/jquery.fileupload-fp.js b/src/fileupload/static/fileupload/js/jquery.fileupload-fp.js
new file mode 100644 (file)
index 0000000..634fb5e
--- /dev/null
@@ -0,0 +1,219 @@
+/*
+ * jQuery File Upload File Processing Plugin 1.0
+ * https://github.com/blueimp/jQuery-File-Upload
+ *
+ * Copyright 2012, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
+
+/*jslint nomen: true, unparam: true, regexp: true */
+/*global define, window, document */
+
+(function (factory) {
+    'use strict';
+    if (typeof define === 'function' && define.amd) {
+        // Register as an anonymous AMD module:
+        define([
+            'jquery',
+            'load-image',
+            'canvas-to-blob',
+            './jquery.fileupload'
+        ], factory);
+    } else {
+        // Browser globals:
+        factory(
+            window.jQuery,
+            window.loadImage
+        );
+    }
+}(function ($, loadImage) {
+    'use strict';
+
+    // The File Upload IP version extends the basic fileupload widget
+    // with file processing functionality:
+    $.widget('blueimpFP.fileupload', $.blueimp.fileupload, {
+
+        options: {
+            // The list of file processing actions:
+            process: [
+            /*
+                {
+                    action: 'load',
+                    fileTypes: /^image\/(gif|jpeg|png)$/,
+                    maxFileSize: 20000000 // 20MB
+                },
+                {
+                    action: 'resize',
+                    maxWidth: 1920,
+                    maxHeight: 1200,
+                    minWidth: 800,
+                    minHeight: 600
+                },
+                {
+                    action: 'save'
+                }
+            */
+            ],
+
+            // The add callback is invoked as soon as files are added to the
+            // fileupload widget (via file input selection, drag & drop or add
+            // API call). See the basic file upload widget for more information:
+            add: function (e, data) {
+                $(this).fileupload('process', data).done(function () {
+                    data.submit();
+                });
+            }
+        },
+
+        processActions: {
+            // Loads the image given via data.files and data.index
+            // as canvas element.
+            // Accepts the options fileTypes (regular expression)
+            // and maxFileSize (integer) to limit the files to load:
+            load: function (data, options) {
+                var that = this,
+                    file = data.files[data.index],
+                    dfd = $.Deferred();
+                if (window.HTMLCanvasElement &&
+                        window.HTMLCanvasElement.prototype.toBlob &&
+                        ($.type(options.maxFileSize) !== 'number' ||
+                            file.size < options.maxFileSize) &&
+                        (!options.fileTypes ||
+                            options.fileTypes.test(file.type))) {
+                    loadImage(
+                        file,
+                        function (canvas) {
+                            data.canvas = canvas;
+                            dfd.resolveWith(that, [data]);
+                        },
+                        {canvas: true}
+                    );
+                } else {
+                    dfd.rejectWith(that, [data]);
+                }
+                return dfd.promise();
+            },
+            // Resizes the image given as data.canvas and updates
+            // data.canvas with the resized image.
+            // Accepts the options maxWidth, maxHeight, minWidth and
+            // minHeight to scale the given image:
+            resize: function (data, options) {
+                if (data.canvas) {
+                    var canvas = loadImage.scale(data.canvas, options);
+                    if (canvas.width !== data.canvas.width ||
+                            canvas.height !== data.canvas.height) {
+                        data.canvas = canvas;
+                        data.processed = true;
+                    }
+                }
+                return data;
+            },
+            // Saves the processed image given as data.canvas
+            // inplace at data.index of data.files:
+            save: function (data, options) {
+                // Do nothing if no processing has happened:
+                if (!data.canvas || !data.processed) {
+                    return data;
+                }
+                var that = this,
+                    file = data.files[data.index],
+                    name = file.name,
+                    dfd = $.Deferred(),
+                    callback = function (blob) {
+                        if (!blob.name) {
+                            if (file.type === blob.type) {
+                                blob.name = file.name;
+                            } else if (file.name) {
+                                blob.name = file.name.replace(
+                                    /\..+$/,
+                                    '.' + blob.type.substr(6)
+                                );
+                            }
+                        }
+                        // Store the created blob at the position
+                        // of the original file in the files list:
+                        data.files[data.index] = blob;
+                        dfd.resolveWith(that, [data]);
+                    };
+                // Use canvas.mozGetAsFile directly, to retain the filename, as
+                // Gecko doesn't support the filename option for FormData.append:
+                if (data.canvas.mozGetAsFile) {
+                    callback(data.canvas.mozGetAsFile(
+                        (/^image\/(jpeg|png)$/.test(file.type) && name) ||
+                            ((name && name.replace(/\..+$/, '')) ||
+                                'blob') + '.png',
+                        file.type
+                    ));
+                } else {
+                    data.canvas.toBlob(callback, file.type);
+                }
+                return dfd.promise();
+            }
+        },
+
+        // Resizes the file at the given index and stores the created blob at
+        // the original position of the files list, returns a Promise object:
+        _processFile: function (files, index, options) {
+            var that = this,
+                dfd = $.Deferred().resolveWith(that, [{
+                    files: files,
+                    index: index
+                }]),
+                chain = dfd.promise();
+            that._processing += 1;
+            $.each(options.process, function (i, settings) {
+                chain = chain.pipe(function (data) {
+                    return that.processActions[settings.action]
+                        .call(this, data, settings);
+                });
+            });
+            chain.always(function () {
+                that._processing -= 1;
+                if (that._processing === 0) {
+                    that.element
+                        .removeClass('fileupload-processing');
+                }
+            });
+            if (that._processing === 1) {
+                that.element.addClass('fileupload-processing');
+            }
+            return chain;
+        },
+
+        // Processes the files given as files property of the data parameter,
+        // returns a Promise object that allows to bind a done handler, which
+        // will be invoked after processing all files (inplace) is done:
+        process: function (data) {
+            var that = this,
+                options = $.extend({}, this.options, data);
+            if (options.process && options.process.length &&
+                    this._isXHRUpload(options)) {
+                $.each(data.files, function (index, file) {
+                    that._processingQueue = that._processingQueue.pipe(
+                        function () {
+                            var dfd = $.Deferred();
+                            that._processFile(data.files, index, options)
+                                .always(function () {
+                                    dfd.resolveWith(that);
+                                });
+                            return dfd.promise();
+                        }
+                    );
+                });
+            }
+            return this._processingQueue;
+        },
+
+        _create: function () {
+            $.blueimp.fileupload.prototype._create.call(this);
+            this._processing = 0;
+            this._processingQueue = $.Deferred().resolveWith(this)
+                .promise();
+        }
+
+    });
+
+}));
diff --git a/src/fileupload/static/fileupload/js/jquery.fileupload-ui.js b/src/fileupload/static/fileupload/js/jquery.fileupload-ui.js
new file mode 100644 (file)
index 0000000..f7fc3bf
--- /dev/null
@@ -0,0 +1,691 @@
+/*
+ * jQuery File Upload User Interface Plugin 6.8.1
+ * https://github.com/blueimp/jQuery-File-Upload
+ *
+ * Copyright 2010, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
+
+/*jslint nomen: true, unparam: true, regexp: true */
+/*global define, window, document, URL, webkitURL, FileReader */
+
+(function (factory) {
+    'use strict';
+    if (typeof define === 'function' && define.amd) {
+        // Register as an anonymous AMD module:
+        define([
+            'jquery',
+            'tmpl',
+            'load-image',
+            './jquery.fileupload-fp'
+        ], factory);
+    } else {
+        // Browser globals:
+        factory(
+            window.jQuery,
+            window.tmpl,
+            window.loadImage
+        );
+    }
+}(function ($, tmpl, loadImage) {
+    'use strict';
+
+    // The UI version extends the FP (file processing) version or the basic
+    // file upload widget and adds complete user interface interaction:
+    var parentWidget = ($.blueimpFP || $.blueimp).fileupload;
+    $.widget('blueimpUI.fileupload', parentWidget, {
+
+        options: {
+            // By default, files added to the widget are uploaded as soon
+            // as the user clicks on the start buttons. To enable automatic
+            // uploads, set the following option to true:
+            autoUpload: false,
+            // The following option limits the number of files that are
+            // allowed to be uploaded using this widget:
+            maxNumberOfFiles: undefined,
+            // The maximum allowed file size:
+            maxFileSize: undefined,
+            // The minimum allowed file size:
+            minFileSize: undefined,
+            // The regular expression for allowed file types, matches
+            // against either file type or file name:
+            acceptFileTypes:  /.+$/i,
+            // The regular expression to define for which files a preview
+            // image is shown, matched against the file type:
+            previewSourceFileTypes: /^image\/(gif|jpeg|png)$/,
+            // The maximum file size of images that are to be displayed as preview:
+            previewSourceMaxFileSize: 5000000, // 5MB
+            // The maximum width of the preview images:
+            previewMaxWidth: 80,
+            // The maximum height of the preview images:
+            previewMaxHeight: 80,
+            // By default, preview images are displayed as canvas elements
+            // if supported by the browser. Set the following option to false
+            // to always display preview images as img elements:
+            previewAsCanvas: true,
+            // The ID of the upload template:
+            uploadTemplateId: 'template-upload',
+            // The ID of the download template:
+            downloadTemplateId: 'template-download',
+            // The container for the list of files. If undefined, it is set to
+            // an element with class "files" inside of the widget element:
+            filesContainer: undefined,
+            // By default, files are appended to the files container.
+            // Set the following option to true, to prepend files instead:
+            prependFiles: false,
+            // The expected data type of the upload response, sets the dataType
+            // option of the $.ajax upload requests:
+            dataType: 'json',
+
+            // The add callback is invoked as soon as files are added to the fileupload
+            // widget (via file input selection, drag & drop or add API call).
+            // See the basic file upload widget for more information:
+            add: function (e, data) {
+                var that = $(this).data('fileupload'),
+                    options = that.options,
+                    files = data.files;
+                $(this).fileupload('process', data).done(function () {
+                    that._adjustMaxNumberOfFiles(-files.length);
+                    data.isAdjusted = true;
+                    data.files.valid = data.isValidated = that._validate(files);
+                    data.context = that._renderUpload(files).data('data', data);
+                    options.filesContainer[
+                        options.prependFiles ? 'prepend' : 'append'
+                    ](data.context);
+                    that._renderPreviews(files, data.context);
+                    that._forceReflow(data.context);
+                    that._transition(data.context).done(
+                        function () {
+                            if ((that._trigger('added', e, data) !== false) &&
+                                    (options.autoUpload || data.autoUpload) &&
+                                    data.autoUpload !== false && data.isValidated) {
+                                data.submit();
+                            }
+                        }
+                    );
+                });
+            },
+            // Callback for the start of each file upload request:
+            send: function (e, data) {
+                var that = $(this).data('fileupload');
+                if (!data.isValidated) {
+                    if (!data.isAdjusted) {
+                        that._adjustMaxNumberOfFiles(-data.files.length);
+                    }
+                    if (!that._validate(data.files)) {
+                        return false;
+                    }
+                }
+                if (data.context && data.dataType &&
+                        data.dataType.substr(0, 6) === 'iframe') {
+                    // Iframe Transport does not support progress events.
+                    // In lack of an indeterminate progress bar, we set
+                    // the progress to 100%, showing the full animated bar:
+                    data.context
+                        .find('.progress').addClass(
+                            !$.support.transition && 'progress-animated'
+                        )
+                        .find('.bar').css(
+                            'width',
+                            parseInt(100, 10) + '%'
+                        );
+                }
+                return that._trigger('sent', e, data);
+            },
+            // Callback for successful uploads:
+            done: function (e, data) {
+                var that = $(this).data('fileupload'),
+                    template;
+                if (data.context) {
+                    data.context.each(function (index) {
+                        var file = ($.isArray(data.result) &&
+                                data.result[index]) || {error: 'emptyResult'};
+                        if (file.error) {
+                            that._adjustMaxNumberOfFiles(1);
+                        }
+                        that._transition($(this)).done(
+                            function () {
+                                var node = $(this);
+                                template = that._renderDownload([file])
+                                    .css('height', node.height())
+                                    .replaceAll(node);
+                                that._forceReflow(template);
+                                that._transition(template).done(
+                                    function () {
+                                        data.context = $(this);
+                                        that._trigger('completed', e, data);
+                                    }
+                                );
+                            }
+                        );
+                    });
+                } else {
+                    template = that._renderDownload(data.result)
+                        .appendTo(that.options.filesContainer);
+                    that._forceReflow(template);
+                    that._transition(template).done(
+                        function () {
+                            data.context = $(this);
+                            that._trigger('completed', e, data);
+                        }
+                    );
+                }
+            },
+            // Callback for failed (abort or error) uploads:
+            fail: function (e, data) {
+                var that = $(this).data('fileupload'),
+                    template;
+                that._adjustMaxNumberOfFiles(data.files.length);
+                if (data.context) {
+                    data.context.each(function (index) {
+                        if (data.errorThrown !== 'abort') {
+                            var file = data.files[index];
+                            file.error = file.error || data.errorThrown ||
+                                true;
+                            that._transition($(this)).done(
+                                function () {
+                                    var node = $(this);
+                                    template = that._renderDownload([file])
+                                        .replaceAll(node);
+                                    that._forceReflow(template);
+                                    that._transition(template).done(
+                                        function () {
+                                            data.context = $(this);
+                                            that._trigger('failed', e, data);
+                                        }
+                                    );
+                                }
+                            );
+                        } else {
+                            that._transition($(this)).done(
+                                function () {
+                                    $(this).remove();
+                                    that._trigger('failed', e, data);
+                                }
+                            );
+                        }
+                    });
+                } else if (data.errorThrown !== 'abort') {
+                    that._adjustMaxNumberOfFiles(-data.files.length);
+                    data.context = that._renderUpload(data.files)
+                        .appendTo(that.options.filesContainer)
+                        .data('data', data);
+                    that._forceReflow(data.context);
+                    that._transition(data.context).done(
+                        function () {
+                            data.context = $(this);
+                            that._trigger('failed', e, data);
+                        }
+                    );
+                } else {
+                    that._trigger('failed', e, data);
+                }
+            },
+            // Callback for upload progress events:
+            progress: function (e, data) {
+                if (data.context) {
+                    data.context.find('.bar').css(
+                        'width',
+                        parseInt(data.loaded / data.total * 100, 10) + '%'
+                    );
+                }
+            },
+            // Callback for global upload progress events:
+            progressall: function (e, data) {
+                var $this = $(this);
+                $this.find('.fileupload-progress')
+                    .find('.bar').css(
+                        'width',
+                        parseInt(data.loaded / data.total * 100, 10) + '%'
+                    ).end()
+                    .find('.progress-extended').each(function () {
+                        $(this).html(
+                            $this.data('fileupload')
+                                ._renderExtendedProgress(data)
+                        );
+                    });
+            },
+            // Callback for uploads start, equivalent to the global ajaxStart event:
+            start: function (e) {
+                var that = $(this).data('fileupload');
+                that._transition($(this).find('.fileupload-progress')).done(
+                    function () {
+                        that._trigger('started', e);
+                    }
+                );
+            },
+            // Callback for uploads stop, equivalent to the global ajaxStop event:
+            stop: function (e) {
+                var that = $(this).data('fileupload');
+                that._transition($(this).find('.fileupload-progress')).done(
+                    function () {
+                        $(this).find('.bar').css('width', '0%');
+                        $(this).find('.progress-extended').html('&nbsp;');
+                        that._trigger('stopped', e);
+                    }
+                );
+            },
+            // Callback for file deletion:
+            destroy: function (e, data) {
+                var that = $(this).data('fileupload');
+                if (data.url) {
+                    $.ajax(data);
+                }
+                that._adjustMaxNumberOfFiles(1);
+                that._transition(data.context).done(
+                    function () {
+                        $(this).remove();
+                        that._trigger('destroyed', e, data);
+                    }
+                );
+            }
+        },
+
+        // Link handler, that allows to download files
+        // by drag & drop of the links to the desktop:
+        _enableDragToDesktop: function () {
+            var link = $(this),
+                url = link.prop('href'),
+                name = link.prop('download'),
+                type = 'application/octet-stream';
+            link.bind('dragstart', function (e) {
+                try {
+                    e.originalEvent.dataTransfer.setData(
+                        'DownloadURL',
+                        [type, name, url].join(':')
+                    );
+                } catch (err) {}
+            });
+        },
+
+        _adjustMaxNumberOfFiles: function (operand) {
+            if (typeof this.options.maxNumberOfFiles === 'number') {
+                this.options.maxNumberOfFiles += operand;
+                if (this.options.maxNumberOfFiles < 1) {
+                    this._disableFileInputButton();
+                } else {
+                    this._enableFileInputButton();
+                }
+            }
+        },
+
+        _formatFileSize: function (bytes) {
+            if (typeof bytes !== 'number') {
+                return '';
+            }
+            if (bytes >= 1000000000) {
+                return (bytes / 1000000000).toFixed(2) + ' GB';
+            }
+            if (bytes >= 1000000) {
+                return (bytes / 1000000).toFixed(2) + ' MB';
+            }
+            return (bytes / 1000).toFixed(2) + ' KB';
+        },
+
+        _formatBitrate: function (bits) {
+            if (typeof bits !== 'number') {
+                return '';
+            }
+            if (bits >= 1000000000) {
+                return (bits / 1000000000).toFixed(2) + ' Gbit/s';
+            }
+            if (bits >= 1000000) {
+                return (bits / 1000000).toFixed(2) + ' Mbit/s';
+            }
+            if (bits >= 1000) {
+                return (bits / 1000).toFixed(2) + ' kbit/s';
+            }
+            return bits + ' bit/s';
+        },
+
+        _formatTime: function (seconds) {
+            var date = new Date(seconds * 1000),
+                days = parseInt(seconds / 86400, 10);
+            days = days ? days + 'd ' : '';
+            return days +
+                ('0' + date.getUTCHours()).slice(-2) + ':' +
+                ('0' + date.getUTCMinutes()).slice(-2) + ':' +
+                ('0' + date.getUTCSeconds()).slice(-2);
+        },
+
+        _formatPercentage: function (floatValue) {
+            return (floatValue * 100).toFixed(2) + ' %';
+        },
+
+        _renderExtendedProgress: function (data) {
+            return this._formatBitrate(data.bitrate) + ' | ' +
+                this._formatTime(
+                    (data.total - data.loaded) * 8 / data.bitrate
+                ) + ' | ' +
+                this._formatPercentage(
+                    data.loaded / data.total
+                ) + ' | ' +
+                this._formatFileSize(data.loaded) + ' / ' +
+                this._formatFileSize(data.total);
+        },
+
+        _hasError: function (file) {
+            if (file.error) {
+                return file.error;
+            }
+            // The number of added files is subtracted from
+            // maxNumberOfFiles before validation, so we check if
+            // maxNumberOfFiles is below 0 (instead of below 1):
+            if (this.options.maxNumberOfFiles < 0) {
+                return 'maxNumberOfFiles';
+            }
+            // Files are accepted if either the file type or the file name
+            // matches against the acceptFileTypes regular expression, as
+            // only browsers with support for the File API report the type:
+            if (!(this.options.acceptFileTypes.test(file.type) ||
+                    this.options.acceptFileTypes.test(file.name))) {
+                return 'acceptFileTypes';
+            }
+            if (this.options.maxFileSize &&
+                    file.size > this.options.maxFileSize) {
+                return 'maxFileSize';
+            }
+            if (typeof file.size === 'number' &&
+                    file.size < this.options.minFileSize) {
+                return 'minFileSize';
+            }
+            return null;
+        },
+
+        _validate: function (files) {
+            var that = this,
+                valid = !!files.length;
+            $.each(files, function (index, file) {
+                file.error = that._hasError(file);
+                if (file.error) {
+                    valid = false;
+                }
+            });
+            return valid;
+        },
+
+        _renderTemplate: function (func, files) {
+            if (!func) {
+                return $();
+            }
+            var result = func({
+                files: files,
+                formatFileSize: this._formatFileSize,
+                options: this.options
+            });
+            if (result instanceof $) {
+                return result;
+            }
+            return $(this.options.templatesContainer).html(result).children();
+        },
+
+        _renderPreview: function (file, node) {
+            var that = this,
+                options = this.options,
+                dfd = $.Deferred();
+            return ((loadImage && loadImage(
+                file,
+                function (img) {
+                    node.append(img);
+                    that._forceReflow(node);
+                    that._transition(node).done(function () {
+                        dfd.resolveWith(node);
+                    });
+                    if (!$.contains(document.body, node[0])) {
+                        // If the element is not part of the DOM,
+                        // transition events are not triggered,
+                        // so we have to resolve manually:
+                        dfd.resolveWith(node);
+                    }
+                },
+                {
+                    maxWidth: options.previewMaxWidth,
+                    maxHeight: options.previewMaxHeight,
+                    canvas: options.previewAsCanvas
+                }
+            )) || dfd.resolveWith(node)) && dfd;
+        },
+
+        _renderPreviews: function (files, nodes) {
+            var that = this,
+                options = this.options;
+            nodes.find('.preview span').each(function (index, element) {
+                var file = files[index];
+                if (options.previewSourceFileTypes.test(file.type) &&
+                        ($.type(options.previewSourceMaxFileSize) !== 'number' ||
+                        file.size < options.previewSourceMaxFileSize)) {
+                    that._processingQueue = that._processingQueue.pipe(function () {
+                        var dfd = $.Deferred();
+                        that._renderPreview(file, $(element)).done(
+                            function () {
+                                dfd.resolveWith(that);
+                            }
+                        );
+                        return dfd.promise();
+                    });
+                }
+            });
+            return this._processingQueue;
+        },
+
+        _renderUpload: function (files) {
+            return this._renderTemplate(
+                this.options.uploadTemplate,
+                files
+            );
+        },
+
+        _renderDownload: function (files) {
+            return this._renderTemplate(
+                this.options.downloadTemplate,
+                files
+            ).find('a[download]').each(this._enableDragToDesktop).end();
+        },
+
+        _startHandler: function (e) {
+            e.preventDefault();
+            var button = $(this),
+                template = button.closest('.template-upload'),
+                data = template.data('data');
+            if (data && data.submit && !data.jqXHR && data.submit()) {
+                button.prop('disabled', true);
+            }
+        },
+
+        _cancelHandler: function (e) {
+            e.preventDefault();
+            var template = $(this).closest('.template-upload'),
+                data = template.data('data') || {};
+            if (!data.jqXHR) {
+                data.errorThrown = 'abort';
+                e.data.fileupload._trigger('fail', e, data);
+            } else {
+                data.jqXHR.abort();
+            }
+        },
+
+        _deleteHandler: function (e) {
+            e.preventDefault();
+            var button = $(this);
+            e.data.fileupload._trigger('destroy', e, {
+                context: button.closest('.template-download'),
+                url: button.attr('data-url'),
+                type: button.attr('data-type') || 'DELETE',
+                dataType: e.data.fileupload.options.dataType
+            });
+        },
+
+        _forceReflow: function (node) {
+            this._reflow = $.support.transition &&
+                node.length && node[0].offsetWidth;
+        },
+
+        _transition: function (node) {
+            var dfd = $.Deferred();
+            if ($.support.transition && node.hasClass('fade')) {
+                node.bind(
+                    $.support.transition.end,
+                    function (e) {
+                        // Make sure we don't respond to other transitions events
+                        // in the container element, e.g. from button elements:
+                        if (e.target === node[0]) {
+                            node.unbind($.support.transition.end);
+                            dfd.resolveWith(node);
+                        }
+                    }
+                ).toggleClass('in');
+            } else {
+                node.toggleClass('in');
+                dfd.resolveWith(node);
+            }
+            return dfd;
+        },
+
+        _initButtonBarEventHandlers: function () {
+            var fileUploadButtonBar = this.element.find('.fileupload-buttonbar'),
+                filesList = this.options.filesContainer,
+                ns = this.options.namespace;
+            fileUploadButtonBar.find('.start')
+                .bind('click.' + ns, function (e) {
+                    e.preventDefault();
+                    filesList.find('.start button').click();
+                });
+            fileUploadButtonBar.find('.cancel')
+                .bind('click.' + ns, function (e) {
+                    e.preventDefault();
+                    filesList.find('.cancel button').click();
+                });
+            fileUploadButtonBar.find('.delete')
+                .bind('click.' + ns, function (e) {
+                    e.preventDefault();
+                    filesList.find('.delete input:checked')
+                        .siblings('button').click();
+                    fileUploadButtonBar.find('.toggle')
+                        .prop('checked', false);
+                });
+            fileUploadButtonBar.find('.toggle')
+                .bind('change.' + ns, function (e) {
+                    filesList.find('.delete input').prop(
+                        'checked',
+                        $(this).is(':checked')
+                    );
+                });
+        },
+
+        _destroyButtonBarEventHandlers: function () {
+            this.element.find('.fileupload-buttonbar button')
+                .unbind('click.' + this.options.namespace);
+            this.element.find('.fileupload-buttonbar .toggle')
+                .unbind('change.' + this.options.namespace);
+        },
+
+        _initEventHandlers: function () {
+            parentWidget.prototype._initEventHandlers.call(this);
+            var eventData = {fileupload: this};
+            this.options.filesContainer
+                .delegate(
+                    '.start button',
+                    'click.' + this.options.namespace,
+                    eventData,
+                    this._startHandler
+                )
+                .delegate(
+                    '.cancel button',
+                    'click.' + this.options.namespace,
+                    eventData,
+                    this._cancelHandler
+                )
+                .delegate(
+                    '.delete button',
+                    'click.' + this.options.namespace,
+                    eventData,
+                    this._deleteHandler
+                );
+            this._initButtonBarEventHandlers();
+        },
+
+        _destroyEventHandlers: function () {
+            var options = this.options;
+            this._destroyButtonBarEventHandlers();
+            options.filesContainer
+                .undelegate('.start button', 'click.' + options.namespace)
+                .undelegate('.cancel button', 'click.' + options.namespace)
+                .undelegate('.delete button', 'click.' + options.namespace);
+            parentWidget.prototype._destroyEventHandlers.call(this);
+        },
+
+        _enableFileInputButton: function () {
+            this.element.find('.fileinput-button input')
+                .prop('disabled', false)
+                .parent().removeClass('disabled');
+        },
+
+        _disableFileInputButton: function () {
+            this.element.find('.fileinput-button input')
+                .prop('disabled', true)
+                .parent().addClass('disabled');
+        },
+
+        _initTemplates: function () {
+            var options = this.options;
+            options.templatesContainer = document.createElement(
+                options.filesContainer.prop('nodeName')
+            );
+            if (tmpl) {
+                if (options.uploadTemplateId) {
+                    options.uploadTemplate = tmpl(options.uploadTemplateId);
+                }
+                if (options.downloadTemplateId) {
+                    options.downloadTemplate = tmpl(options.downloadTemplateId);
+                }
+            }
+        },
+
+        _initFilesContainer: function () {
+            var options = this.options;
+            if (options.filesContainer === undefined) {
+                options.filesContainer = this.element.find('.files');
+            } else if (!(options.filesContainer instanceof $)) {
+                options.filesContainer = $(options.filesContainer);
+            }
+        },
+
+        _initSpecialOptions: function () {
+            parentWidget.prototype._initSpecialOptions.call(this);
+            this._initFilesContainer();
+            this._initTemplates();
+        },
+
+        _create: function () {
+            parentWidget.prototype._create.call(this);
+            this._refreshOptionsList.push(
+                'filesContainer',
+                'uploadTemplateId',
+                'downloadTemplateId'
+            );
+            if (!$.blueimpFP) {
+                this._processingQueue = $.Deferred().resolveWith(this).promise();
+                this.process = function () {
+                    return this._processingQueue;
+                };
+            }
+        },
+
+        enable: function () {
+            parentWidget.prototype.enable.call(this);
+            this.element.find('input, button').prop('disabled', false);
+            this._enableFileInputButton();
+        },
+
+        disable: function () {
+            this.element.find('input, button').prop('disabled', true);
+            this._disableFileInputButton();
+            parentWidget.prototype.disable.call(this);
+        }
+
+    });
+
+}));
diff --git a/src/fileupload/static/fileupload/js/jquery.fileupload.js b/src/fileupload/static/fileupload/js/jquery.fileupload.js
new file mode 100644 (file)
index 0000000..05a654b
--- /dev/null
@@ -0,0 +1,949 @@
+/*
+ * jQuery File Upload Plugin 5.11.2
+ * https://github.com/blueimp/jQuery-File-Upload
+ *
+ * Copyright 2010, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
+
+/*jslint nomen: true, unparam: true, regexp: true */
+/*global define, window, document, Blob, FormData, location */
+
+(function (factory) {
+    'use strict';
+    if (typeof define === 'function' && define.amd) {
+        // Register as an anonymous AMD module:
+        define([
+            'jquery',
+            'jquery.ui.widget'
+        ], factory);
+    } else {
+        // Browser globals:
+        factory(window.jQuery);
+    }
+}(function ($) {
+    'use strict';
+
+    // The FileReader API is not actually used, but works as feature detection,
+    // as e.g. Safari supports XHR file uploads via the FormData API,
+    // but not non-multipart XHR file uploads:
+    $.support.xhrFileUpload = !!(window.XMLHttpRequestUpload && window.FileReader);
+    $.support.xhrFormDataFileUpload = !!window.FormData;
+
+    // The fileupload widget listens for change events on file input fields defined
+    // via fileInput setting and paste or drop events of the given dropZone.
+    // In addition to the default jQuery Widget methods, the fileupload widget
+    // exposes the "add" and "send" methods, to add or directly send files using
+    // the fileupload API.
+    // By default, files added via file input selection, paste, drag & drop or
+    // "add" method are uploaded immediately, but it is possible to override
+    // the "add" callback option to queue file uploads.
+    $.widget('blueimp.fileupload', {
+
+        options: {
+            // The namespace used for event handler binding on the dropZone and
+            // fileInput collections.
+            // If not set, the name of the widget ("fileupload") is used.
+            namespace: undefined,
+            // The drop target collection, by the default the complete document.
+            // Set to null or an empty collection to disable drag & drop support:
+            dropZone: $(document),
+            // The file input field collection, that is listened for change events.
+            // If undefined, it is set to the file input fields inside
+            // of the widget element on plugin initialization.
+            // Set to null or an empty collection to disable the change listener.
+            fileInput: undefined,
+            // By default, the file input field is replaced with a clone after
+            // each input field change event. This is required for iframe transport
+            // queues and allows change events to be fired for the same file
+            // selection, but can be disabled by setting the following option to false:
+            replaceFileInput: true,
+            // The parameter name for the file form data (the request argument name).
+            // If undefined or empty, the name property of the file input field is
+            // used, or "files[]" if the file input name property is also empty,
+            // can be a string or an array of strings:
+            paramName: undefined,
+            // By default, each file of a selection is uploaded using an individual
+            // request for XHR type uploads. Set to false to upload file
+            // selections in one request each:
+            singleFileUploads: true,
+            // To limit the number of files uploaded with one XHR request,
+            // set the following option to an integer greater than 0:
+            limitMultiFileUploads: undefined,
+            // Set the following option to true to issue all file upload requests
+            // in a sequential order:
+            sequentialUploads: false,
+            // To limit the number of concurrent uploads,
+            // set the following option to an integer greater than 0:
+            limitConcurrentUploads: undefined,
+            // Set the following option to true to force iframe transport uploads:
+            forceIframeTransport: false,
+            // Set the following option to the location of a redirect url on the
+            // origin server, for cross-domain iframe transport uploads:
+            redirect: undefined,
+            // The parameter name for the redirect url, sent as part of the form
+            // data and set to 'redirect' if this option is empty:
+            redirectParamName: undefined,
+            // Set the following option to the location of a postMessage window,
+            // to enable postMessage transport uploads:
+            postMessage: undefined,
+            // By default, XHR file uploads are sent as multipart/form-data.
+            // The iframe transport is always using multipart/form-data.
+            // Set to false to enable non-multipart XHR uploads:
+            multipart: true,
+            // To upload large files in smaller chunks, set the following option
+            // to a preferred maximum chunk size. If set to 0, null or undefined,
+            // or the browser does not support the required Blob API, files will
+            // be uploaded as a whole.
+            maxChunkSize: undefined,
+            // When a non-multipart upload or a chunked multipart upload has been
+            // aborted, this option can be used to resume the upload by setting
+            // it to the size of the already uploaded bytes. This option is most
+            // useful when modifying the options object inside of the "add" or
+            // "send" callbacks, as the options are cloned for each file upload.
+            uploadedBytes: undefined,
+            // By default, failed (abort or error) file uploads are removed from the
+            // global progress calculation. Set the following option to false to
+            // prevent recalculating the global progress data:
+            recalculateProgress: true,
+            // Interval in milliseconds to calculate and trigger progress events:
+            progressInterval: 100,
+            // Interval in milliseconds to calculate progress bitrate:
+            bitrateInterval: 500,
+
+            // Additional form data to be sent along with the file uploads can be set
+            // using this option, which accepts an array of objects with name and
+            // value properties, a function returning such an array, a FormData
+            // object (for XHR file uploads), or a simple object.
+            // The form of the first fileInput is given as parameter to the function:
+            formData: function (form) {
+                return form.serializeArray();
+            },
+
+            // The add callback is invoked as soon as files are added to the fileupload
+            // widget (via file input selection, drag & drop, paste or add API call).
+            // If the singleFileUploads option is enabled, this callback will be
+            // called once for each file in the selection for XHR file uplaods, else
+            // once for each file selection.
+            // The upload starts when the submit method is invoked on the data parameter.
+            // The data object contains a files property holding the added files
+            // and allows to override plugin options as well as define ajax settings.
+            // Listeners for this callback can also be bound the following way:
+            // .bind('fileuploadadd', func);
+            // data.submit() returns a Promise object and allows to attach additional
+            // handlers using jQuery's Deferred callbacks:
+            // data.submit().done(func).fail(func).always(func);
+            add: function (e, data) {
+                data.submit();
+            },
+
+            // Other callbacks:
+            // Callback for the submit event of each file upload:
+            // submit: function (e, data) {}, // .bind('fileuploadsubmit', func);
+            // Callback for the start of each file upload request:
+            // send: function (e, data) {}, // .bind('fileuploadsend', func);
+            // Callback for successful uploads:
+            // done: function (e, data) {}, // .bind('fileuploaddone', func);
+            // Callback for failed (abort or error) uploads:
+            // fail: function (e, data) {}, // .bind('fileuploadfail', func);
+            // Callback for completed (success, abort or error) requests:
+            // always: function (e, data) {}, // .bind('fileuploadalways', func);
+            // Callback for upload progress events:
+            // progress: function (e, data) {}, // .bind('fileuploadprogress', func);
+            // Callback for global upload progress events:
+            // progressall: function (e, data) {}, // .bind('fileuploadprogressall', func);
+            // Callback for uploads start, equivalent to the global ajaxStart event:
+            // start: function (e) {}, // .bind('fileuploadstart', func);
+            // Callback for uploads stop, equivalent to the global ajaxStop event:
+            // stop: function (e) {}, // .bind('fileuploadstop', func);
+            // Callback for change events of the fileInput collection:
+            // change: function (e, data) {}, // .bind('fileuploadchange', func);
+            // Callback for paste events to the dropZone collection:
+            // paste: function (e, data) {}, // .bind('fileuploadpaste', func);
+            // Callback for drop events of the dropZone collection:
+            // drop: function (e, data) {}, // .bind('fileuploaddrop', func);
+            // Callback for dragover events of the dropZone collection:
+            // dragover: function (e) {}, // .bind('fileuploaddragover', func);
+
+            // The plugin options are used as settings object for the ajax calls.
+            // The following are jQuery ajax settings required for the file uploads:
+            processData: false,
+            contentType: false,
+            cache: false
+        },
+
+        // A list of options that require a refresh after assigning a new value:
+        _refreshOptionsList: [
+            'namespace',
+            'dropZone',
+            'fileInput',
+            'multipart',
+            'forceIframeTransport'
+        ],
+
+        _BitrateTimer: function () {
+            this.timestamp = +(new Date());
+            this.loaded = 0;
+            this.bitrate = 0;
+            this.getBitrate = function (now, loaded, interval) {
+                var timeDiff = now - this.timestamp;
+                if (!this.bitrate || !interval || timeDiff > interval) {
+                    this.bitrate = (loaded - this.loaded) * (1000 / timeDiff) * 8;
+                    this.loaded = loaded;
+                    this.timestamp = now;
+                }
+                return this.bitrate;
+            };
+        },
+
+        _isXHRUpload: function (options) {
+            return !options.forceIframeTransport &&
+                ((!options.multipart && $.support.xhrFileUpload) ||
+                $.support.xhrFormDataFileUpload);
+        },
+
+        _getFormData: function (options) {
+            var formData;
+            if (typeof options.formData === 'function') {
+                return options.formData(options.form);
+            }
+                       if ($.isArray(options.formData)) {
+                return options.formData;
+            }
+                       if (options.formData) {
+                formData = [];
+                $.each(options.formData, function (name, value) {
+                    formData.push({name: name, value: value});
+                });
+                return formData;
+            }
+            return [];
+        },
+
+        _getTotal: function (files) {
+            var total = 0;
+            $.each(files, function (index, file) {
+                total += file.size || 1;
+            });
+            return total;
+        },
+
+        _onProgress: function (e, data) {
+            if (e.lengthComputable) {
+                var now = +(new Date()),
+                    total,
+                    loaded;
+                if (data._time && data.progressInterval &&
+                        (now - data._time < data.progressInterval) &&
+                        e.loaded !== e.total) {
+                    return;
+                }
+                data._time = now;
+                total = data.total || this._getTotal(data.files);
+                loaded = parseInt(
+                    e.loaded / e.total * (data.chunkSize || total),
+                    10
+                ) + (data.uploadedBytes || 0);
+                this._loaded += loaded - (data.loaded || data.uploadedBytes || 0);
+                data.lengthComputable = true;
+                data.loaded = loaded;
+                data.total = total;
+                data.bitrate = data._bitrateTimer.getBitrate(
+                    now,
+                    loaded,
+                    data.bitrateInterval
+                );
+                // Trigger a custom progress event with a total data property set
+                // to the file size(s) of the current upload and a loaded data
+                // property calculated accordingly:
+                this._trigger('progress', e, data);
+                // Trigger a global progress event for all current file uploads,
+                // including ajax calls queued for sequential file uploads:
+                this._trigger('progressall', e, {
+                    lengthComputable: true,
+                    loaded: this._loaded,
+                    total: this._total,
+                    bitrate: this._bitrateTimer.getBitrate(
+                        now,
+                        this._loaded,
+                        data.bitrateInterval
+                    )
+                });
+            }
+        },
+
+        _initProgressListener: function (options) {
+            var that = this,
+                xhr = options.xhr ? options.xhr() : $.ajaxSettings.xhr();
+            // Accesss to the native XHR object is required to add event listeners
+            // for the upload progress event:
+            if (xhr.upload) {
+                $(xhr.upload).bind('progress', function (e) {
+                    var oe = e.originalEvent;
+                    // Make sure the progress event properties get copied over:
+                    e.lengthComputable = oe.lengthComputable;
+                    e.loaded = oe.loaded;
+                    e.total = oe.total;
+                    that._onProgress(e, options);
+                });
+                options.xhr = function () {
+                    return xhr;
+                };
+            }
+        },
+
+        _initXHRData: function (options) {
+            var formData,
+                file = options.files[0],
+                // Ignore non-multipart setting if not supported:
+                multipart = options.multipart || !$.support.xhrFileUpload,
+                paramName = options.paramName[0];
+            if (!multipart || options.blob) {
+                // For non-multipart uploads and chunked uploads,
+                // file meta data is not part of the request body,
+                // so we transmit this data as part of the HTTP headers.
+                // For cross domain requests, these headers must be allowed
+                // via Access-Control-Allow-Headers or removed using
+                // the beforeSend callback:
+                options.headers = $.extend(options.headers, {
+                    'X-File-Name': file.name,
+                    'X-File-Type': file.type,
+                    'X-File-Size': file.size
+                });
+                if (!options.blob) {
+                    // Non-chunked non-multipart upload:
+                    options.contentType = file.type;
+                    options.data = file;
+                } else if (!multipart) {
+                    // Chunked non-multipart upload:
+                    options.contentType = 'application/octet-stream';
+                    options.data = options.blob;
+                }
+            }
+            if (multipart && $.support.xhrFormDataFileUpload) {
+                if (options.postMessage) {
+                    // window.postMessage does not allow sending FormData
+                    // objects, so we just add the File/Blob objects to
+                    // the formData array and let the postMessage window
+                    // create the FormData object out of this array:
+                    formData = this._getFormData(options);
+                    if (options.blob) {
+                        formData.push({
+                            name: paramName,
+                            value: options.blob
+                        });
+                    } else {
+                        $.each(options.files, function (index, file) {
+                            formData.push({
+                                name: options.paramName[index] || paramName,
+                                value: file
+                            });
+                        });
+                    }
+                } else {
+                    if (options.formData instanceof FormData) {
+                        formData = options.formData;
+                    } else {
+                        formData = new FormData();
+                        $.each(this._getFormData(options), function (index, field) {
+                            formData.append(field.name, field.value);
+                        });
+                    }
+                    if (options.blob) {
+                        formData.append(paramName, options.blob, file.name);
+                    } else {
+                        $.each(options.files, function (index, file) {
+                            // File objects are also Blob instances.
+                            // This check allows the tests to run with
+                            // dummy objects:
+                            if (file instanceof Blob) {
+                                formData.append(
+                                    options.paramName[index] || paramName,
+                                    file,
+                                    file.name
+                                );
+                            }
+                        });
+                    }
+                }
+                options.data = formData;
+            }
+            // Blob reference is not needed anymore, free memory:
+            options.blob = null;
+        },
+
+        _initIframeSettings: function (options) {
+            // Setting the dataType to iframe enables the iframe transport:
+            options.dataType = 'iframe ' + (options.dataType || '');
+            // The iframe transport accepts a serialized array as form data:
+            options.formData = this._getFormData(options);
+            // Add redirect url to form data on cross-domain uploads:
+            if (options.redirect && $('<a></a>').prop('href', options.url)
+                    .prop('host') !== location.host) {
+                options.formData.push({
+                    name: options.redirectParamName || 'redirect',
+                    value: options.redirect
+                });
+            }
+        },
+
+        _initDataSettings: function (options) {
+            if (this._isXHRUpload(options)) {
+                if (!this._chunkedUpload(options, true)) {
+                    if (!options.data) {
+                        this._initXHRData(options);
+                    }
+                    this._initProgressListener(options);
+                }
+                if (options.postMessage) {
+                    // Setting the dataType to postmessage enables the
+                    // postMessage transport:
+                    options.dataType = 'postmessage ' + (options.dataType || '');
+                }
+            } else {
+                this._initIframeSettings(options, 'iframe');
+            }
+        },
+
+        _getParamName: function (options) {
+            var fileInput = $(options.fileInput),
+                paramName = options.paramName;
+            if (!paramName) {
+                paramName = [];
+                fileInput.each(function () {
+                    var input = $(this),
+                        name = input.prop('name') || 'files[]',
+                        i = (input.prop('files') || [1]).length;
+                    while (i) {
+                        paramName.push(name);
+                        i -= 1;
+                    }
+                });
+                if (!paramName.length) {
+                    paramName = [fileInput.prop('name') || 'files[]'];
+                }
+            } else if (!$.isArray(paramName)) {
+                paramName = [paramName];
+            }
+            return paramName;
+        },
+
+        _initFormSettings: function (options) {
+            // Retrieve missing options from the input field and the
+            // associated form, if available:
+            if (!options.form || !options.form.length) {
+                options.form = $(options.fileInput.prop('form'));
+            }
+            options.paramName = this._getParamName(options);
+            if (!options.url) {
+                options.url = options.form.prop('action') || location.href;
+            }
+            // The HTTP request method must be "POST" or "PUT":
+            options.type = (options.type || options.form.prop('method') || '')
+                .toUpperCase();
+            if (options.type !== 'POST' && options.type !== 'PUT') {
+                options.type = 'POST';
+            }
+        },
+
+        _getAJAXSettings: function (data) {
+            var options = $.extend({}, this.options, data);
+            this._initFormSettings(options);
+            this._initDataSettings(options);
+            return options;
+        },
+
+        // Maps jqXHR callbacks to the equivalent
+        // methods of the given Promise object:
+        _enhancePromise: function (promise) {
+            promise.success = promise.done;
+            promise.error = promise.fail;
+            promise.complete = promise.always;
+            return promise;
+        },
+
+        // Creates and returns a Promise object enhanced with
+        // the jqXHR methods abort, success, error and complete:
+        _getXHRPromise: function (resolveOrReject, context, args) {
+            var dfd = $.Deferred(),
+                promise = dfd.promise();
+            context = context || this.options.context || promise;
+            if (resolveOrReject === true) {
+                dfd.resolveWith(context, args);
+            } else if (resolveOrReject === false) {
+                dfd.rejectWith(context, args);
+            }
+            promise.abort = dfd.promise;
+            return this._enhancePromise(promise);
+        },
+
+        // Uploads a file in multiple, sequential requests
+        // by splitting the file up in multiple blob chunks.
+        // If the second parameter is true, only tests if the file
+        // should be uploaded in chunks, but does not invoke any
+        // upload requests:
+        _chunkedUpload: function (options, testOnly) {
+            var that = this,
+                file = options.files[0],
+                fs = file.size,
+                ub = options.uploadedBytes = options.uploadedBytes || 0,
+                mcs = options.maxChunkSize || fs,
+                // Use the Blob methods with the slice implementation
+                // according to the W3C Blob API specification:
+                slice = file.webkitSlice || file.mozSlice || file.slice,
+                upload,
+                n,
+                jqXHR,
+                pipe;
+            if (!(this._isXHRUpload(options) && slice && (ub || mcs < fs)) ||
+                    options.data) {
+                return false;
+            }
+            if (testOnly) {
+                return true;
+            }
+            if (ub >= fs) {
+                file.error = 'uploadedBytes';
+                return this._getXHRPromise(
+                    false,
+                    options.context,
+                    [null, 'error', file.error]
+                );
+            }
+            // n is the number of blobs to upload,
+            // calculated via filesize, uploaded bytes and max chunk size:
+            n = Math.ceil((fs - ub) / mcs);
+            // The chunk upload method accepting the chunk number as parameter:
+            upload = function (i) {
+                if (!i) {
+                    return that._getXHRPromise(true, options.context);
+                }
+                // Upload the blobs in sequential order:
+                return upload(i -= 1).pipe(function () {
+                    // Clone the options object for each chunk upload:
+                    var o = $.extend({}, options);
+                    o.blob = slice.call(
+                        file,
+                        ub + i * mcs,
+                        ub + (i + 1) * mcs
+                    );
+                    // Store the current chunk size, as the blob itself
+                    // will be dereferenced after data processing:
+                    o.chunkSize = o.blob.size;
+                    // Process the upload data (the blob and potential form data):
+                    that._initXHRData(o);
+                    // Add progress listeners for this chunk upload:
+                    that._initProgressListener(o);
+                    jqXHR = ($.ajax(o) || that._getXHRPromise(false, o.context))
+                        .done(function () {
+                            // Create a progress event if upload is done and
+                            // no progress event has been invoked for this chunk:
+                            if (!o.loaded) {
+                                that._onProgress($.Event('progress', {
+                                    lengthComputable: true,
+                                    loaded: o.chunkSize,
+                                    total: o.chunkSize
+                                }), o);
+                            }
+                            options.uploadedBytes = o.uploadedBytes +=
+                                o.chunkSize;
+                        });
+                    return jqXHR;
+                });
+            };
+            // Return the piped Promise object, enhanced with an abort method,
+            // which is delegated to the jqXHR object of the current upload,
+            // and jqXHR callbacks mapped to the equivalent Promise methods:
+            pipe = upload(n);
+            pipe.abort = function () {
+                return jqXHR.abort();
+            };
+            return this._enhancePromise(pipe);
+        },
+
+        _beforeSend: function (e, data) {
+            if (this._active === 0) {
+                // the start callback is triggered when an upload starts
+                // and no other uploads are currently running,
+                // equivalent to the global ajaxStart event:
+                this._trigger('start');
+                // Set timer for global bitrate progress calculation:
+                this._bitrateTimer = new this._BitrateTimer();
+            }
+            this._active += 1;
+            // Initialize the global progress values:
+            this._loaded += data.uploadedBytes || 0;
+            this._total += this._getTotal(data.files);
+        },
+
+        _onDone: function (result, textStatus, jqXHR, options) {
+            if (!this._isXHRUpload(options)) {
+                // Create a progress event for each iframe load:
+                this._onProgress($.Event('progress', {
+                    lengthComputable: true,
+                    loaded: 1,
+                    total: 1
+                }), options);
+            }
+            options.result = result;
+            options.textStatus = textStatus;
+            options.jqXHR = jqXHR;
+            this._trigger('done', null, options);
+        },
+
+        _onFail: function (jqXHR, textStatus, errorThrown, options) {
+            options.jqXHR = jqXHR;
+            options.textStatus = textStatus;
+            options.errorThrown = errorThrown;
+            this._trigger('fail', null, options);
+            if (options.recalculateProgress) {
+                // Remove the failed (error or abort) file upload from
+                // the global progress calculation:
+                this._loaded -= options.loaded || options.uploadedBytes || 0;
+                this._total -= options.total || this._getTotal(options.files);
+            }
+        },
+
+        _onAlways: function (jqXHRorResult, textStatus, jqXHRorError, options) {
+            this._active -= 1;
+            options.textStatus = textStatus;
+            if (jqXHRorError && jqXHRorError.always) {
+                options.jqXHR = jqXHRorError;
+                options.result = jqXHRorResult;
+            } else {
+                options.jqXHR = jqXHRorResult;
+                options.errorThrown = jqXHRorError;
+            }
+            this._trigger('always', null, options);
+            if (this._active === 0) {
+                // The stop callback is triggered when all uploads have
+                // been completed, equivalent to the global ajaxStop event:
+                this._trigger('stop');
+                // Reset the global progress values:
+                this._loaded = this._total = 0;
+                this._bitrateTimer = null;
+            }
+        },
+
+        _onSend: function (e, data) {
+            var that = this,
+                jqXHR,
+                slot,
+                pipe,
+                options = that._getAJAXSettings(data),
+                send = function (resolve, args) {
+                    that._sending += 1;
+                    // Set timer for bitrate progress calculation:
+                    options._bitrateTimer = new that._BitrateTimer();
+                    jqXHR = jqXHR || (
+                        (resolve !== false &&
+                        that._trigger('send', e, options) !== false &&
+                        (that._chunkedUpload(options) || $.ajax(options))) ||
+                        that._getXHRPromise(false, options.context, args)
+                    ).done(function (result, textStatus, jqXHR) {
+                        that._onDone(result, textStatus, jqXHR, options);
+                    }).fail(function (jqXHR, textStatus, errorThrown) {
+                        that._onFail(jqXHR, textStatus, errorThrown, options);
+                    }).always(function (jqXHRorResult, textStatus, jqXHRorError) {
+                        that._sending -= 1;
+                        that._onAlways(
+                            jqXHRorResult,
+                            textStatus,
+                            jqXHRorError,
+                            options
+                        );
+                        if (options.limitConcurrentUploads &&
+                                options.limitConcurrentUploads > that._sending) {
+                            // Start the next queued upload,
+                            // that has not been aborted:
+                            var nextSlot = that._slots.shift();
+                            while (nextSlot) {
+                                if (!nextSlot.isRejected()) {
+                                    nextSlot.resolve();
+                                    break;
+                                }
+                                nextSlot = that._slots.shift();
+                            }
+                        }
+                    });
+                    return jqXHR;
+                };
+            this._beforeSend(e, options);
+            if (this.options.sequentialUploads ||
+                    (this.options.limitConcurrentUploads &&
+                    this.options.limitConcurrentUploads <= this._sending)) {
+                if (this.options.limitConcurrentUploads > 1) {
+                    slot = $.Deferred();
+                    this._slots.push(slot);
+                    pipe = slot.pipe(send);
+                } else {
+                    pipe = (this._sequence = this._sequence.pipe(send, send));
+                }
+                // Return the piped Promise object, enhanced with an abort method,
+                // which is delegated to the jqXHR object of the current upload,
+                // and jqXHR callbacks mapped to the equivalent Promise methods:
+                pipe.abort = function () {
+                    var args = [undefined, 'abort', 'abort'];
+                    if (!jqXHR) {
+                        if (slot) {
+                            slot.rejectWith(args);
+                        }
+                        return send(false, args);
+                    }
+                    return jqXHR.abort();
+                };
+                return this._enhancePromise(pipe);
+            }
+            return send();
+        },
+
+        _onAdd: function (e, data) {
+            var that = this,
+                result = true,
+                options = $.extend({}, this.options, data),
+                limit = options.limitMultiFileUploads,
+                paramName = this._getParamName(options),
+                paramNameSet,
+                paramNameSlice,
+                fileSet,
+                i;
+            if (!(options.singleFileUploads || limit) ||
+                    !this._isXHRUpload(options)) {
+                fileSet = [data.files];
+                paramNameSet = [paramName];
+            } else if (!options.singleFileUploads && limit) {
+                fileSet = [];
+                paramNameSet = [];
+                for (i = 0; i < data.files.length; i += limit) {
+                    fileSet.push(data.files.slice(i, i + limit));
+                    paramNameSlice = paramName.slice(i, i + limit);
+                    if (!paramNameSlice.length) {
+                        paramNameSlice = paramName;
+                    }
+                    paramNameSet.push(paramNameSlice);
+                }
+            } else {
+                paramNameSet = paramName;
+            }
+            data.originalFiles = data.files;
+            $.each(fileSet || data.files, function (index, element) {
+                var newData = $.extend({}, data);
+                newData.files = fileSet ? element : [element];
+                newData.paramName = paramNameSet[index];
+                newData.submit = function () {
+                    newData.jqXHR = this.jqXHR =
+                        (that._trigger('submit', e, this) !== false) &&
+                        that._onSend(e, this);
+                    return this.jqXHR;
+                };
+                return (result = that._trigger('add', e, newData));
+            });
+            return result;
+        },
+
+        // File Normalization for Gecko 1.9.1 (Firefox 3.5) support:
+        _normalizeFile: function (index, file) {
+            if (file.name === undefined && file.size === undefined) {
+                file.name = file.fileName;
+                file.size = file.fileSize;
+            }
+        },
+
+        _replaceFileInput: function (input) {
+            var inputClone = input.clone(true);
+            $('<form></form>').append(inputClone)[0].reset();
+            // Detaching allows to insert the fileInput on another form
+            // without loosing the file input value:
+            input.after(inputClone).detach();
+            // Avoid memory leaks with the detached file input:
+            $.cleanData(input.unbind('remove'));
+            // Replace the original file input element in the fileInput
+            // collection with the clone, which has been copied including
+            // event handlers:
+            this.options.fileInput = this.options.fileInput.map(function (i, el) {
+                if (el === input[0]) {
+                    return inputClone[0];
+                }
+                return el;
+            });
+            // If the widget has been initialized on the file input itself,
+            // override this.element with the file input clone:
+            if (input[0] === this.element[0]) {
+                this.element = inputClone;
+            }
+        },
+
+        _onChange: function (e) {
+            var that = e.data.fileupload,
+                data = {
+                    files: $.each($.makeArray(e.target.files), that._normalizeFile),
+                    fileInput: $(e.target),
+                    form: $(e.target.form)
+                };
+            if (!data.files.length) {
+                // If the files property is not available, the browser does not
+                // support the File API and we add a pseudo File object with
+                // the input value as name with path information removed:
+                data.files = [{name: e.target.value.replace(/^.*\\/, '')}];
+            }
+            if (that.options.replaceFileInput) {
+                that._replaceFileInput(data.fileInput);
+            }
+            if (that._trigger('change', e, data) === false ||
+                    that._onAdd(e, data) === false) {
+                return false;
+            }
+        },
+
+        _onPaste: function (e) {
+            var that = e.data.fileupload,
+                cbd = e.originalEvent.clipboardData,
+                items = (cbd && cbd.items) || [],
+                data = {files: []};
+            $.each(items, function (index, item) {
+                var file = item.getAsFile && item.getAsFile();
+                if (file) {
+                    data.files.push(file);
+                }
+            });
+            if (that._trigger('paste', e, data) === false ||
+                    that._onAdd(e, data) === false) {
+                return false;
+            }
+        },
+
+        _onDrop: function (e) {
+            var that = e.data.fileupload,
+                dataTransfer = e.dataTransfer = e.originalEvent.dataTransfer,
+                data = {
+                    files: $.each(
+                        $.makeArray(dataTransfer && dataTransfer.files),
+                        that._normalizeFile
+                    )
+                };
+            if (that._trigger('drop', e, data) === false ||
+                    that._onAdd(e, data) === false) {
+                return false;
+            }
+            e.preventDefault();
+        },
+
+        _onDragOver: function (e) {
+            var that = e.data.fileupload,
+                dataTransfer = e.dataTransfer = e.originalEvent.dataTransfer;
+            if (that._trigger('dragover', e) === false) {
+                return false;
+            }
+            if (dataTransfer) {
+                dataTransfer.dropEffect = dataTransfer.effectAllowed = 'copy';
+            }
+            e.preventDefault();
+        },
+
+        _initEventHandlers: function () {
+            var ns = this.options.namespace;
+            if (this._isXHRUpload(this.options)) {
+                this.options.dropZone
+                    .bind('dragover.' + ns, {fileupload: this}, this._onDragOver)
+                    .bind('drop.' + ns, {fileupload: this}, this._onDrop)
+                    .bind('paste.' + ns, {fileupload: this}, this._onPaste);
+            }
+            this.options.fileInput
+                .bind('change.' + ns, {fileupload: this}, this._onChange);
+        },
+
+        _destroyEventHandlers: function () {
+            var ns = this.options.namespace;
+            this.options.dropZone
+                .unbind('dragover.' + ns, this._onDragOver)
+                .unbind('drop.' + ns, this._onDrop)
+                .unbind('paste.' + ns, this._onPaste);
+            this.options.fileInput
+                .unbind('change.' + ns, this._onChange);
+        },
+
+        _setOption: function (key, value) {
+            var refresh = $.inArray(key, this._refreshOptionsList) !== -1;
+            if (refresh) {
+                this._destroyEventHandlers();
+            }
+            $.Widget.prototype._setOption.call(this, key, value);
+            if (refresh) {
+                this._initSpecialOptions();
+                this._initEventHandlers();
+            }
+        },
+
+        _initSpecialOptions: function () {
+            var options = this.options;
+            if (options.fileInput === undefined) {
+                options.fileInput = this.element.is('input:file') ?
+                        this.element : this.element.find('input:file');
+            } else if (!(options.fileInput instanceof $)) {
+                options.fileInput = $(options.fileInput);
+            }
+            if (!(options.dropZone instanceof $)) {
+                options.dropZone = $(options.dropZone);
+            }
+        },
+
+        _create: function () {
+            var options = this.options;
+            // Initialize options set via HTML5 data-attributes:
+            $.extend(options, $(this.element[0].cloneNode(false)).data());
+            options.namespace = options.namespace || this.widgetName;
+            this._initSpecialOptions();
+            this._slots = [];
+            this._sequence = this._getXHRPromise(true);
+            this._sending = this._active = this._loaded = this._total = 0;
+            this._initEventHandlers();
+        },
+
+        destroy: function () {
+            this._destroyEventHandlers();
+            $.Widget.prototype.destroy.call(this);
+        },
+
+        enable: function () {
+            $.Widget.prototype.enable.call(this);
+            this._initEventHandlers();
+        },
+
+        disable: function () {
+            this._destroyEventHandlers();
+            $.Widget.prototype.disable.call(this);
+        },
+
+        // This method is exposed to the widget API and allows adding files
+        // using the fileupload API. The data parameter accepts an object which
+        // must have a files property and can contain additional options:
+        // .fileupload('add', {files: filesList});
+        add: function (data) {
+            if (!data || this.options.disabled) {
+                return;
+            }
+            data.files = $.each($.makeArray(data.files), this._normalizeFile);
+            this._onAdd(null, data);
+        },
+
+        // This method is exposed to the widget API and allows sending files
+        // using the fileupload API. The data parameter accepts an object which
+        // must have a files property and can contain additional options:
+        // .fileupload('send', {files: filesList});
+        // The method returns a Promise object for the file upload call.
+        send: function (data) {
+            if (data && !this.options.disabled) {
+                data.files = $.each($.makeArray(data.files), this._normalizeFile);
+                if (data.files.length) {
+                    return this._onSend(null, data);
+                }
+            }
+            return this._getXHRPromise(false, data && data.context);
+        }
+
+    });
+
+}));
diff --git a/src/fileupload/static/fileupload/js/jquery.iframe-transport.js b/src/fileupload/static/fileupload/js/jquery.iframe-transport.js
new file mode 100644 (file)
index 0000000..04a5662
--- /dev/null
@@ -0,0 +1,171 @@
+/*
+ * jQuery Iframe Transport Plugin 1.4
+ * https://github.com/blueimp/jQuery-File-Upload
+ *
+ * Copyright 2011, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
+
+/*jslint unparam: true, nomen: true */
+/*global define, window, document */
+
+(function (factory) {
+    'use strict';
+    if (typeof define === 'function' && define.amd) {
+        // Register as an anonymous AMD module:
+        define(['jquery'], factory);
+    } else {
+        // Browser globals:
+        factory(window.jQuery);
+    }
+}(function ($) {
+    'use strict';
+
+    // Helper variable to create unique names for the transport iframes:
+    var counter = 0;
+
+    // The iframe transport accepts three additional options:
+    // options.fileInput: a jQuery collection of file input fields
+    // options.paramName: the parameter name for the file form data,
+    //  overrides the name property of the file input field(s),
+    //  can be a string or an array of strings.
+    // options.formData: an array of objects with name and value properties,
+    //  equivalent to the return data of .serializeArray(), e.g.:
+    //  [{name: 'a', value: 1}, {name: 'b', value: 2}]
+    $.ajaxTransport('iframe', function (options) {
+        if (options.async && (options.type === 'POST' || options.type === 'GET')) {
+            var form,
+                iframe;
+            return {
+                send: function (_, completeCallback) {
+                    form = $('<form style="display:none;"></form>');
+                    // javascript:false as initial iframe src
+                    // prevents warning popups on HTTPS in IE6.
+                    // IE versions below IE8 cannot set the name property of
+                    // elements that have already been added to the DOM,
+                    // so we set the name along with the iframe HTML markup:
+                    iframe = $(
+                        '<iframe src="javascript:false;" name="iframe-transport-' +
+                            (counter += 1) + '"></iframe>'
+                    ).bind('load', function () {
+                        var fileInputClones,
+                            paramNames = $.isArray(options.paramName) ?
+                                    options.paramName : [options.paramName];
+                        iframe
+                            .unbind('load')
+                            .bind('load', function () {
+                                var response;
+                                // Wrap in a try/catch block to catch exceptions thrown
+                                // when trying to access cross-domain iframe contents:
+                                try {
+                                    response = iframe.contents();
+                                    // Google Chrome and Firefox do not throw an
+                                    // exception when calling iframe.contents() on
+                                    // cross-domain requests, so we unify the response:
+                                    if (!response.length || !response[0].firstChild) {
+                                        throw new Error();
+                                    }
+                                } catch (e) {
+                                    response = undefined;
+                                }
+                                // The complete callback returns the
+                                // iframe content document as response object:
+                                completeCallback(
+                                    200,
+                                    'success',
+                                    {'iframe': response}
+                                );
+                                // Fix for IE endless progress bar activity bug
+                                // (happens on form submits to iframe targets):
+                                $('<iframe src="javascript:false;"></iframe>')
+                                    .appendTo(form);
+                                form.remove();
+                            });
+                        form
+                            .prop('target', iframe.prop('name'))
+                            .prop('action', options.url)
+                            .prop('method', options.type);
+                        if (options.formData) {
+                            $.each(options.formData, function (index, field) {
+                                $('<input type="hidden"/>')
+                                    .prop('name', field.name)
+                                    .val(field.value)
+                                    .appendTo(form);
+                            });
+                        }
+                        if (options.fileInput && options.fileInput.length &&
+                                options.type === 'POST') {
+                            fileInputClones = options.fileInput.clone();
+                            // Insert a clone for each file input field:
+                            options.fileInput.after(function (index) {
+                                return fileInputClones[index];
+                            });
+                            if (options.paramName) {
+                                options.fileInput.each(function (index) {
+                                    $(this).prop(
+                                        'name',
+                                        paramNames[index] || options.paramName
+                                    );
+                                });
+                            }
+                            // Appending the file input fields to the hidden form
+                            // removes them from their original location:
+                            form
+                                .append(options.fileInput)
+                                .prop('enctype', 'multipart/form-data')
+                                // enctype must be set as encoding for IE:
+                                .prop('encoding', 'multipart/form-data');
+                        }
+                        form.submit();
+                        // Insert the file input fields at their original location
+                        // by replacing the clones with the originals:
+                        if (fileInputClones && fileInputClones.length) {
+                            options.fileInput.each(function (index, input) {
+                                var clone = $(fileInputClones[index]);
+                                $(input).prop('name', clone.prop('name'));
+                                clone.replaceWith(input);
+                            });
+                        }
+                    });
+                    form.append(iframe).appendTo(document.body);
+                },
+                abort: function () {
+                    if (iframe) {
+                        // javascript:false as iframe src aborts the request
+                        // and prevents warning popups on HTTPS in IE6.
+                        // concat is used to avoid the "Script URL" JSLint error:
+                        iframe
+                            .unbind('load')
+                            .prop('src', 'javascript'.concat(':false;'));
+                    }
+                    if (form) {
+                        form.remove();
+                    }
+                }
+            };
+        }
+    });
+
+    // The iframe transport returns the iframe content document as response.
+    // The following adds converters from iframe to text, json, html, and script:
+    $.ajaxSetup({
+        converters: {
+            'iframe text': function (iframe) {
+                return $(iframe[0].body).text();
+            },
+            'iframe json': function (iframe) {
+                return $.parseJSON($(iframe[0].body).text());
+            },
+            'iframe html': function (iframe) {
+                return $(iframe[0].body).html();
+            },
+            'iframe script': function (iframe) {
+                return $.globalEval($(iframe[0].body).text());
+            }
+        }
+    });
+
+}));
diff --git a/src/fileupload/static/fileupload/js/jquery.ui.widget.js b/src/fileupload/static/fileupload/js/jquery.ui.widget.js
new file mode 100644 (file)
index 0000000..9da8673
--- /dev/null
@@ -0,0 +1,282 @@
+/*
+ * jQuery UI Widget 1.8.18+amd
+ * https://github.com/blueimp/jQuery-File-Upload
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Widget
+ */
+
+(function (factory) {
+    if (typeof define === "function" && define.amd) {
+        // Register as an anonymous AMD module:
+        define(["jquery"], factory);
+    } else {
+        // Browser globals:
+        factory(jQuery);
+    }
+}(function( $, undefined ) {
+
+// jQuery 1.4+
+if ( $.cleanData ) {
+       var _cleanData = $.cleanData;
+       $.cleanData = function( elems ) {
+               for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
+                       try {
+                               $( elem ).triggerHandler( "remove" );
+                       // http://bugs.jquery.com/ticket/8235
+                       } catch( e ) {}
+               }
+               _cleanData( elems );
+       };
+} else {
+       var _remove = $.fn.remove;
+       $.fn.remove = function( selector, keepData ) {
+               return this.each(function() {
+                       if ( !keepData ) {
+                               if ( !selector || $.filter( selector, [ this ] ).length ) {
+                                       $( "*", this ).add( [ this ] ).each(function() {
+                                               try {
+                                                       $( this ).triggerHandler( "remove" );
+                                               // http://bugs.jquery.com/ticket/8235
+                                               } catch( e ) {}
+                                       });
+                               }
+                       }
+                       return _remove.call( $(this), selector, keepData );
+               });
+       };
+}
+
+$.widget = function( name, base, prototype ) {
+       var namespace = name.split( "." )[ 0 ],
+               fullName;
+       name = name.split( "." )[ 1 ];
+       fullName = namespace + "-" + name;
+
+       if ( !prototype ) {
+               prototype = base;
+               base = $.Widget;
+       }
+
+       // create selector for plugin
+       $.expr[ ":" ][ fullName ] = function( elem ) {
+               return !!$.data( elem, name );
+       };
+
+       $[ namespace ] = $[ namespace ] || {};
+       $[ namespace ][ name ] = function( options, element ) {
+               // allow instantiation without initializing for simple inheritance
+               if ( arguments.length ) {
+                       this._createWidget( options, element );
+               }
+       };
+
+       var basePrototype = new base();
+       // we need to make the options hash a property directly on the new instance
+       // otherwise we'll modify the options hash on the prototype that we're
+       // inheriting from
+//     $.each( basePrototype, function( key, val ) {
+//             if ( $.isPlainObject(val) ) {
+//                     basePrototype[ key ] = $.extend( {}, val );
+//             }
+//     });
+       basePrototype.options = $.extend( true, {}, basePrototype.options );
+       $[ namespace ][ name ].prototype = $.extend( true, basePrototype, {
+               namespace: namespace,
+               widgetName: name,
+               widgetEventPrefix: $[ namespace ][ name ].prototype.widgetEventPrefix || name,
+               widgetBaseClass: fullName
+       }, prototype );
+
+       $.widget.bridge( name, $[ namespace ][ name ] );
+};
+
+$.widget.bridge = function( name, object ) {
+       $.fn[ name ] = function( options ) {
+               var isMethodCall = typeof options === "string",
+                       args = Array.prototype.slice.call( arguments, 1 ),
+                       returnValue = this;
+
+               // allow multiple hashes to be passed on init
+               options = !isMethodCall && args.length ?
+                       $.extend.apply( null, [ true, options ].concat(args) ) :
+                       options;
+
+               // prevent calls to internal methods
+               if ( isMethodCall && options.charAt( 0 ) === "_" ) {
+                       return returnValue;
+               }
+
+               if ( isMethodCall ) {
+                       this.each(function() {
+                               var instance = $.data( this, name ),
+                                       methodValue = instance && $.isFunction( instance[options] ) ?
+                                               instance[ options ].apply( instance, args ) :
+                                               instance;
+                               // TODO: add this back in 1.9 and use $.error() (see #5972)
+//                             if ( !instance ) {
+//                                     throw "cannot call methods on " + name + " prior to initialization; " +
+//                                             "attempted to call method '" + options + "'";
+//                             }
+//                             if ( !$.isFunction( instance[options] ) ) {
+//                                     throw "no such method '" + options + "' for " + name + " widget instance";
+//                             }
+//                             var methodValue = instance[ options ].apply( instance, args );
+                               if ( methodValue !== instance && methodValue !== undefined ) {
+                                       returnValue = methodValue;
+                                       return false;
+                               }
+                       });
+               } else {
+                       this.each(function() {
+                               var instance = $.data( this, name );
+                               if ( instance ) {
+                                       instance.option( options || {} )._init();
+                               } else {
+                                       $.data( this, name, new object( options, this ) );
+                               }
+                       });
+               }
+
+               return returnValue;
+       };
+};
+
+$.Widget = function( options, element ) {
+       // allow instantiation without initializing for simple inheritance
+       if ( arguments.length ) {
+               this._createWidget( options, element );
+       }
+};
+
+$.Widget.prototype = {
+       widgetName: "widget",
+       widgetEventPrefix: "",
+       options: {
+               disabled: false
+       },
+       _createWidget: function( options, element ) {
+               // $.widget.bridge stores the plugin instance, but we do it anyway
+               // so that it's stored even before the _create function runs
+               $.data( element, this.widgetName, this );
+               this.element = $( element );
+               this.options = $.extend( true, {},
+                       this.options,
+                       this._getCreateOptions(),
+                       options );
+
+               var self = this;
+               this.element.bind( "remove." + this.widgetName, function() {
+                       self.destroy();
+               });
+
+               this._create();
+               this._trigger( "create" );
+               this._init();
+       },
+       _getCreateOptions: function() {
+               return $.metadata && $.metadata.get( this.element[0] )[ this.widgetName ];
+       },
+       _create: function() {},
+       _init: function() {},
+
+       destroy: function() {
+               this.element
+                       .unbind( "." + this.widgetName )
+                       .removeData( this.widgetName );
+               this.widget()
+                       .unbind( "." + this.widgetName )
+                       .removeAttr( "aria-disabled" )
+                       .removeClass(
+                               this.widgetBaseClass + "-disabled " +
+                               "ui-state-disabled" );
+       },
+
+       widget: function() {
+               return this.element;
+       },
+
+       option: function( key, value ) {
+               var options = key;
+
+               if ( arguments.length === 0 ) {
+                       // don't return a reference to the internal hash
+                       return $.extend( {}, this.options );
+               }
+
+               if  (typeof key === "string" ) {
+                       if ( value === undefined ) {
+                               return this.options[ key ];
+                       }
+                       options = {};
+                       options[ key ] = value;
+               }
+
+               this._setOptions( options );
+
+               return this;
+       },
+       _setOptions: function( options ) {
+               var self = this;
+               $.each( options, function( key, value ) {
+                       self._setOption( key, value );
+               });
+
+               return this;
+       },
+       _setOption: function( key, value ) {
+               this.options[ key ] = value;
+
+               if ( key === "disabled" ) {
+                       this.widget()
+                               [ value ? "addClass" : "removeClass"](
+                                       this.widgetBaseClass + "-disabled" + " " +
+                                       "ui-state-disabled" )
+                               .attr( "aria-disabled", value );
+               }
+
+               return this;
+       },
+
+       enable: function() {
+               return this._setOption( "disabled", false );
+       },
+       disable: function() {
+               return this._setOption( "disabled", true );
+       },
+
+       _trigger: function( type, event, data ) {
+               var prop, orig,
+                       callback = this.options[ type ];
+
+               data = data || {};
+               event = $.Event( event );
+               event.type = ( type === this.widgetEventPrefix ?
+                       type :
+                       this.widgetEventPrefix + type ).toLowerCase();
+               // the original event may come from any element
+               // so we need to reset the target on the new event
+               event.target = this.element[ 0 ];
+
+               // copy original event properties over to the new event
+               orig = event.originalEvent;
+               if ( orig ) {
+                       for ( prop in orig ) {
+                               if ( !( prop in event ) ) {
+                                       event[ prop ] = orig[ prop ];
+                               }
+                       }
+               }
+
+               this.element.trigger( event, data );
+
+               return !( $.isFunction(callback) &&
+                       callback.call( this.element[0], event, data ) === false ||
+                       event.isDefaultPrevented() );
+       }
+};
+
+}));
diff --git a/src/fileupload/static/fileupload/js/load-image.min.js b/src/fileupload/static/fileupload/js/load-image.min.js
new file mode 100644 (file)
index 0000000..faac82f
--- /dev/null
@@ -0,0 +1 @@
+(function(a){"use strict";var b=function(a,c,d){var e=document.createElement("img"),f,g;return e.onerror=c,e.onload=function(){g&&b.revokeObjectURL(g),c(b.scale(e,d))},window.Blob&&a instanceof Blob||window.File&&a instanceof File?f=g=b.createObjectURL(a):f=a,f?(e.src=f,e):b.readFile(a,function(a){e.src=a})},c=window.createObjectURL&&window||window.URL&&URL||window.webkitURL&&webkitURL;b.scale=function(a,b){b=b||{};var c=document.createElement("canvas"),d=a.width,e=a.height,f=Math.max((b.minWidth||d)/d,(b.minHeight||e)/e);return f>1&&(d=parseInt(d*f,10),e=parseInt(e*f,10)),f=Math.min((b.maxWidth||d)/d,(b.maxHeight||e)/e),f<1&&(d=parseInt(d*f,10),e=parseInt(e*f,10)),a.getContext||b.canvas&&c.getContext?(c.width=d,c.height=e,c.getContext("2d").drawImage(a,0,0,d,e),c):(a.width=d,a.height=e,a)},b.createObjectURL=function(a){return c?c.createObjectURL(a):!1},b.revokeObjectURL=function(a){return c?c.revokeObjectURL(a):!1},b.readFile=function(a,b){if(window.FileReader&&FileReader.prototype.readAsDataURL){var c=new FileReader;return c.onload=function(a){b(a.target.result)},c.readAsDataURL(a),c}return!1},typeof define!="undefined"&&define.amd?define(function(){return b}):a.loadImage=b})(this);
diff --git a/src/fileupload/static/fileupload/js/locale-en.js b/src/fileupload/static/fileupload/js/locale-en.js
new file mode 100644 (file)
index 0000000..ea64b0a
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * jQuery File Upload Plugin Localization Example 6.5.1
+ * https://github.com/blueimp/jQuery-File-Upload
+ *
+ * Copyright 2012, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
+
+/*global window */
+
+window.locale = {
+    "fileupload": {
+        "errors": {
+            "maxFileSize": "File is too big",
+            "minFileSize": "File is too small",
+            "acceptFileTypes": "Filetype not allowed",
+            "maxNumberOfFiles": "Max number of files exceeded",
+            "uploadedBytes": "Uploaded bytes exceed file size",
+            "emptyResult": "Empty file upload result"
+        },
+        "error": "Error",
+        "start": "Start",
+        "cancel": "Cancel",
+        "destroy": "Delete"
+    }
+};
diff --git a/src/fileupload/static/fileupload/js/locale-pl.js b/src/fileupload/static/fileupload/js/locale-pl.js
new file mode 100755 (executable)
index 0000000..9d05227
--- /dev/null
@@ -0,0 +1,16 @@
+window.locale = {
+    "fileupload": {
+        "errors": {
+            "maxFileSize": "Plik jest zbyt duży",
+            "minFileSize": "Plik jest zbyt mały",
+            "acceptFileTypes": "Niedozwolony typ pliku",
+            "maxNumberOfFiles": "Zbyt wiele plików",
+            "uploadedBytes": "Przesłane dane przekraczają rozmiar pliku",
+            "emptyResult": "Wysyłanie plików zwróciło pusty wynik"
+        },
+        "error": "Błąd",
+        "start": "Start",
+        "cancel": "Anuluj",
+        "destroy": "Usuń"
+    }
+};
diff --git a/src/fileupload/static/fileupload/js/main.js b/src/fileupload/static/fileupload/js/main.js
new file mode 100644 (file)
index 0000000..64c3064
--- /dev/null
@@ -0,0 +1,15 @@
+$(function () {
+    // Initialize the jQuery File Upload widget:
+    $('#fileupload').fileupload();
+
+    // Load existing files:
+    $('#fileupload').each(function () {
+        var that = this;
+        $.getJSON(this.action, function (result) {
+            if (result && result.length) {
+                $(that).fileupload('option', 'done')
+                    .call(that, null, {result: result});
+            }
+        });
+    });
+});
diff --git a/src/fileupload/static/fileupload/js/tmpl.min.js b/src/fileupload/static/fileupload/js/tmpl.min.js
new file mode 100644 (file)
index 0000000..c6e4922
--- /dev/null
@@ -0,0 +1 @@
+(function(a){"use strict";var b=function(a,c){var d=/[^\w\-\.:]/.test(a)?new Function(b.arg+",tmpl","var _e=tmpl.encode"+b.helper+",_s='"+a.replace(b.regexp,b.func)+"';return _s;"):b.cache[a]=b.cache[a]||b(b.load(a));return c?d(c,b):function(a){return d(a,b)}};b.cache={},b.load=function(a){return document.getElementById(a).innerHTML},b.regexp=/([\s'\\])(?![^%]*%\})|(?:\{%(=|#)([\s\S]+?)%\})|(\{%)|(%\})/g,b.func=function(a,b,c,d,e,f){if(b)return{"\n":"\\n","\r":"\\r","\t":"\\t"," ":" "}[a]||"\\"+a;if(c)return c==="="?"'+_e("+d+")+'":"'+("+d+"||'')+'";if(e)return"';";if(f)return"_s+='"},b.encReg=/[<>&"'\x00]/g,b.encMap={"<":"&lt;",">":"&gt;","&":"&amp;",'"':"&quot;","'":"&#39;"},b.encode=function(a){return String(a||"").replace(b.encReg,function(a){return b.encMap[a]||""})},b.arg="o",b.helper=",print=function(s,e){_s+=e&&(s||'')||_e(s);},include=function(s,d){_s+=tmpl(s,d);}",typeof define=="function"&&define.amd?define(function(){return b}):a.tmpl=b})(this);
diff --git a/src/fileupload/templates/fileupload/picture_form.html b/src/fileupload/templates/fileupload/picture_form.html
new file mode 100644 (file)
index 0000000..44c37db
--- /dev/null
@@ -0,0 +1,86 @@
+{% extends "catalogue/base.html" %}
+{% load i18n %}
+{% load upload_tags %}
+
+{% block add_css %}
+    <link rel="stylesheet" href="{{ STATIC_URL }}fileupload/css/bootstrap.min.css">
+    <link rel="stylesheet" href="{{ STATIC_URL }}fileupload/css/style.css">
+    <link rel="stylesheet" href="{{ STATIC_URL }}fileupload/css/bootstrap-image-gallery.min.css">
+    <link rel="stylesheet" href="{{ STATIC_URL }}fileupload/css/jquery.fileupload-ui.css">
+    <!-- Shim to make HTML5 elements usable in older Internet Explorer versions -->
+    <!--[if lt IE 9]><script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script><![endif]-->
+{% endblock %}
+
+
+{% block content %}
+
+<h1>
+{% trans "Browse:" %}
+{% for crumb in view.breadcrumbs %}
+    {% if crumb.1 %}
+        <a href="{{ crumb.1 }}">{{ crumb.0 }}</a>
+    {% else %}
+        {{ crumb.0 }}
+    {% endif %}
+    {% if not forloop.last %}/{% endif %}
+{% endfor %}
+</h1>
+
+
+    <form id="fileupload" method="post" action="." enctype="multipart/form-data">{% csrf_token %}
+        <div class="row fileupload-buttonbar">
+            <div class="span7">
+                <span class="btn btn-success fileinput-button">
+                    <i class="icon-plus icon-white"></i>
+                    <span>{% trans "Add files..." %}</span>
+                    <input type="file" multiple="" name="files">
+                </span>
+                <button class="btn btn-primary start" type="submit">
+                    <i class="icon-upload icon-white"></i>
+                    <span>{% trans "Start upload" %}</span>
+                </button>
+                <button class="btn btn-warning cancel" type="reset">
+                    <i class="icon-ban-circle icon-white"></i>
+                    <span>{% trans "Cancel upload" %}</span>
+                </button>
+                <button class="btn btn-danger delete" type="button">
+                    <i class="icon-trash icon-white"></i>
+                    <span>{% trans "Delete" %}</span>
+                </button>
+                <input type="checkbox" class="toggle">
+            </div>
+            <div class="span5 fileupload-progress fade">
+                <div class="progress progress-success progres-striped active">
+                    <div class="bar" style="width:0%"></div>
+                </div>
+                <div class="progress-extended">&nbsp;</div>
+            </div>
+        </div>
+        <div class="fileupload-loading"></div>
+        <table class="table table-striped"><tbody class="files" data-toggle="modal-gallery" data-target="#modal-gallery"></tbody></table>
+    </form>
+    <div class="fileupload-content">
+        <table class="files"></table>
+        <div class="fileupload-progressbar"></div>
+    </div>
+
+{% endblock %}
+
+{% block add_js %}
+
+{% upload_js %}
+<script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
+<script src="{{ STATIC_URL }}fileupload/js/jquery.ui.widget.js"></script>
+<script src="{{ STATIC_URL }}fileupload/js/tmpl.min.js"></script>
+<script src="{{ STATIC_URL }}fileupload/js/load-image.min.js"></script>
+<script src="{{ STATIC_URL }}fileupload/js/canvas-to-blob.min.js"></script>
+<script src="{{ STATIC_URL }}fileupload/js/bootstrap.min.js"></script>
+<script src="{{ STATIC_URL }}fileupload/js/bootstrap-image-gallery.min.js"></script>
+<script src="{{ STATIC_URL }}fileupload/js/jquery.iframe-transport.js"></script>
+<script src="{{ STATIC_URL }}fileupload/js/jquery.fileupload.js"></script>
+<script src="{{ STATIC_URL }}fileupload/js/jquery.fileupload-fp.js"></script>
+<script src="{{ STATIC_URL }}fileupload/js/jquery.fileupload-ui.js"></script>
+<script src="{{ STATIC_URL }}fileupload/js/locale-{{ LANGUAGE_CODE }}.js"></script>
+<script src="{{ STATIC_URL }}fileupload/js/main.js"></script>
+<script src="{{ STATIC_URL }}fileupload/js/csrf.js"></script>
+{% endblock %}
diff --git a/src/fileupload/templatetags/__init__.py b/src/fileupload/templatetags/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/fileupload/templatetags/upload_tags.py b/src/fileupload/templatetags/upload_tags.py
new file mode 100644 (file)
index 0000000..aefce2e
--- /dev/null
@@ -0,0 +1,68 @@
+from django import template
+
+register = template.Library()
+
+@register.simple_tag
+def upload_js():
+    return """
+<!-- The template to display files available for upload -->
+<script id="template-upload" type="text/x-tmpl">
+{% for (var i=0, file; file=o.files[i]; i++) { %}
+    <tr class="template-upload fade">
+        <td class="preview"><span class="fade"></span></td>
+        <td class="name"><span>{%=file.name%}</span></td>
+        <td class="size"><span>{%=o.formatFileSize(file.size)%}</span></td>
+        {% if (file.error) { %}
+            <td class="error" colspan="2"><span class="label label-important">{%=locale.fileupload.error%}</span> {%=locale.fileupload.errors[file.error] || file.error%}</td>
+        {% } else if (o.files.valid && !i) { %}
+            <td>
+                <div class="progress progress-success progress-striped active"><div class="bar" style="width:0%;"></div></div>
+            </td>
+            <td class="start">{% if (!o.options.autoUpload) { %}
+                <button class="btn btn-success">
+                    <i class="icon-upload icon-white"></i>
+                    <span>{%=locale.fileupload.start%}</span>
+                </button>
+            {% } %}</td>
+        {% } else { %}
+            <td colspan="2"></td>
+        {% } %}
+        <td class="cancel">{% if (!i) { %}
+            <button class="btn btn-warning">
+                <i class="icon-ban-circle icon-white"></i>
+                <span>{%=locale.fileupload.cancel%}</span>
+            </button>
+        {% } %}</td>
+    </tr>
+{% } %}
+</script>
+<!-- The template to display files available for download -->
+<script id="template-download" type="text/x-tmpl">
+{% for (var i=0, file; file=o.files[i]; i++) { %}
+    <tr class="template-download fade">
+        {% if (file.error) { %}
+            <td></td>
+            <td class="name"><span>{%=file.name%}</span></td>
+            <td class="size"><span>{%=o.formatFileSize(file.size)%}</span></td>
+            <td class="error" colspan="2"><span class="label label-important">{%=locale.fileupload.error%}</span> {%=locale.fileupload.errors[file.error] || file.error%}</td>
+        {% } else { %}
+            <td class="preview">{% if (file.thumbnail_url) { %}
+                <a href="{%=file.url%}" title="{%=file.name%}" rel="gallery" download="{%=file.name%}"><img src="{%=file.thumbnail_url%}"></a>
+            {% } %}</td>
+            <td class="name">
+                <a href="{%=file.url%}" title="{%=file.name%}" rel="{%=file.thumbnail_url&&'gallery'%}" download="{%=file.name%}">{%=file.name%}</a>
+            </td>
+            <td class="size"><span>{%=o.formatFileSize(file.size)%}</span></td>
+            <td colspan="2"></td>
+        {% } %}
+        <td class="delete">
+            <button class="btn btn-danger" data-type="{%=file.delete_type%}" data-url="{%=file.delete_url%}">
+                <i class="icon-trash icon-white"></i>
+                <span>{%=locale.fileupload.destroy%}</span>
+            </button>
+            <input type="checkbox" name="delete" value="1">
+        </td>
+    </tr>
+{% } %}
+</script>
+"""
diff --git a/src/fileupload/urls.py b/src/fileupload/urls.py
new file mode 100644 (file)
index 0000000..cd4f46c
--- /dev/null
@@ -0,0 +1,7 @@
+from django.conf.urls import patterns, url
+from fileupload.views import UploadView
+
+urlpatterns = patterns('',
+    url(r'^(?P<path>(?:.*/)?)$', UploadView.as_view(), name='fileupload'),
+)
+
diff --git a/src/fileupload/views.py b/src/fileupload/views.py
new file mode 100644 (file)
index 0000000..a2025fe
--- /dev/null
@@ -0,0 +1,154 @@
+import json
+import os
+from urllib import quote
+from django.conf import settings
+from django.http import HttpResponse, HttpResponseRedirect, HttpResponseForbidden, Http404
+from django.utils.decorators import method_decorator
+from django.utils.encoding import smart_bytes
+from django.views.decorators.vary import vary_on_headers
+from django.views.generic import FormView, View
+from .forms import UploadForm
+
+
+# Use sorl.thumbnail if available.
+try:
+    from sorl.thumbnail import default
+except ImportError:
+    def thumbnail(relpath):
+        return settings.MEDIA_URL + relpath
+else:
+    def thumbnail(relpath):
+        try:
+            return default.backend.get_thumbnail(relpath, "x50").url
+        except (IOError, ValueError):
+            # That's not an image. No thumb.
+            return None
+
+
+class JSONResponse(HttpResponse):
+    """JSON response class."""
+    def __init__(self, obj='', mimetype="application/json", *args, **kwargs):
+        content = json.dumps(obj)
+        super(JSONResponse, self).__init__(content, mimetype, *args, **kwargs)
+
+
+class UploadView(FormView):
+    template_name = "fileupload/picture_form.html"
+    form_class = UploadForm
+
+    def get_object(self, request, *args, **kwargs):
+        """Get any data for later use."""
+        return None
+
+    def get_directory(self):
+        """Directory relative to MEDIA_ROOT. Must end with a slash."""
+        return self.kwargs['path']
+
+    def breadcrumbs(self):
+        """List of tuples (name, url) or just (name,) for breadcrumbs.
+
+        Probably only the last item (representing currently browsed dir)
+        should lack url.
+
+        """
+        directory = self.get_directory()
+        now_path = os.path.dirname(self.request.get_full_path())
+        directory = os.path.dirname(directory)
+        if directory:
+            crumbs = [
+                (os.path.basename(directory),)
+            ]
+            directory = os.path.dirname(directory)
+            now_path = (os.path.dirname(now_path))
+            while directory:
+                crumbs.insert(0, (os.path.basename(directory), now_path+'/'))
+                directory = os.path.dirname(directory)
+                now_path = os.path.dirname(now_path)
+            crumbs.insert(0, ('media', now_path))
+        else:
+            crumbs = [('media',)]
+        return crumbs
+
+    def get_safe_path(self, filename=""):
+        """Finds absolute filesystem path of the browsed dir of file.
+        
+        Makes sure it's inside MEDIA_ROOT.
+        
+        """
+        filename = smart_bytes(filename)
+        path = os.path.abspath(os.path.join(
+                settings.MEDIA_ROOT,
+                smart_bytes(self.get_directory()),
+                filename))
+        if not path.startswith(settings.MEDIA_ROOT):
+            raise Http404
+        if filename:
+            if not path.startswith(self.get_safe_path()):
+                raise Http404
+        return path
+
+    def get_url(self, filename):
+        """Finds URL of a file in browsed dir."""
+        return settings.MEDIA_URL + self.get_directory() + quote(filename.encode('utf-8'))
+
+    @method_decorator(vary_on_headers('Accept'))
+    def dispatch(self, request, *args, **kwargs):
+        self.object = self.get_object(request, *args, **kwargs)
+        return super(UploadView, self).dispatch(request, *args, **kwargs)
+
+    def get(self, request, *args, **kwargs):
+        if request.is_ajax():
+            files = []
+            path = self.get_safe_path()
+            if os.path.isdir(path):
+                for f in sorted(os.listdir(path)):
+                    file_info = {
+                        "name": f,
+                    }
+                    if os.path.isdir(os.path.join(path, f)):
+                        file_info.update({
+                            "url": "%s%s/" % (request.get_full_path(), f),
+                        })
+                    else:
+                        file_info.update({
+                            "url": self.get_url(f),
+                            'thumbnail_url': thumbnail(self.get_directory() + f),
+                            'delete_url': "%s?file=%s" % (
+                                request.get_full_path(),
+                                quote(f.encode('utf-8'))),
+                            'delete_type': "DELETE"
+                        })
+                        thumbnail_url = thumbnail(self.get_directory() + f),
+                    files.append(file_info)
+            return JSONResponse(files)
+        else:
+            return super(UploadView, self).get(request, *args, **kwargs)
+
+    def form_valid(self, form):
+        flist = self.request.FILES.getlist('files')
+        path = self.get_safe_path()
+        if not os.path.isdir(path):
+            os.makedirs(path)
+        data = []
+        for f in flist:
+            with open(self.get_safe_path(f.name), 'w') as destination:
+                for chunk in f.chunks():
+                    destination.write(chunk)
+            data.append({
+                'name': f.name, 
+                'url': self.get_url(f.name),
+                'thumbnail_url': thumbnail(self.get_directory() + f.name),
+                        'delete_url': "%s?file=%s" % (
+                            self.request.get_full_path(),
+                            quote(f.name.encode('utf-8'))),
+                'delete_type': "DELETE"
+            })
+        response = JSONResponse(data)
+        response['Content-Disposition'] = 'inline; filename=files.json'
+        return response
+
+    def delete(self, request, *args, **kwargs):
+        os.unlink(self.get_safe_path(request.GET.get('file')))
+        response = JSONResponse(True)
+        response['Content-Disposition'] = 'inline; filename=files.json'
+        return response
diff --git a/src/manage.py b/src/manage.py
new file mode 100755 (executable)
index 0000000..7e83932
--- /dev/null
@@ -0,0 +1,10 @@
+#!/usr/bin/env python
+import os
+import sys
+
+if __name__ == "__main__":
+    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "redakcja.settings")
+
+    from django.core.management import execute_from_command_line
+
+    execute_from_command_line(sys.argv)
diff --git a/src/redakcja/__init__.py b/src/redakcja/__init__.py
new file mode 100644 (file)
index 0000000..b64e43e
--- /dev/null
@@ -0,0 +1,5 @@
+from __future__ import absolute_import
+
+# This will make sure the app is always imported when
+# Django starts so that shared_task will use this app.
+from .celery import app as celery_app
diff --git a/src/redakcja/celery.py b/src/redakcja/celery.py
new file mode 100644 (file)
index 0000000..a2fed69
--- /dev/null
@@ -0,0 +1,23 @@
+from __future__ import absolute_import
+
+import os
+import sys
+
+ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+sys.path = [
+    os.path.join(ROOT, 'apps'),
+    os.path.join(ROOT, 'lib'),
+    os.path.join(ROOT, 'lib/librarian'),
+] + sys.path
+
+os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'redakcja.localsettings')
+
+from celery import Celery
+from django.conf import settings
+
+app = Celery('redakcja')
+
+# Using a string here means the worker will not have to
+# pickle the object when using Windows.
+app.config_from_object('django.conf:settings')
+app.autodiscover_tasks(lambda: settings.INSTALLED_APPS)
diff --git a/src/redakcja/context_processors.py b/src/redakcja/context_processors.py
new file mode 100644 (file)
index 0000000..5e3372e
--- /dev/null
@@ -0,0 +1,19 @@
+# -*- coding: utf-8
+
+def settings(request):
+    from django.conf import settings
+
+    if settings.SHOW_APP_VERSION:
+        import subprocess
+        process = subprocess.Popen(["git", "show", "--oneline"], stdout=subprocess.PIPE)
+        data, _err = process.communicate()
+        # get app version 
+        VERSION = data.splitlines()[0].split()[0]
+    else:
+        VERSION = ''
+
+    return {
+        'MEDIA_URL': settings.MEDIA_URL,
+        'STATIC_URL': settings.STATIC_URL,
+        'APP_VERSION': VERSION,
+    }
diff --git a/src/redakcja/locale/pl/LC_MESSAGES/django.mo b/src/redakcja/locale/pl/LC_MESSAGES/django.mo
new file mode 100644 (file)
index 0000000..7f35966
Binary files /dev/null and b/src/redakcja/locale/pl/LC_MESSAGES/django.mo differ
diff --git a/src/redakcja/locale/pl/LC_MESSAGES/django.po b/src/redakcja/locale/pl/LC_MESSAGES/django.po
new file mode 100644 (file)
index 0000000..916f4db
--- /dev/null
@@ -0,0 +1,145 @@
+# Polskie tłumaczenie dla platformy wolnych lektur.
+# Copyright (C) 2009
+# This file is distributed under the same license as the 'platforma' package.
+# lrekucki@gmail.com, 2009.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2011-10-18 11:18+0200\n"
+"PO-Revision-Date: 2011-10-18 11:19+0100\n"
+"Last-Translator: Radek Czajka <radoslaw.czajka@nowoczesnapolska.org.pl>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: templates/404.html:8
+msgid "Page not found"
+msgstr "Strona nie została znaleziona"
+
+#: templates/404.html:10
+#, python-format
+msgid ""
+"The page you're trying\n"
+"to reach (<tt>%(p)s</tt>) could not be found. If it's a document, try\n"
+"looking for it on the <a href=\"/\">document list</a>."
+msgstr "Strona, do której próbujesz dotrzeć (<tt>%(p)s</tt>), nie istnieje. Spróbuj poszukać jej na <a href=\"/\">liście dokumentów</a>."
+
+#: templates/404.html:15
+#, python-format
+msgid ""
+"If you\n"
+"still can't find what you're looking for, please\n"
+"<a href=\"%(m)s\">contact the administrator</a>."
+msgstr "Jeśli nadal nie możesz znaleźć tego, czego szukasz, <a href=\"%(m)s\">skontaktuj się z administratorem</a>."
+
+#: templates/404.html:22
+#, python-format
+msgid ""
+"If you're coming from Redmine, please note that\n"
+"work is no longer managed there. \n"
+"Go to the <a href=\"/\">document list</a>\n"
+"or to <a href=\"%(m)s\">your page</a> instead."
+msgstr ""
+"Jeśli skierował Cię tu Redmine, zwróć uwagę, że nie służy on już do koordynowania prac redakcyjnych. Możesz za to przejść do <a href=\"/\">listy dokumentów</a>\n"
+"albo do <a href=\"%(m)s\">swojej strony</a>."
+
+#: templates/base.html:7
+msgid "Platforma Redakcyjna"
+msgstr ""
+
+#: templates/base.html:16
+msgid "Loading"
+msgstr "Ładowanie"
+
+#: templates/admin/index.html:21
+#, python-format
+msgid "Models available in the %(name)s application."
+msgstr ""
+
+#: templates/admin/index.html:22
+#, python-format
+msgid "%(name)s"
+msgstr ""
+
+#: templates/admin/index.html:32
+msgid "Add"
+msgstr ""
+
+#: templates/admin/index.html:38
+msgid "Change"
+msgstr ""
+
+#: templates/admin/index.html:48
+msgid "You don't have permission to edit anything."
+msgstr ""
+
+#: templates/admin/index.html:56
+msgid "Recent Actions"
+msgstr ""
+
+#: templates/admin/index.html:57
+msgid "My Actions"
+msgstr ""
+
+#: templates/admin/index.html:61
+msgid "None available"
+msgstr ""
+
+#: templates/admin/index.html:75
+msgid "Unknown content"
+msgstr ""
+
+#: templates/pagination/pagination.html:5
+#: templates/pagination/pagination.html:7
+msgid "previous"
+msgstr "poprzednie"
+
+#: templates/pagination/pagination.html:21
+#: templates/pagination/pagination.html:23
+msgid "next"
+msgstr "następne"
+
+#: templates/registration/head_login.html:5
+msgid "Log Out"
+msgstr "Wyloguj"
+
+#: templates/registration/head_login.html:9
+msgid "Log In"
+msgstr "Logowanie"
+
+#~ msgid "Refresh panel"
+#~ msgstr "Odśwież panel"
+
+#~ msgid "Print version"
+#~ msgstr "Wersja do druku"
+
+#~ msgid "Next page"
+#~ msgstr "Następna strona"
+
+#~ msgid "Zoom in"
+#~ msgstr "Powiększ"
+
+#~ msgid "Zoom out"
+#~ msgstr "Zmniejsz"
+
+#~ msgid "Reset zoom"
+#~ msgstr "Oryginalny rozmiar"
+
+#~ msgid "History"
+#~ msgstr "Historia"
+
+#~ msgid "Push"
+#~ msgstr "Zatwierdź"
+
+#~ msgid "Pull"
+#~ msgstr "Uaktualnij"
+
+#~ msgid "Save"
+#~ msgstr "Zapisz"
+
+#~ msgid "Quick save"
+#~ msgstr "Szybki zapis"
diff --git a/src/redakcja/localsettings.sample b/src/redakcja/localsettings.sample
new file mode 100644 (file)
index 0000000..a0eb426
--- /dev/null
@@ -0,0 +1,38 @@
+# 
+#  localsettings template for Platforma
+#
+#  Duplicate this file as localsettings.py and change it to your liking.
+#  Settings defined in localsettings.py will override settings from 
+#  settings.py file. localsettings.py should never be commited 
+#  to a version control system. Please make changes to settings.py 
+#  or localsettings.sample instead.
+#
+
+# THIS IS REQUIRED
+from redakcja.settings import *
+
+# Path to repository with managed documents
+CATALOGUE_REPO_PATH = '/srv/redakcja/books'
+
+LOGGING_CONFIG_FILE = "/srv/redakcja/logging.cfg.dev"
+
+STATIC_ROOT = '/srv/redakcja/static/'
+MEDIA_ROOT = '/srv/redakcja/media/'
+
+# Subdirectory of MEDIA_ROOT containing images
+IMAGE_DIR = 'images/'
+
+CAS_SERVER_URL = 'http://logowanie.nowoczesnapolska.org.pl/cas/'
+CAS_VERSION = '3'
+
+REDMINE_URL = 'http://redmine.nowoczesnapolska.org.pl/'
+DEBUG = True
+MAINTENANCE_MODE = False
+COMPRESS = False
+
+APICLIENT_WL_CONSUMER_KEY = None
+APICLIENT_WL_CONSUMER_SECRET = None
+
+CELERY_ALWAYS_EAGER = False
+
+SECRET_KEY = ''
diff --git a/src/redakcja/settings/__init__.py b/src/redakcja/settings/__init__.py
new file mode 100644 (file)
index 0000000..4d4fe8f
--- /dev/null
@@ -0,0 +1,37 @@
+from __future__ import absolute_import
+from os import path
+from redakcja.settings.common import *
+
+DATABASES = {
+    'default': {
+        'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
+        'NAME': path.join(PROJECT_ROOT, 'dev.sqlite'), # Or path to database file if using sqlite3.
+        'USER': '',                      # Not used with sqlite3.
+        'PASSWORD': '',                  # Not used with sqlite3.
+        'HOST': '',                      # Set to empty string for localhost. Not used with sqlite3.
+        'PORT': '',                      # Set to empty string for default. Not used with sqlite3.
+    }
+}
+
+try:
+    LOGGING_CONFIG_FILE
+except NameError:
+    LOGGING_CONFIG_FILE = os.path.join(PROJECT_ROOT, 'config',
+                                ('logging.cfg' if not DEBUG else 'logging.cfg.dev'))
+try:
+    import logging
+
+    if os.path.isfile(LOGGING_CONFIG_FILE):
+        import logging.config
+        logging.config.fileConfig(LOGGING_CONFIG_FILE)
+    else:
+        import sys
+        logging.basicConfig(stream=sys.stderr)
+except (ImportError,), exc:
+    raise
+
+
+try:
+    from redakcja.localsettings import *
+except ImportError:
+    pass
diff --git a/src/redakcja/settings/common.py b/src/redakcja/settings/common.py
new file mode 100644 (file)
index 0000000..6c53d44
--- /dev/null
@@ -0,0 +1,154 @@
+# -*- coding: utf-8 -*-
+from __future__ import absolute_import
+import os.path
+
+PROJECT_ROOT = os.path.realpath(os.path.dirname(os.path.dirname(__file__)))
+
+DEBUG = False
+TEMPLATE_DEBUG = DEBUG
+
+MAINTENANCE_MODE = False
+
+ADMINS = (
+    (u'Radek Czajka', 'radoslaw.czajka@nowoczesnapolska.org.pl'),
+)
+
+MANAGERS = ADMINS
+
+# Local time zone for this installation. Choices can be found here:
+# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
+# although not all choices may be available on all operating systems.
+# If running in a Windows environment this must be set to the same as your
+# system time zone.
+TIME_ZONE = 'Europe/Warsaw'
+
+# Language code for this installation. All choices can be found here:
+# http://www.i18nguy.com/unicode/language-identifiers.html
+LANGUAGE_CODE = 'pl'
+
+#import locale
+#locale.setlocale(locale.LC_ALL, '')
+
+SITE_ID = 1
+
+# If you set this to False, Django will make some optimizations so as not
+# to load the internationalization machinery.
+USE_I18N = True
+USE_L10N = True
+
+
+# Absolute path to the directory that holds media.
+# Example: "/home/media/media.lawrence.com/"
+MEDIA_ROOT = PROJECT_ROOT + '/media/dynamic'
+STATIC_ROOT = PROJECT_ROOT + '/../static/'
+
+STATICFILES_DIRS = [
+    PROJECT_ROOT + '/static/'
+]
+
+# URL that handles the media served from MEDIA_ROOT. Make sure to use a
+# trailing slash if there is a path component (optional in other cases).
+# Examples: "http://media.lawrence.com", "http://example.com/media/"
+MEDIA_URL = '/media/dynamic/'
+STATIC_URL = '/media/static/'
+
+SESSION_COOKIE_NAME = "redakcja_sessionid"
+
+# List of callables that know how to import templates from various sources.
+TEMPLATE_LOADERS = (
+    'django.template.loaders.filesystem.Loader',
+    'django.template.loaders.app_directories.Loader',
+)
+
+TEMPLATE_CONTEXT_PROCESSORS = (
+    "django.contrib.auth.context_processors.auth",
+    "django.core.context_processors.debug",
+    "django.core.context_processors.i18n",
+    "redakcja.context_processors.settings", # this is instead of media
+    'django.core.context_processors.csrf',
+    "django.core.context_processors.request",
+)
+
+
+MIDDLEWARE_CLASSES = (
+    'django.middleware.common.CommonMiddleware',
+    'django.middleware.csrf.CsrfViewMiddleware',
+    'django.contrib.sessions.middleware.SessionMiddleware',
+    'django.contrib.messages.middleware.MessageMiddleware',
+
+    'django.contrib.auth.middleware.AuthenticationMiddleware',
+    'django_cas.middleware.CASMiddleware',
+
+    'django.contrib.admindocs.middleware.XViewMiddleware',
+    'pagination.middleware.PaginationMiddleware',
+    'maintenancemode.middleware.MaintenanceModeMiddleware',
+)
+
+AUTHENTICATION_BACKENDS = (
+    'django.contrib.auth.backends.ModelBackend',
+    'fnpdjango.auth_backends.AttrCASBackend',
+)
+
+ROOT_URLCONF = 'redakcja.urls'
+
+TEMPLATE_DIRS = (
+    PROJECT_ROOT + '/templates',
+)
+
+FIREPYTHON_LOGGER_NAME = "fnp"
+
+INSTALLED_APPS = (
+    'django.contrib.auth',
+    'django.contrib.contenttypes',
+    'django.contrib.sessions',
+    'django.contrib.messages',
+    'django.contrib.staticfiles',
+    'django.contrib.sites',
+    'django.contrib.admin',
+    'django.contrib.admindocs',
+    'django.contrib.comments',
+    'raven.contrib.django.raven_compat',
+
+    'south',
+    'sorl.thumbnail',
+    'pagination',
+    'django_gravatar',
+    'fileupload',
+    'kombu.transport.django',
+    'pipeline',
+    'fnpdjango',
+
+    'catalogue',
+    'cover',
+    'dvcs',
+    'wiki',
+    'wiki_img',
+    'toolbar',
+    'apiclient',
+    'email_mangler',
+)
+
+LOGIN_REDIRECT_URL = '/documents/user'
+
+CAS_USER_ATTRS_MAP = {
+    'email': 'email', 'firstname': 'first_name', 'lastname': 'last_name'}
+
+# REPOSITORY_PATH = '/Users/zuber/Projekty/platforma/files/books'
+
+IMAGE_DIR = 'images/'
+
+
+BROKER_URL = 'django://'
+CELERY_EAGER_PROPAGATES_EXCEPTIONS = True
+CELERY_SEND_TASK_ERROR_EMAILS = True
+CELERY_ACCEPT_CONTENT = ['pickle']  # Remove when all tasks jsonable.
+
+SHOW_APP_VERSION = False
+
+MIN_COVER_SIZE = (915, 1270)
+
+try:
+    from redakcja.settings.compress import *
+except ImportError:
+    pass
+
diff --git a/src/redakcja/settings/compress.py b/src/redakcja/settings/compress.py
new file mode 100644 (file)
index 0000000..9bee670
--- /dev/null
@@ -0,0 +1,146 @@
+STATICFILES_FINDERS = (
+    'django.contrib.staticfiles.finders.FileSystemFinder',
+    'django.contrib.staticfiles.finders.AppDirectoriesFinder',
+#    'django.contrib.staticfiles.finders.DefaultStorageFinder',
+    'pipeline.finders.PipelineFinder',
+)
+
+
+STATICFILES_STORAGE = 'pipeline.storage.PipelineCachedStorage'
+PIPELINE_CSS_COMPRESSOR = None
+PIPELINE_JS_COMPRESSOR = None
+PIPELINE_STORAGE = 'pipeline.storage.PipelineFinderStorage'
+
+
+# CSS and JS files to compress
+PIPELINE_CSS = {
+    'detail': {
+         'source_filenames': (
+            'css/master.css',
+            'css/toolbar.css',
+            'css/gallery.css',
+            'css/history.css',
+            'css/summary.css',
+            'css/html.css',
+            'css/jquery.autocomplete.css',
+            'css/imgareaselect-default.css', #img!
+            'css/dialogs.css',
+        ),
+        'output_filename': 'compressed/detail_styles.css',
+    },
+    'catalogue': {
+        'source_filenames': (
+            'css/filelist.css',
+        ),
+        'output_filename': 'compressed/catalogue_styles.css',
+     },
+     'book': {
+        'source_filenames': (
+            'css/book.css',
+        ),
+        'output_filename': 'compressed/book.css',
+    },
+    'book_list': {
+        'source_filenames': (
+            'contextmenu/jquery.contextMenu.css',
+            'css/book_list.css',
+        ),
+        'output_filename': 'compressed/book_list.css',
+    },
+}
+
+PIPELINE_JS = {
+    # everything except codemirror
+    'detail': {
+        'source_filenames': (
+                # libraries
+                'js/lib/jquery/jquery.autocomplete.js',
+                'js/lib/jquery/jquery.blockui.js',
+                'js/lib/jquery/jquery.elastic.js',
+                'js/lib/jquery/jquery.xmlns.js',
+                'js/button_scripts.js',
+                'js/slugify.js',
+
+                # wiki scripts
+                'js/wiki/wikiapi.js',
+                'js/wiki/xslt.js',
+
+                # base UI
+                'js/wiki/base.js',
+                'js/wiki/toolbar.js',
+
+                # dialogs
+                'js/wiki/dialog_save.js',
+                'js/wiki/dialog_revert.js',
+                'js/wiki/dialog_pubmark.js',
+
+                # views
+                'js/wiki/view_history.js',
+                'js/wiki/view_summary.js',
+                'js/wiki/view_editor_source.js',
+                'js/wiki/view_editor_wysiwyg.js',
+                'js/wiki/view_gallery.js',
+                'js/wiki/view_annotations.js',
+                'js/wiki/view_search.js',
+                'js/wiki/view_column_diff.js',
+        ),
+        'output_filename': 'compressed/detail_scripts.js',
+     },
+    'wiki_img': {
+        'source_filenames': (
+                # libraries
+                'js/lib/jquery-1.4.2.min.js',
+                'js/lib/jquery/jquery.autocomplete.js',
+                'js/lib/jquery/jquery.blockui.js',
+                'js/lib/jquery/jquery.elastic.js',
+                'js/lib/jquery/jquery.imgareaselect.js',
+                'js/button_scripts.js',
+                'js/slugify.js',
+
+                # wiki scripts
+                'js/wiki_img/wikiapi.js',
+
+                # base UI
+                'js/wiki_img/base.js',
+                'js/wiki/toolbar.js',
+
+                # dialogs
+                'js/wiki/dialog_save.js',
+                'js/wiki/dialog_revert.js',
+                'js/wiki/dialog_pubmark.js',
+
+                # views
+                'js/wiki_img/view_editor_objects.js',
+                'js/wiki_img/view_editor_motifs.js',
+                'js/wiki/view_editor_source.js',
+                'js/wiki/view_history.js',
+                'js/wiki/view_column_diff.js',
+        ),
+        'output_filename': 'compressed/detail_img_scripts.js',
+     },
+    'catalogue': {
+        'source_filenames': (
+                'js/catalogue/catalogue.js',
+                'js/slugify.js',
+                'email_mangler/email_mangler.js',
+        ),
+        'output_filename': 'compressed/catalogue_scripts.js',
+     },
+     'book': {
+        'source_filenames': (
+            'js/book_text/jquery.eventdelegation.js',
+            'js/book_text/jquery.scrollto.js',
+            'js/book_text/jquery.highlightfade.js',
+            'js/book_text/book.js',
+        ),
+        'output_filename': 'compressed/book.js',
+         },
+    'book_list': {
+        'source_filenames': (
+            'contextmenu/jquery.ui.position.js',
+            'contextmenu/jquery.contextMenu.js',
+            'js/catalogue/book_list.js',
+        ),
+        'output_filename': 'compressed/book_list.js',
+    }
+}
diff --git a/src/redakcja/settings/integration_test.py b/src/redakcja/settings/integration_test.py
new file mode 100644 (file)
index 0000000..ba477bb
--- /dev/null
@@ -0,0 +1,7 @@
+from redakcja.settings.test import *
+
+NOSE_ARGS = ()
+
+STATIC_ROOT_SYMLINK = os.path.dirname(STATIC_ROOT) + '_test'
+STATICFILES_DIRS.append(STATIC_ROOT_SYMLINK)
+
diff --git a/src/redakcja/settings/test.py b/src/redakcja/settings/test.py
new file mode 100644 (file)
index 0000000..cd13178
--- /dev/null
@@ -0,0 +1,44 @@
+#
+# Nose tests
+#
+
+from redakcja.settings.common import *
+
+# ROOT_URLCONF = 'yourapp.settings.test.urls'
+
+DATABASES = {
+    'default': {
+        'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
+        'NAME': '', # Or path to database file if using sqlite3.
+        'USER': '',                      # Not used with sqlite3.
+        'PASSWORD': '',                  # Not used with sqlite3.
+        'HOST': '',                      # Set to empty string for localhost. Not used with sqlite3.
+        'PORT': '',                      # Set to empty string for default. Not used with sqlite3.
+    }
+}
+
+import tempfile
+
+CATALOGUE_REPO_PATH = tempfile.mkdtemp(prefix='redakcja-repo')
+CATALOGUE_IMAGE_REPO_PATH = tempfile.mkdtemp(prefix='redakcja-repo-img')
+MEDIA_ROOT = tempfile.mkdtemp(prefix='media-root')
+CELERY_ALWAYS_EAGER = True
+
+INSTALLED_APPS += ('django_nose', 'dvcs.tests')
+
+TEST_RUNNER = 'django_nose.NoseTestSuiteRunner'
+TEST_MODULES = ('catalogue', 'cover', 'dvcs.tests', 'wiki', 'toolbar')
+COVER_APPS = ('catalogue', 'cover', 'dvcs', 'wiki', 'toolbar')
+NOSE_ARGS = (
+    '--tests=' + ','.join(TEST_MODULES),
+    '--cover-package=' + ','.join(COVER_APPS),
+    '-d',
+    '--with-doctest',
+    '--with-xunit',
+    '--with-xcoverage',
+)
+
+SECRET_KEY = "not-so-secret"
+
+
+LITERARY_DIRECTOR_USERNAME = 'Kaowiec'
diff --git a/src/redakcja/static/contextmenu/images/cut.png b/src/redakcja/static/contextmenu/images/cut.png
new file mode 100755 (executable)
index 0000000..f215d6f
Binary files /dev/null and b/src/redakcja/static/contextmenu/images/cut.png differ
diff --git a/src/redakcja/static/contextmenu/images/door.png b/src/redakcja/static/contextmenu/images/door.png
new file mode 100755 (executable)
index 0000000..369fc46
Binary files /dev/null and b/src/redakcja/static/contextmenu/images/door.png differ
diff --git a/src/redakcja/static/contextmenu/images/page_white_copy.png b/src/redakcja/static/contextmenu/images/page_white_copy.png
new file mode 100755 (executable)
index 0000000..a9f31a2
Binary files /dev/null and b/src/redakcja/static/contextmenu/images/page_white_copy.png differ
diff --git a/src/redakcja/static/contextmenu/images/page_white_delete.png b/src/redakcja/static/contextmenu/images/page_white_delete.png
new file mode 100755 (executable)
index 0000000..af1ecaf
Binary files /dev/null and b/src/redakcja/static/contextmenu/images/page_white_delete.png differ
diff --git a/src/redakcja/static/contextmenu/images/page_white_edit.png b/src/redakcja/static/contextmenu/images/page_white_edit.png
new file mode 100755 (executable)
index 0000000..b93e776
Binary files /dev/null and b/src/redakcja/static/contextmenu/images/page_white_edit.png differ
diff --git a/src/redakcja/static/contextmenu/images/page_white_paste.png b/src/redakcja/static/contextmenu/images/page_white_paste.png
new file mode 100755 (executable)
index 0000000..5b2cbb3
Binary files /dev/null and b/src/redakcja/static/contextmenu/images/page_white_paste.png differ
diff --git a/src/redakcja/static/contextmenu/index.html b/src/redakcja/static/contextmenu/index.html
new file mode 100644 (file)
index 0000000..f099c95
--- /dev/null
@@ -0,0 +1,200 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">\r
+\r
+<html xmlns="http://www.w3.org/1999/xhtml">\r
+\r
+       <head>\r
+               <title>jQuery Context Menu Plugin Demo</title>\r
+               <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />\r
+               \r
+               <style type="text/css">\r
+                       BODY,\r
+                       HTML {\r
+                               padding: 0px;\r
+                               margin: 0px;\r
+                       }\r
+                       BODY {\r
+                               font-family: Verdana, Arial, Helvetica, sans-serif;\r
+                               font-size: 11px;\r
+                               background: #FFF;\r
+                               padding: 15px;\r
+                       }\r
+                       \r
+                       H1 {\r
+                               font-family: Georgia, serif;\r
+                               font-size: 20px;\r
+                               font-weight: normal;\r
+                       }\r
+                       \r
+                       H2 {\r
+                               font-family: Georgia, serif;\r
+                               font-size: 16px;\r
+                               font-weight: normal;\r
+                               margin: 0px 0px 10px 0px;\r
+                       }\r
+                       \r
+                       #myDiv {\r
+                               width: 150px;\r
+                               border: solid 1px #2AA7DE;\r
+                               background: #6CC8EF;\r
+                               text-align: center;\r
+                               padding: 4em .5em;\r
+                               margin: 1em;\r
+                               float: left;\r
+                       }\r
+                       \r
+                       #myList {\r
+                               margin: 1em;\r
+                               float: left;\r
+                       }\r
+                       \r
+                       #myList UL {\r
+                               padding: 0px;\r
+                               margin: 0em 1em;\r
+                       }\r
+                       \r
+                       #myList LI {\r
+                               width: 100px;\r
+                               border: solid 1px #2AA7DE;\r
+                               background: #6CC8EF;\r
+                               padding: 5px 5px;\r
+                               margin: 2px 0px;\r
+                               list-style: none;\r
+                       }\r
+                       \r
+                       #options {\r
+                               clear: left;\r
+                       }\r
+                       \r
+                       #options INPUT {\r
+                               font-family: Verdana, Arial, Helvetica, sans-serif;\r
+                               font-size: 11px;\r
+                               width: 150px;\r
+                       }\r
+                       \r
+               </style>                \r
+               \r
+               <script src="../js/lib/jquery-1.7.2.min.js" type="text/javascript"></script>\r
+               <script src="jquery.contextMenu.js" type="text/javascript"></script>\r
+               <link href="jquery.contextMenu.css" rel="stylesheet" type="text/css" />\r
+               \r
+               <script type="text/javascript">\r
+                       \r
+                       $(document).ready( function() {\r
+                               \r
+                               // Show menu when #myDiv is clicked\r
+                               $("#myDiv").contextMenu({\r
+                                       menu: 'myMenu'\r
+                               },\r
+                                       function(action, el, pos) {\r
+                                       alert(\r
+                                               'Action: ' + action + '\n\n' +\r
+                                               'Element ID: ' + $(el).attr('id') + '\n\n' + \r
+                                               'X: ' + pos.x + '  Y: ' + pos.y + ' (relative to element)\n\n' + \r
+                                               'X: ' + pos.docX + '  Y: ' + pos.docY+ ' (relative to document)'\r
+                                               );\r
+                               });\r
+                               \r
+                               // Show menu when a list item is clicked\r
+                               $("#myList UL LI").contextMenu({\r
+                                       menu: 'myMenu'\r
+                               }, function(action, el, pos) {\r
+                                       alert(\r
+                                               'Action: ' + action + '\n\n' +\r
+                                               'Element text: ' + $(el).text() + '\n\n' + \r
+                                               'X: ' + pos.x + '  Y: ' + pos.y + ' (relative to element)\n\n' + \r
+                                               'X: ' + pos.docX + '  Y: ' + pos.docY+ ' (relative to document)'\r
+                                               );\r
+                               });\r
+                               \r
+                               // Disable menus\r
+                               $("#disableMenus").click( function() {\r
+                                       $('#myDiv, #myList UL LI').disableContextMenu();\r
+                                       $(this).attr('disabled', true);\r
+                                       $("#enableMenus").attr('disabled', false);\r
+                               });\r
+                               \r
+                               // Enable menus\r
+                               $("#enableMenus").click( function() {\r
+                                       $('#myDiv, #myList UL LI').enableContextMenu();\r
+                                       $(this).attr('disabled', true);\r
+                                       $("#disableMenus").attr('disabled', false);\r
+                               });\r
+                               \r
+                               // Disable cut/copy\r
+                               $("#disableItems").click( function() {\r
+                                       $('#myMenu').disableContextMenuItems('#cut,#copy');\r
+                                       $(this).attr('disabled', true);\r
+                                       $("#enableItems").attr('disabled', false);\r
+                               });\r
+                               \r
+                               // Enable cut/copy\r
+                               $("#enableItems").click( function() {\r
+                                       $('#myMenu').enableContextMenuItems('#cut,#copy');\r
+                                       $(this).attr('disabled', true);\r
+                                       $("#disableItems").attr('disabled', false);\r
+                               });                             \r
+                               \r
+                       });\r
+                       \r
+               </script>\r
+       </head>\r
+       \r
+       <body>\r
+               \r
+               <h1>jQuery Context Menu Plugin Demo</h1>\r
+               <p>\r
+                       This plugin lets you add context menu functionality to your web applications.\r
+               </p>\r
+               \r
+               <p>\r
+                       <strong>Tip:</strong> Try using your keyboard to make a selection.\r
+               </p>\r
+               \r
+               <p>\r
+                       <a href="http://abeautifulsite.net/2008/09/jquery-context-menu-plugin/">Back to the project page</a>\r
+               </p>\r
+               \r
+               <h2>Demo</h2>\r
+               \r
+               <div id="myDiv">\r
+                       Right click to view the context menu\r
+               </div>\r
+               \r
+               <div id="myList">\r
+                       <ul>\r
+                               <li>Item 1</li>\r
+                               <li>Item 2</li>\r
+                               <li>Item 3</li>\r
+                               <li>Item 4</li>\r
+                               <li>Item 5</li>\r
+                               <li>Item 6</li>\r
+                       </ul>\r
+               </div>\r
+               \r
+               <div id="options">\r
+                       <p>\r
+                               <input type="button" id="disableItems" value="Disable Cut/Copy" />\r
+                               <input type="button" id="enableItems" value="Enable Cut/Copy" disabled="disabled" />\r
+                       </p>\r
+                       \r
+                       <p>\r
+                               <input type="button" id="disableMenus" value="Disable Context Menus" />\r
+                               <input type="button" id="enableMenus" value="Enable Context Menus" disabled="disabled" />\r
+                       </p>\r
+               </div>\r
+               \r
+               <ul id="myMenu" class="contextMenu">\r
+                       <li class="edit"><a href="#edit">Edit</a></li>\r
+                       <li class="cut separator"><a href="#cut">Cut</a>\r
+                         <ul>\r
+                           <li class="pe"><a href="#pe">Pe</a></li>\r
+                         </ul>\r
+                       </li>\r
+                       <li class="copy"><a href="#copy">Copy</a></li>\r
+                       <li class="paste"><a href="#paste">Paste</a></li>\r
+                       <li class="delete"><a href="#delete">Delete</a></li>\r
+                       <li class="quit separator"><a href="#quit">Quit</a></li>\r
+               </ul>\r
+               \r
+       </body>\r
+</html>\r
diff --git a/src/redakcja/static/contextmenu/jquery.contextMenu.css b/src/redakcja/static/contextmenu/jquery.contextMenu.css
new file mode 100755 (executable)
index 0000000..46af248
--- /dev/null
@@ -0,0 +1,134 @@
+/*!\r
+ * jQuery contextMenu - Plugin for simple contextMenu handling\r
+ *\r
+ * Version: 1.5.13\r
+ *\r
+ * Authors: Rodney Rehm, Addy Osmani (patches for FF)\r
+ * Web: http://medialize.github.com/jQuery-contextMenu/\r
+ *\r
+ * Licensed under\r
+ *   MIT License http://www.opensource.org/licenses/mit-license\r
+ *   GPL v3 http://opensource.org/licenses/GPL-3.0\r
+ *\r
+ */\r
+\r
+.context-menu-list {\r
+    margin:0; \r
+    padding:0;\r
+    \r
+    min-width: 120px;\r
+    max-width: 250px;\r
+    display: inline-block;\r
+    position: absolute;\r
+    list-style-type: none;\r
+    \r
+    border: 1px solid #DDD;\r
+    background: #EEE;\r
+    \r
+    -webkit-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.5);\r
+       -moz-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.5);\r
+        -ms-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.5);\r
+         -o-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.5);\r
+            box-shadow: 0 2px 5px rgba(0, 0, 0, 0.5);\r
+    \r
+    font-family: Verdana, Arial, Helvetica, sans-serif;\r
+    font-size: 11px;\r
+}\r
+\r
+.context-menu-item {\r
+    padding: 2px 2px 2px 24px;\r
+    background-color: #EEE;\r
+    position: relative;\r
+    -moz-user-select: -moz-none;\r
+}\r
+\r
+.context-menu-separator {\r
+    padding-bottom:0;\r
+    border-bottom: 1px solid #DDD;\r
+}\r
+\r
+.context-menu-item > label {\r
+    -moz-user-select: text;\r
+}\r
+\r
+.context-menu-item.hover {\r
+    cursor: pointer;\r
+    background-color: #39F;\r
+}\r
+\r
+.context-menu-item.disabled {\r
+    color: #666;\r
+}\r
+\r
+.context-menu-input.hover,\r
+.context-menu-item.disabled.hover {\r
+    cursor: default;\r
+    background-color: #EEE;\r
+}\r
+\r
+.context-menu-submenu:after {\r
+    content: ">";\r
+    color: #666;\r
+    position: absolute;\r
+    top: 0;\r
+    right: 3px;\r
+    z-index: 1;\r
+}\r
+\r
+/* icons\r
+    #protip:\r
+    In case you want to use sprites for icons (which I would suggest you do) have a look at\r
+    http://css-tricks.com/13224-pseudo-spriting/ to get an idea of how to implement \r
+    .context-menu-item.icon:before {}\r
+ */\r
+.context-menu-item.icon { min-height: 18px; background-repeat: no-repeat; background-position: 4px 2px; }\r
+.context-menu-item.icon-edit { background-image: url(images/page_white_edit.png); }\r
+.context-menu-item.icon-cut { background-image: url(images/cut.png); }\r
+.context-menu-item.icon-copy { background-image: url(images/page_white_copy.png); }\r
+.context-menu-item.icon-paste { background-image: url(images/page_white_paste.png); }\r
+.context-menu-item.icon-delete { background-image: url(images/page_white_delete.png); }\r
+.context-menu-item.icon-quit { background-image: url(images/door.png); }\r
+\r
+/* vertically align inside labels */\r
+.context-menu-input > label > * { vertical-align: top; }\r
+\r
+/* position checkboxes and radios as icons */\r
+.context-menu-input > label > input[type="checkbox"],\r
+.context-menu-input > label > input[type="radio"] {\r
+    margin-left: -17px;\r
+}\r
+.context-menu-input > label > span {\r
+    margin-left: 5px;\r
+}\r
+\r
+.context-menu-input > label,\r
+.context-menu-input > label > input[type="text"],\r
+.context-menu-input > label > textarea,\r
+.context-menu-input > label > select {\r
+    display: block;\r
+    width: 100%;\r
+    \r
+    -webkit-box-sizing: border-box;\r
+       -moz-box-sizing: border-box;\r
+        -ms-box-sizing: border-box;\r
+         -o-box-sizing: border-box;\r
+            box-sizing: border-box;\r
+}\r
+\r
+.context-menu-input > label > textarea {\r
+    height: 100px;\r
+}\r
+.context-menu-item > .context-menu-list {\r
+    display: none;\r
+    /* re-positioned by js */\r
+    right: -5px;\r
+    top: 5px;\r
+}\r
+\r
+.context-menu-item.hover > .context-menu-list {\r
+    display: block;\r
+}\r
+\r
+.context-menu-accesskey {\r
+    text-decoration: underline;\r
+}\r
diff --git a/src/redakcja/static/contextmenu/jquery.contextMenu.js b/src/redakcja/static/contextmenu/jquery.contextMenu.js
new file mode 100755 (executable)
index 0000000..53c3fe9
--- /dev/null
@@ -0,0 +1,1485 @@
+/*!\r
+ * jQuery contextMenu - Plugin for simple contextMenu handling\r
+ *\r
+ * Version: 1.5.13\r
+ *\r
+ * Authors: Rodney Rehm, Addy Osmani (patches for FF)\r
+ * Web: http://medialize.github.com/jQuery-contextMenu/\r
+ *\r
+ * Licensed under\r
+ *   MIT License http://www.opensource.org/licenses/mit-license\r
+ *   GPL v3 http://opensource.org/licenses/GPL-3.0\r
+ *\r
+ */\r
+\r
+(function($, undefined){\r
+    \r
+    // TODO: -\r
+        // ARIA stuff: menuitem, menuitemcheckbox und menuitemradio\r
+        // create <menu> structure if $.support[htmlCommand || htmlMenuitem] and !opt.disableNative\r
+\r
+// determine html5 compatibility\r
+$.support.htmlMenuitem = ('HTMLMenuItemElement' in window);\r
+$.support.htmlCommand = ('HTMLCommandElement' in window);\r
+\r
+var // currently active contextMenu trigger\r
+    $currentTrigger = null,\r
+    // is contextMenu initialized with at least one menu?\r
+    initialized = false,\r
+    // window handle\r
+    $win = $(window),\r
+    // number of registered menus\r
+    counter = 0,\r
+    // mapping selector to namespace\r
+    namespaces = {},\r
+    // mapping namespace to options\r
+    menus = {},\r
+    // custom command type handlers\r
+    types = {},\r
+    // default values\r
+    defaults = {\r
+        // selector of contextMenu trigger\r
+        selector: null,\r
+        // where to append the menu to\r
+        appendTo: null,\r
+        // method to trigger context menu ["right", "left", "hover"]\r
+        trigger: "right",\r
+        // hide menu when mouse leaves trigger / menu elements\r
+        autoHide: false,\r
+        // ms to wait before showing a hover-triggered context menu\r
+        delay: 200,\r
+        // determine position to show menu at\r
+        determinePosition: function($menu) {\r
+            // position to the lower middle of the trigger element\r
+            if ($.ui && $.ui.position) {\r
+                // .position() is provided as a jQuery UI utility\r
+                // (...and it won't work on hidden elements)\r
+                $menu.css('display', 'block').position({\r
+                    my: "center top",\r
+                    at: "center bottom",\r
+                    of: this,\r
+                    offset: "0 5",\r
+                    collision: "fit"\r
+                }).css('display', 'none');\r
+            } else {\r
+                // determine contextMenu position\r
+                var offset = this.offset();\r
+                offset.top += this.outerHeight();\r
+                offset.left += this.outerWidth() / 2 - $menu.outerWidth() / 2;\r
+                $menu.css(offset);\r
+            }\r
+        },\r
+        // position menu\r
+        position: function(opt, x, y) {\r
+            var $this = this,\r
+                offset;\r
+            // determine contextMenu position\r
+            if (!x && !y) {\r
+                opt.determinePosition.call(this, opt.$menu);\r
+                return;\r
+            } else if (x === "maintain" && y === "maintain") {\r
+                // x and y must not be changed (after re-show on command click)\r
+                offset = opt.$menu.position();\r
+            } else {\r
+                // x and y are given (by mouse event)\r
+                var triggerIsFixed = opt.$trigger.parents().andSelf()\r
+                    .filter(function() {\r
+                        return $(this).css('position') == "fixed";\r
+                    }).length;\r
+\r
+                if (triggerIsFixed) {\r
+                    y -= $win.scrollTop();\r
+                    x -= $win.scrollLeft();\r
+                }\r
+                offset = {top: y, left: x};\r
+            }\r
+            \r
+            // correct offset if viewport demands it\r
+            var bottom = $win.scrollTop() + $win.height(),\r
+                right = $win.scrollLeft() + $win.width(),\r
+                height = opt.$menu.height(),\r
+                width = opt.$menu.width();\r
+            \r
+            if (offset.top + height > bottom) {\r
+                offset.top -= height;\r
+            }\r
+            \r
+            if (offset.left + width > right) {\r
+                offset.left -= width;\r
+            }\r
+            \r
+            opt.$menu.css(offset);\r
+        },\r
+        // position the sub-menu\r
+        positionSubmenu: function($menu) {\r
+            if ($.ui && $.ui.position) {\r
+                // .position() is provided as a jQuery UI utility\r
+                // (...and it won't work on hidden elements)\r
+                $menu.css('display', 'block').position({\r
+                    my: "left top",\r
+                    at: "right top",\r
+                    of: this,\r
+                    collision: "fit"\r
+                }).css('display', '');\r
+            } else {\r
+                // determine contextMenu position\r
+                var offset = this.offset();\r
+                offset.top += 0;\r
+                offset.left += this.outerWidth();\r
+                $menu.css(offset);\r
+            }\r
+        },\r
+        // offset to add to zIndex\r
+        zIndex: 1,\r
+        // show hide animation settings\r
+        animation: {\r
+            duration: 50,\r
+            show: 'slideDown',\r
+            hide: 'slideUp'\r
+        },\r
+        // events\r
+        events: {\r
+            show: $.noop,\r
+            hide: $.noop\r
+        },\r
+        // default callback\r
+        callback: null,\r
+        // list of contextMenu items\r
+        items: {}\r
+    },\r
+    // mouse position for hover activation\r
+    hoveract = {\r
+        timer: null,\r
+        pageX: null,\r
+        pageY: null\r
+    },\r
+    // determine zIndex\r
+    zindex = function($t) {\r
+        var zin = 0,\r
+            $tt = $t;\r
+\r
+        while (true) {\r
+            zin = Math.max(zin, parseInt($tt.css('z-index'), 10) || 0);\r
+            $tt = $tt.parent();\r
+            if (!$tt || !$tt.length || $tt.prop('nodeName').toLowerCase() == 'body') {\r
+                break;\r
+            }\r
+        }\r
+        \r
+        return zin;\r
+    },\r
+    // event handlers\r
+    handle = {\r
+        // abort anything\r
+        abortevent: function(e){\r
+            e.preventDefault();\r
+            e.stopImmediatePropagation();\r
+        },\r
+        \r
+        // contextmenu show dispatcher\r
+        contextmenu: function(e) {\r
+            var $this = $(this);\r
+            \r
+            // disable actual context-menu\r
+            e.preventDefault();\r
+            e.stopImmediatePropagation();\r
+            \r
+            // abort native-triggered events unless we're triggering on right click\r
+            if (e.data.trigger != 'right' && e.originalEvent) {\r
+                return;\r
+            }\r
+            \r
+            if (!$this.hasClass('context-menu-disabled')) {\r
+                // theoretically need to fire a show event at <menu>\r
+                // http://www.whatwg.org/specs/web-apps/current-work/multipage/interactive-elements.html#context-menus\r
+                // var evt = jQuery.Event("show", { data: data, pageX: e.pageX, pageY: e.pageY, relatedTarget: this });\r
+                // e.data.$menu.trigger(evt);\r
+                \r
+                $currentTrigger = $this;\r
+                if (e.data.build) {\r
+                    var built = e.data.build($currentTrigger, e);\r
+                    // abort if build() returned false\r
+                    if (built === false) {\r
+                        return;\r
+                    }\r
+                    \r
+                    // dynamically build menu on invocation\r
+                    e.data = $.extend(true, defaults, e.data, built || {});\r
+\r
+                    // abort if there are no items to display\r
+                    if (!e.data.items || $.isEmptyObject(e.data.items)) {\r
+                        // Note: jQuery captures and ignores errors from event handlers\r
+                        if (window.console) {\r
+                            (console.error || console.log)("No items specified to show in contextMenu");\r
+                        }\r
+                        \r
+                        throw new Error('No Items sepcified');\r
+                    }\r
+                    \r
+                    // backreference for custom command type creation\r
+                    e.data.$trigger = $currentTrigger;\r
+                    \r
+                    op.create(e.data);\r
+                }\r
+                // show menu\r
+                op.show.call($this, e.data, e.pageX, e.pageY);\r
+            }\r
+        },\r
+        // contextMenu left-click trigger\r
+        click: function(e) {\r
+            e.preventDefault();\r
+            e.stopImmediatePropagation();\r
+            $(this).trigger(jQuery.Event("contextmenu", { data: e.data, pageX: e.pageX, pageY: e.pageY }));\r
+        },\r
+        // contextMenu right-click trigger\r
+        mousedown: function(e) {\r
+            // register mouse down\r
+            var $this = $(this);\r
+            \r
+            // hide any previous menus\r
+            if ($currentTrigger && $currentTrigger.length && !$currentTrigger.is($this)) {\r
+                $currentTrigger.data('contextMenu').$menu.trigger('contextmenu:hide');\r
+            }\r
+            \r
+            // activate on right click\r
+            if (e.button == 2) {\r
+                $currentTrigger = $this.data('contextMenuActive', true);\r
+            }\r
+        },\r
+        // contextMenu right-click trigger\r
+        mouseup: function(e) {\r
+            // show menu\r
+            var $this = $(this);\r
+            if ($this.data('contextMenuActive') && $currentTrigger && $currentTrigger.length && $currentTrigger.is($this) && !$this.hasClass('context-menu-disabled')) {\r
+                e.preventDefault();\r
+                e.stopImmediatePropagation();\r
+                $currentTrigger = $this;\r
+                $this.trigger(jQuery.Event("contextmenu", { data: e.data, pageX: e.pageX, pageY: e.pageY }));\r
+            }\r
+            \r
+            $this.removeData('contextMenuActive');\r
+        },\r
+        // contextMenu hover trigger\r
+        mouseenter: function(e) {\r
+            var $this = $(this),\r
+                $related = $(e.relatedTarget),\r
+                $document = $(document);\r
+            \r
+            // abort if we're coming from a menu\r
+            if ($related.is('.context-menu-list') || $related.closest('.context-menu-list').length) {\r
+                return;\r
+            }\r
+            \r
+            // abort if a menu is shown\r
+            if ($currentTrigger && $currentTrigger.length) {\r
+                return;\r
+            }\r
+            \r
+            hoveract.pageX = e.pageX;\r
+            hoveract.pageY = e.pageY;\r
+            hoveract.data = e.data;\r
+            $document.on('mousemove.contextMenuShow', handle.mousemove);\r
+            hoveract.timer = setTimeout(function() {\r
+                hoveract.timer = null;\r
+                $document.off('mousemove.contextMenuShow');\r
+                $currentTrigger = $this;\r
+                $this.trigger(jQuery.Event("contextmenu", { data: hoveract.data, pageX: hoveract.pageX, pageY: hoveract.pageY }));\r
+            }, e.data.delay );\r
+        },\r
+        // contextMenu hover trigger\r
+        mousemove: function(e) {\r
+            hoveract.pageX = e.pageX;\r
+            hoveract.pageY = e.pageY;\r
+        },\r
+        // contextMenu hover trigger\r
+        mouseleave: function(e) {\r
+            // abort if we're leaving for a menu\r
+            var $related = $(e.relatedTarget);\r
+            if ($related.is('.context-menu-list') || $related.closest('.context-menu-list').length) {\r
+                return;\r
+            }\r
+            \r
+            try {\r
+                clearTimeout(hoveract.timer);\r
+            } catch(e) {}\r
+            \r
+            hoveract.timer = null;\r
+        },\r
+        \r
+        // click on layer to hide contextMenu\r
+        layerClick: function(e) {\r
+            var $this = $(this),\r
+                root = $this.data('contextMenuRoot');\r
+                \r
+            e.preventDefault();\r
+            e.stopImmediatePropagation();\r
+            \r
+            if ((root.trigger == 'left' && e.button == 0) || (root.trigger == 'right' && e.button == 2)) {\r
+                var offset = root.$trigger.offset();\r
+                \r
+                // while this looks kinda awful, it's the best way to avoid\r
+                // unnecessarily calculating any positions\r
+                offset.top += $(window).scrollTop();\r
+                if (offset.top <= e.pageY) {\r
+                    offset.left += $(window).scrollLeft();\r
+                    if (offset.left <= e.pageX) {\r
+                        offset.bottom = offset.top + root.$trigger.outerHeight();\r
+                        if (offset.bottom >= e.pageY) {\r
+                            offset.right = offset.left + root.$trigger.outerWidth();\r
+                            if (offset.right >= e.pageX) {\r
+                                // reposition\r
+                                root.position.call(root.$trigger, root, e.pageX, e.pageY);\r
+                                return;\r
+                            }\r
+                        }\r
+                    }\r
+                }\r
+            } \r
+            \r
+            // remove only after mouseup has completed\r
+            $this.on('mouseup', function(e) {\r
+                e.preventDefault();\r
+                e.stopImmediatePropagation();\r
+                root.$menu.trigger('contextmenu:hide');\r
+            });\r
+        },\r
+        // key handled :hover\r
+        keyStop: function(e, opt) {\r
+            if (!opt.isInput) {\r
+                e.preventDefault();\r
+            }\r
+            \r
+            e.stopPropagation();\r
+        },\r
+        key: function(e) {\r
+            var opt = $currentTrigger.data('contextMenu') || {},\r
+                $children = opt.$menu.children(),\r
+                $round;\r
+\r
+            switch (e.keyCode) {\r
+                case 9:\r
+                case 38: // up\r
+                    handle.keyStop(e, opt);\r
+                    // if keyCode is [38 (up)] or [9 (tab) with shift]\r
+                    if (opt.isInput) {\r
+                        if (e.keyCode == 9 && e.shiftKey) {\r
+                            e.preventDefault();\r
+                            opt.$selected && opt.$selected.find('input, textarea, select').blur();\r
+                            opt.$menu.trigger('prevcommand');\r
+                            return;\r
+                        } else if (e.keyCode == 38 && opt.$selected.find('input, textarea, select').prop('type') == 'checkbox') {\r
+                            // checkboxes don't capture this key\r
+                            e.preventDefault();\r
+                            return;\r
+                        }\r
+                    } else if (e.keyCode != 9 || e.shiftKey) {\r
+                        opt.$menu.trigger('prevcommand');\r
+                        return;\r
+                    }\r
+                    \r
+                case 9: // tab\r
+                case 40: // down\r
+                    handle.keyStop(e, opt);\r
+                    if (opt.isInput) {\r
+                        if (e.keyCode == 9) {\r
+                            e.preventDefault();\r
+                            opt.$selected && opt.$selected.find('input, textarea, select').blur();\r
+                            opt.$menu.trigger('nextcommand');\r
+                            return;\r
+                        } else if (e.keyCode == 40 && opt.$selected.find('input, textarea, select').prop('type') == 'checkbox') {\r
+                            // checkboxes don't capture this key\r
+                            e.preventDefault();\r
+                            return;\r
+                        }\r
+                    } else {\r
+                        opt.$menu.trigger('nextcommand');\r
+                        return;\r
+                    }\r
+                    break;\r
+                \r
+                case 37: // left\r
+                    handle.keyStop(e, opt);\r
+                    if (opt.isInput || !opt.$selected || !opt.$selected.length) {\r
+                        break;\r
+                    }\r
+                \r
+                    if (!opt.$selected.parent().hasClass('context-menu-root')) {\r
+                        var $parent = opt.$selected.parent().parent();\r
+                        opt.$selected.trigger('contextmenu:blur');\r
+                        opt.$selected = $parent;\r
+                        return;\r
+                    }\r
+                    break;\r
+                    \r
+                case 39: // right\r
+                    handle.keyStop(e, opt);\r
+                    if (opt.isInput || !opt.$selected || !opt.$selected.length) {\r
+                        break;\r
+                    }\r
+                    \r
+                    var itemdata = opt.$selected.data('contextMenu') || {};\r
+                    if (itemdata.$menu && opt.$selected.hasClass('context-menu-submenu')) {\r
+                        opt.$selected = null;\r
+                        itemdata.$selected = null;\r
+                        itemdata.$menu.trigger('nextcommand');\r
+                        return;\r
+                    }\r
+                    break;\r
+                \r
+                case 35: // end\r
+                case 36: // home\r
+                    if (opt.$selected && opt.$selected.find('input, textarea, select').length) {\r
+                        return;\r
+                    } else {\r
+                        (opt.$selected && opt.$selected.parent() || opt.$menu)\r
+                            .children(':not(.disabled, .not-selectable)')[e.keyCode == 36 ? 'first' : 'last']()\r
+                            .trigger('contextmenu:focus');\r
+                        e.preventDefault();\r
+                        return;\r
+                    }\r
+                    break;\r
+                    \r
+                case 13: // enter\r
+                    handle.keyStop(e, opt);\r
+                    if (opt.isInput) {\r
+                        if (opt.$selected && !opt.$selected.is('textarea, select')) {\r
+                            e.preventDefault();\r
+                            return;\r
+                        }\r
+                        break;\r
+                    }\r
+                    opt.$selected && opt.$selected.trigger('mouseup');\r
+                    return;\r
+                    \r
+                case 32: // space\r
+                case 33: // page up\r
+                case 34: // page down\r
+                    // prevent browser from scrolling down while menu is visible\r
+                    handle.keyStop(e, opt);\r
+                    return;\r
+                    \r
+                case 27: // esc\r
+                    handle.keyStop(e, opt);\r
+                    opt.$menu.trigger('contextmenu:hide');\r
+                    return;\r
+                    \r
+                default: // 0-9, a-z\r
+                    var k = (String.fromCharCode(e.keyCode)).toUpperCase();\r
+                    if (opt.accesskeys[k]) {\r
+                        // according to the specs accesskeys must be invoked immediately\r
+                        opt.accesskeys[k].$node.trigger(opt.accesskeys[k].$menu\r
+                            ? 'contextmenu:focus'\r
+                            : 'mouseup'\r
+                        );\r
+                        return;\r
+                    }\r
+                    break;\r
+            }\r
+            // pass event to selected item, \r
+            // stop propagation to avoid endless recursion\r
+            e.stopPropagation();\r
+            opt.$selected && opt.$selected.trigger(e);\r
+        },\r
+\r
+        // select previous possible command in menu\r
+        prevItem: function(e) {\r
+            e.stopPropagation();\r
+            var opt = $(this).data('contextMenu') || {};\r
+\r
+            // obtain currently selected menu\r
+            if (opt.$selected) {\r
+                var $s = opt.$selected;\r
+                opt = opt.$selected.parent().data('contextMenu') || {};\r
+                opt.$selected = $s;\r
+            }\r
+            \r
+            var $children = opt.$menu.children(),\r
+                $prev = !opt.$selected || !opt.$selected.prev().length ? $children.last() : opt.$selected.prev(),\r
+                $round = $prev;\r
+            \r
+            // skip disabled\r
+            while ($prev.hasClass('disabled') || $prev.hasClass('not-selectable')) {\r
+                if ($prev.prev().length) {\r
+                    $prev = $prev.prev();\r
+                } else {\r
+                    $prev = $children.last();\r
+                }\r
+                if ($prev.is($round)) {\r
+                    // break endless loop\r
+                    return;\r
+                }\r
+            }\r
+            \r
+            // leave current\r
+            if (opt.$selected) {\r
+                handle.itemMouseleave.call(opt.$selected.get(0), e);\r
+            }\r
+            \r
+            // activate next\r
+            handle.itemMouseenter.call($prev.get(0), e);\r
+            \r
+            // focus input\r
+            var $input = $prev.find('input, textarea, select');\r
+            if ($input.length) {\r
+                $input.focus();\r
+            }\r
+        },\r
+        // select next possible command in menu\r
+        nextItem: function(e) {\r
+            e.stopPropagation();\r
+            var opt = $(this).data('contextMenu') || {};\r
+\r
+            // obtain currently selected menu\r
+            if (opt.$selected) {\r
+                var $s = opt.$selected;\r
+                opt = opt.$selected.parent().data('contextMenu') || {};\r
+                opt.$selected = $s;\r
+            }\r
+\r
+            var $children = opt.$menu.children(),\r
+                $next = !opt.$selected || !opt.$selected.next().length ? $children.first() : opt.$selected.next(),\r
+                $round = $next;\r
+\r
+            // skip disabled\r
+            while ($next.hasClass('disabled') || $next.hasClass('not-selectable')) {\r
+                if ($next.next().length) {\r
+                    $next = $next.next();\r
+                } else {\r
+                    $next = $children.first();\r
+                }\r
+                if ($next.is($round)) {\r
+                    // break endless loop\r
+                    return;\r
+                }\r
+            }\r
+            \r
+            // leave current\r
+            if (opt.$selected) {\r
+                handle.itemMouseleave.call(opt.$selected.get(0), e);\r
+            }\r
+            \r
+            // activate next\r
+            handle.itemMouseenter.call($next.get(0), e);\r
+            \r
+            // focus input\r
+            var $input = $next.find('input, textarea, select');\r
+            if ($input.length) {\r
+                $input.focus();\r
+            }\r
+        },\r
+        \r
+        // flag that we're inside an input so the key handler can act accordingly\r
+        focusInput: function(e) {\r
+            var $this = $(this).closest('.context-menu-item'),\r
+                data = $this.data(),\r
+                opt = data.contextMenu,\r
+                root = data.contextMenuRoot;\r
+\r
+            root.$selected = opt.$selected = $this;\r
+            root.isInput = opt.isInput = true;\r
+        },\r
+        // flag that we're inside an input so the key handler can act accordingly\r
+        blurInput: function(e) {\r
+            var $this = $(this).closest('.context-menu-item'),\r
+                data = $this.data(),\r
+                opt = data.contextMenu,\r
+                root = data.contextMenuRoot;\r
+\r
+            root.isInput = opt.isInput = false;\r
+        },\r
+        \r
+        // :hover on menu\r
+        menuMouseenter: function(e) {\r
+            var root = $(this).data().contextMenuRoot;\r
+            root.hovering = true;\r
+        },\r
+        // :hover on menu\r
+        menuMouseleave: function(e) {\r
+            var root = $(this).data().contextMenuRoot;\r
+            if (root.$layer && root.$layer.is(e.relatedTarget)) {\r
+                root.hovering = false;\r
+            }\r
+        },\r
+        \r
+        // :hover done manually so key handling is possible\r
+        itemMouseenter: function(e) {\r
+            var $this = $(this),\r
+                data = $this.data(),\r
+                opt = data.contextMenu,\r
+                root = data.contextMenuRoot;\r
+            \r
+            root.hovering = true;\r
+\r
+            // abort if we're re-entering\r
+            if (e && root.$layer && root.$layer.is(e.relatedTarget)) {\r
+                e.preventDefault();\r
+                e.stopImmediatePropagation();\r
+            }\r
+\r
+            // make sure only one item is selected\r
+            (opt.$menu ? opt : root).$menu\r
+                .children('.hover').trigger('contextmenu:blur');\r
+\r
+            if ($this.hasClass('disabled') || $this.hasClass('not-selectable')) {\r
+                opt.$selected = null;\r
+                return;\r
+            }\r
+            \r
+            $this.trigger('contextmenu:focus');\r
+        },\r
+        // :hover done manually so key handling is possible\r
+        itemMouseleave: function(e) {\r
+            var $this = $(this),\r
+                data = $this.data(),\r
+                opt = data.contextMenu,\r
+                root = data.contextMenuRoot;\r
+\r
+            if (root !== opt && root.$layer && root.$layer.is(e.relatedTarget)) {\r
+                root.$selected && root.$selected.trigger('contextmenu:blur');\r
+                e.preventDefault();\r
+                e.stopImmediatePropagation();\r
+                root.$selected = opt.$selected = opt.$node;\r
+                return;\r
+            }\r
+            \r
+            $this.trigger('contextmenu:blur');\r
+        },\r
+        // contextMenu item click\r
+        itemClick: function(e) {\r
+            var $this = $(this),\r
+                data = $this.data(),\r
+                opt = data.contextMenu,\r
+                root = data.contextMenuRoot,\r
+                key = data.contextMenuKey,\r
+                callback;\r
+\r
+            // abort if the key is unknown or disabled or is a menu\r
+            if (!opt.items[key] || $this.hasClass('disabled') || $this.hasClass('context-menu-submenu')) {\r
+                return;\r
+            }\r
+\r
+            e.preventDefault();\r
+            e.stopImmediatePropagation();\r
+\r
+            if ($.isFunction(root.callbacks[key])) {\r
+                // item-specific callback\r
+                callback = root.callbacks[key];\r
+            } else if ($.isFunction(root.callback)) {\r
+                // default callback\r
+                callback = root.callback;                \r
+            } else {\r
+                // no callback, no action\r
+                return;\r
+            }\r
+\r
+            // hide menu if callback doesn't stop that\r
+            if (callback.call(root.$trigger, key, root) !== false) {\r
+                root.$menu.trigger('contextmenu:hide');\r
+            } else {\r
+                op.update.call(root.$trigger, root);\r
+            }\r
+        },\r
+        // ignore click events on input elements\r
+        inputClick: function(e) {\r
+            e.stopImmediatePropagation();\r
+        },\r
+        \r
+        // hide <menu>\r
+        hideMenu: function(e) {\r
+            var root = $(this).data('contextMenuRoot');\r
+            op.hide.call(root.$trigger, root);\r
+        },\r
+        // focus <command>\r
+        focusItem: function(e) {\r
+            e.stopPropagation();\r
+            var $this = $(this),\r
+                data = $this.data(),\r
+                opt = data.contextMenu,\r
+                root = data.contextMenuRoot;\r
+\r
+            $this.addClass('hover')\r
+                .siblings('.hover').trigger('contextmenu:blur');\r
+            \r
+            // remember selected\r
+            opt.$selected = root.$selected = $this;\r
+            \r
+            // position sub-menu - do after show so dumb $.ui.position can keep up\r
+            if (opt.$node) {\r
+                root.positionSubmenu.call(opt.$node, opt.$menu);\r
+            }\r
+        },\r
+        // blur <command>\r
+        blurItem: function(e) {\r
+            e.stopPropagation();\r
+            var $this = $(this),\r
+                data = $this.data(),\r
+                opt = data.contextMenu,\r
+                root = data.contextMenuRoot;\r
+            \r
+            $this.removeClass('hover');\r
+            opt.$selected = null;\r
+        }\r
+    },\r
+    // operations\r
+    op = {\r
+        show: function(opt, x, y) {\r
+            var $this = $(this),\r
+                offset,\r
+                css = {};\r
+\r
+            // hide any open menus\r
+            $('#context-menu-layer').trigger('mousedown');\r
+\r
+            // backreference for callbacks\r
+            opt.$trigger = $this;\r
+\r
+            // show event\r
+            if (opt.events.show.call($this, opt) === false) {\r
+                $currentTrigger = null;\r
+                return;\r
+            }\r
+            \r
+            // create or update context menu\r
+            op.update.call($this, opt);\r
+            \r
+            // position menu\r
+            opt.position.call($this, opt, x, y);\r
+\r
+            // make sure we're in front\r
+            if (opt.zIndex) {\r
+                css.zIndex = zindex($this) + opt.zIndex;\r
+            }\r
+            \r
+            // add layer\r
+            op.layer.call(opt.$menu, opt, css.zIndex);\r
+            \r
+            // adjust sub-menu zIndexes\r
+            opt.$menu.find('ul').css('zIndex', css.zIndex + 1);\r
+            \r
+            // position and show context menu\r
+            opt.$menu.css( css )[opt.animation.show](opt.animation.duration);\r
+            // make options available\r
+            $this.data('contextMenu', opt);\r
+            // register key handler\r
+            $(document).off('keydown.contextMenu').on('keydown.contextMenu', handle.key);\r
+            // register autoHide handler\r
+            if (opt.autoHide) {\r
+                // trigger element coordinates\r
+                var pos = $this.position();\r
+                pos.right = pos.left + $this.outerWidth();\r
+                pos.bottom = pos.top + this.outerHeight();\r
+                // mouse position handler\r
+                $(document).on('mousemove.contextMenuAutoHide', function(e) {\r
+                    if (opt.$layer && !opt.hovering && (!(e.pageX >= pos.left && e.pageX <= pos.right) || !(e.pageY >= pos.top && e.pageY <= pos.bottom))) {\r
+                        // if mouse in menu...\r
+                        opt.$layer.trigger('mousedown');\r
+                    }\r
+                });\r
+            }\r
+        },\r
+        hide: function(opt) {\r
+            var $this = $(this);\r
+            if (!opt) {\r
+                opt = $this.data('contextMenu') || {};\r
+            }\r
+            \r
+            // hide event\r
+            if (opt.events && opt.events.hide.call($this, opt) === false) {\r
+                return;\r
+            }\r
+            \r
+            if (opt.$layer) {\r
+                // keep layer for a bit so the contextmenu event can be aborted properly by opera\r
+                setTimeout((function($layer){ return function(){\r
+                        $layer.remove();\r
+                    };\r
+                })(opt.$layer), 10);\r
+                \r
+                try {\r
+                    delete opt.$layer;\r
+                } catch(e) {\r
+                    opt.$layer = null;\r
+                }\r
+            }\r
+            \r
+            // remove handle\r
+            $currentTrigger = null;\r
+            // remove selected\r
+            opt.$menu.find('.hover').trigger('contextmenu:blur');\r
+            opt.$selected = null;\r
+            // unregister key and mouse handlers\r
+            //$(document).off('.contextMenuAutoHide keydown.contextMenu'); // http://bugs.jquery.com/ticket/10705\r
+            $(document).off('.contextMenuAutoHide').off('keydown.contextMenu');\r
+            // hide menu\r
+            opt.$menu && opt.$menu[opt.animation.hide](opt.animation.duration);\r
+            \r
+            // tear down dynamically built menu\r
+            if (opt.build) {\r
+                opt.$menu.remove();\r
+                $.each(opt, function(key, value) {\r
+                    switch (key) {\r
+                        case 'ns':\r
+                        case 'selector':\r
+                        case 'build':\r
+                        case 'trigger':\r
+                            return true;\r
+\r
+                        default:\r
+                            opt[key] = undefined;\r
+                            try {\r
+                                delete opt[key];\r
+                            } catch (e) {}\r
+                            return true;\r
+                   }\r
+                });\r
+            }\r
+        },\r
+        create: function(opt, root) {\r
+            if (root === undefined) {\r
+                root = opt;\r
+            }\r
+            // create contextMenu\r
+            opt.$menu = $('<ul class="context-menu-list ' + (opt.className || "") + '"></ul>').data({\r
+                'contextMenu': opt,\r
+                'contextMenuRoot': root\r
+            });\r
+            \r
+            $.each(['callbacks', 'commands', 'inputs'], function(i,k){\r
+                opt[k] = {};\r
+                if (!root[k]) {\r
+                    root[k] = {};\r
+                }\r
+            });\r
+            \r
+            root.accesskeys || (root.accesskeys = {});\r
+            \r
+            // create contextMenu items\r
+            $.each(opt.items, function(key, item){\r
+                var $t = $('<li class="context-menu-item ' + (item.className || "") +'"></li>'),\r
+                    $label = null,\r
+                    $input = null;\r
+                \r
+                item.$node = $t.data({\r
+                    'contextMenu': opt,\r
+                    'contextMenuRoot': root,\r
+                    'contextMenuKey': key\r
+                });\r
+                \r
+                // register accesskey\r
+                // NOTE: the accesskey attribute should be applicable to any element, but Safari5 and Chrome13 still can't do that\r
+                if (item.accesskey) {\r
+                    var aks = splitAccesskey(item.accesskey);\r
+                    for (var i=0, ak; ak = aks[i]; i++) {\r
+                        if (!root.accesskeys[ak]) {\r
+                            root.accesskeys[ak] = item;\r
+                            item._name = item.name.replace(new RegExp('(' + ak + ')', 'i'), '<span class="context-menu-accesskey">$1</span>');\r
+                            break;\r
+                        }\r
+                    }\r
+                }\r
+                \r
+                if (typeof item == "string") {\r
+                    $t.addClass('context-menu-separator not-selectable');\r
+                } else if (item.type && types[item.type]) {\r
+                    // run custom type handler\r
+                    types[item.type].call($t, item, opt, root);\r
+                    // register commands\r
+                    $.each([opt, root], function(i,k){\r
+                        k.commands[key] = item;\r
+                        if ($.isFunction(item.callback)) {\r
+                            k.callbacks[key] = item.callback;\r
+                        }\r
+                    });\r
+                } else {\r
+                    // add label for input\r
+                    if (item.type == 'html') {\r
+                        $t.addClass('context-menu-html not-selectable');\r
+                    } else if (item.type) {\r
+                        $label = $('<label></label>').appendTo($t);\r
+                        $('<span></span>').html(item._name || item.name).appendTo($label);\r
+                        $t.addClass('context-menu-input');\r
+                        opt.hasTypes = true;\r
+                        $.each([opt, root], function(i,k){\r
+                            k.commands[key] = item;\r
+                            k.inputs[key] = item;\r
+                        });\r
+                    } else if (item.items) {\r
+                        item.type = 'sub';\r
+                    }\r
+                \r
+                    switch (item.type) {\r
+                        case 'text':\r
+                            $input = $('<input type="text" value="1" name="context-menu-input-'+ key +'" value="">')\r
+                                .val(item.value || "").appendTo($label);\r
+                            break;\r
+                    \r
+                        case 'textarea':\r
+                            $input = $('<textarea name="context-menu-input-'+ key +'"></textarea>')\r
+                                .val(item.value || "").appendTo($label);\r
+\r
+                            if (item.height) {\r
+                                $input.height(item.height);\r
+                            }\r
+                            break;\r
+\r
+                        case 'checkbox':\r
+                            $input = $('<input type="checkbox" value="1" name="context-menu-input-'+ key +'" value="">')\r
+                                .val(item.value || "").prop("checked", !!item.selected).prependTo($label);\r
+                            break;\r
+\r
+                        case 'radio':\r
+                            $input = $('<input type="radio" value="1" name="context-menu-input-'+ item.radio +'" value="">')\r
+                                .val(item.value || "").prop("checked", !!item.selected).prependTo($label);\r
+                            break;\r
+                    \r
+                        case 'select':\r
+                            $input = $('<select name="context-menu-input-'+ key +'">').appendTo($label);\r
+                            if (item.options) {\r
+                                $.each(item.options, function(value, text) {\r
+                                    $('<option></option>').val(value).text(text).appendTo($input);\r
+                                });\r
+                                $input.val(item.selected);\r
+                            }\r
+                            break;\r
+                        \r
+                        case 'sub':\r
+                            $('<span></span>').html(item._name || item.name).appendTo($t);\r
+                            item.appendTo = item.$node;\r
+                            op.create(item, root);\r
+                            $t.data('contextMenu', item).addClass('context-menu-submenu');\r
+                            item.callback = null;\r
+                            break;\r
+                        \r
+                        case 'html':\r
+                            $(item.html).appendTo($t);\r
+                            break;\r
+                        \r
+                        default:\r
+                            $.each([opt, root], function(i,k){\r
+                                k.commands[key] = item;\r
+                                if ($.isFunction(item.callback)) {\r
+                                    k.callbacks[key] = item.callback;\r
+                                }\r
+                            });\r
+                            \r
+                            $('<span></span>').html(item._name || item.name || "").appendTo($t);\r
+                            break;\r
+                    }\r
+                    \r
+                    // disable key listener in <input>\r
+                    if (item.type && item.type != 'sub' && item.type != 'html') {\r
+                        $input\r
+                            .on('focus', handle.focusInput)\r
+                            .on('blur', handle.blurInput);\r
+                        \r
+                        if (item.events) {\r
+                            $input.on(item.events);\r
+                        }\r
+                    }\r
+                \r
+                    // add icons\r
+                    if (item.icon) {\r
+                        $t.addClass("icon icon-" + item.icon);\r
+                    }\r
+                }\r
+                \r
+                // cache contained elements\r
+                item.$input = $input;\r
+                item.$label = $label;\r
+\r
+                // attach item to menu\r
+                $t.appendTo(opt.$menu);\r
+                \r
+                // Disable text selection\r
+                if (!opt.hasTypes) {\r
+                    if($.browser.msie) {\r
+                        $t.on('selectstart.disableTextSelect', handle.abortevent);\r
+                    } else if(!$.browser.mozilla) {\r
+                        $t.on('mousedown.disableTextSelect', handle.abortevent);\r
+                    }\r
+                }\r
+            });\r
+            // attach contextMenu to <body> (to bypass any possible overflow:hidden issues on parents of the trigger element)\r
+            if (!opt.$node) {\r
+                opt.$menu.css('display', 'none').addClass('context-menu-root');\r
+            }\r
+            opt.$menu.appendTo(opt.appendTo || document.body);\r
+        },\r
+        update: function(opt, root) {\r
+            var $this = this;\r
+            if (root === undefined) {\r
+                root = opt;\r
+                // determine widths of submenus, as CSS won't grow them automatically\r
+                // position:absolute > position:absolute; min-width:100; max-width:200; results in width: 100;\r
+                // kinda sucks hard...\r
+                opt.$menu.find('ul').andSelf().css({position: 'static', display: 'block'}).each(function(){\r
+                    var $this = $(this);\r
+                    $this.width($this.css('position', 'absolute').width())\r
+                        .css('position', 'static');\r
+                }).css({position: '', display: ''});\r
+            }\r
+            // re-check disabled for each item\r
+            opt.$menu.children().each(function(){\r
+                var $item = $(this),\r
+                    key = $item.data('contextMenuKey'),\r
+                    item = opt.items[key],\r
+                    disabled = ($.isFunction(item.disabled) && item.disabled.call($this, key, root)) || item.disabled === true;\r
+\r
+                // dis- / enable item\r
+                $item[disabled ? 'addClass' : 'removeClass']('disabled');\r
+                \r
+                if (item.type) {\r
+                    // dis- / enable input elements\r
+                    $item.find('input, select, textarea').prop('disabled', disabled);\r
+                    \r
+                    // update input states\r
+                    switch (item.type) {\r
+                        case 'text':\r
+                        case 'textarea':\r
+                            item.$input.val(item.value || "");\r
+                            break;\r
+                            \r
+                        case 'checkbox':\r
+                        case 'radio':\r
+                            item.$input.val(item.value || "").prop('checked', !!item.selected);\r
+                            break;\r
+                            \r
+                        case 'select':\r
+                            item.$input.val(item.selected || "");\r
+                            break;\r
+                    }\r
+                }\r
+                \r
+                if (item.$menu) {\r
+                    // update sub-menu\r
+                    op.update.call($this, item, root);\r
+                }\r
+            });\r
+        },\r
+        layer: function(opt, zIndex) {\r
+            // add transparent layer for click area\r
+            // filter and background for Internet Explorer, Issue #23\r
+            return opt.$layer = $('<div id="context-menu-layer" style="position:fixed; z-index:' + zIndex + '; top:0; left:0; opacity: 0; filter: alpha(opacity=0); background-color: #000;"></div>')\r
+                .css({height: $win.height(), width: $win.width(), display: 'block'})\r
+                .data('contextMenuRoot', opt)\r
+                .insertBefore(this)\r
+                .on('contextmenu', handle.abortevent)\r
+                .on('mousedown', handle.layerClick);\r
+        }\r
+    };\r
+\r
+// split accesskey according to http://www.whatwg.org/specs/web-apps/current-work/multipage/editing.html#assigned-access-key\r
+function splitAccesskey(val) {\r
+    var t = val.split(/\s+/),\r
+        keys = [];\r
+        \r
+    for (var i=0, k; k = t[i]; i++) {\r
+        k = k[0].toUpperCase(); // first character only\r
+        // theoretically non-accessible characters should be ignored, but different systems, different keyboard layouts, ... screw it.\r
+        // a map to look up already used access keys would be nice\r
+        keys.push(k);\r
+    }\r
+    \r
+    return keys;\r
+}\r
+\r
+// handle contextMenu triggers\r
+$.fn.contextMenu = function(operation) {\r
+    if (operation === undefined) {\r
+        this.first().trigger('contextmenu');\r
+    } else if (operation.x && operation.y) {\r
+        this.first().trigger(jQuery.Event("contextmenu", {pageX: operation.x, pageY: operation.y}));\r
+    } else if (operation === "hide") {\r
+        var $menu = this.data('contextMenu').$menu;\r
+        $menu && $menu.trigger('contextmenu:hide');\r
+    } else if (operation) {\r
+        this.removeClass('context-menu-disabled');\r
+    } else if (!operation) {\r
+        this.addClass('context-menu-disabled');\r
+    }\r
+    \r
+    return this;\r
+};\r
+\r
+// manage contextMenu instances\r
+$.contextMenu = function(operation, options) {\r
+    if (typeof operation != 'string') {\r
+        options = operation;\r
+        operation = 'create';\r
+    }\r
+    \r
+    if (typeof options == 'string') {\r
+        options = {selector: options};\r
+    } else if (options === undefined) {\r
+        options = {};\r
+    }\r
+    \r
+    // merge with default options\r
+    var o = $.extend(true, {}, defaults, options || {}),\r
+        $body = $body = $(document);\r
+    \r
+    switch (operation) {\r
+        case 'create':\r
+            // no selector no joy\r
+            if (!o.selector) {\r
+                throw new Error('No selector specified');\r
+            }\r
+            // make sure internal classes are not bound to\r
+            if (o.selector.match(/.context-menu-(list|item|input)($|\s)/)) {\r
+                throw new Error('Cannot bind to selector "' + o.selector + '" as it contains a reserved className');\r
+            }\r
+            if (!o.build && (!o.items || $.isEmptyObject(o.items))) {\r
+                throw new Error('No Items sepcified');\r
+            }\r
+            counter ++;\r
+            o.ns = '.contextMenu' + counter;\r
+            namespaces[o.selector] = o.ns;\r
+            menus[o.ns] = o;\r
+            \r
+            if (!initialized) {\r
+                // make sure item click is registered first\r
+                $body\r
+                    .on({\r
+                        'contextmenu:hide.contextMenu': handle.hideMenu,\r
+                        'prevcommand.contextMenu': handle.prevItem,\r
+                        'nextcommand.contextMenu': handle.nextItem,\r
+                        'contextmenu.contextMenu': handle.abortevent,\r
+                        'mouseenter.contextMenu': handle.menuMouseenter,\r
+                        'mouseleave.contextMenu': handle.menuMouseleave\r
+                    }, '.context-menu-list')\r
+                    .on('mouseup.contextMenu', '.context-menu-input', handle.inputClick)\r
+                    .on({\r
+                        'mouseup.contextMenu': handle.itemClick,\r
+                        'contextmenu:focus.contextMenu': handle.focusItem,\r
+                        'contextmenu:blur.contextMenu': handle.blurItem,\r
+                        'contextmenu.contextMenu': handle.abortevent,\r
+                        'mouseenter.contextMenu': handle.itemMouseenter,\r
+                        'mouseleave.contextMenu': handle.itemMouseleave\r
+                    }, '.context-menu-item');\r
+\r
+                initialized = true;\r
+            }\r
+            \r
+            // engage native contextmenu event\r
+            $body\r
+                .on('contextmenu' + o.ns, o.selector, o, handle.contextmenu);\r
+            \r
+            switch (o.trigger) {\r
+                case 'hover':\r
+                        $body\r
+                            .on('mouseenter' + o.ns, o.selector, o, handle.mouseenter)\r
+                            .on('mouseleave' + o.ns, o.selector, o, handle.mouseleave);                    \r
+                    break;\r
+                    \r
+                case 'left':\r
+                        $body.on('click' + o.ns, o.selector, o, handle.click);\r
+                    break;\r
+                /*\r
+                default:\r
+                    // http://www.quirksmode.org/dom/events/contextmenu.html\r
+                    $body\r
+                        .on('mousedown' + o.ns, o.selector, o, handle.mousedown)\r
+                        .on('mouseup' + o.ns, o.selector, o, handle.mouseup);\r
+                    break;\r
+                */\r
+            }\r
+            \r
+            // create menu\r
+            if (!o.build) {\r
+                op.create(o);\r
+            }\r
+            break;\r
+        \r
+        case 'destroy':\r
+            if (!o.selector) {\r
+                $body.off('.contextMenu .contextMenuAutoHide');\r
+                $.each(namespaces, function(key, value) {\r
+                    $body.off(value);\r
+                });\r
+                \r
+                namespaces = {};\r
+                menus = {};\r
+                counter = 0;\r
+                initialized = false;\r
+                \r
+                $('.context-menu-list').remove();\r
+            } else if (namespaces[o.selector]) {\r
+                try {\r
+                    if (menus[namespaces[o.selector]].$menu) {\r
+                        menus[namespaces[o.selector]].$menu.remove();\r
+                    }\r
+                    \r
+                    delete menus[namespaces[o.selector]];\r
+                } catch(e) {\r
+                    menus[namespaces[o.selector]] = null;\r
+                }\r
+                \r
+                $body.off(namespaces[o.selector]);\r
+            }\r
+            break;\r
+        \r
+        case 'html5':\r
+            // if <command> or <menuitem> are not handled by the browser,\r
+            // or options was a bool true,\r
+            // initialize $.contextMenu for them\r
+            if ((!$.support.htmlCommand && !$.support.htmlMenuitem) || (typeof options == "boolean" && options)) {\r
+                $('menu[type="context"]').each(function() {\r
+                    if (this.id) {\r
+                        $.contextMenu({\r
+                            selector: '[contextmenu=' + this.id +']',\r
+                            items: $.contextMenu.fromMenu(this)\r
+                        });\r
+                    }\r
+                }).css('display', 'none');\r
+            }\r
+            break;\r
+        \r
+        default:\r
+            throw new Error('Unknown operation "' + operation + '"');\r
+    }\r
+    \r
+    return this;\r
+};\r
+\r
+// import values into <input> commands\r
+$.contextMenu.setInputValues = function(opt, data) {\r
+    if (data === undefined) {\r
+        data = {};\r
+    }\r
+    \r
+    $.each(opt.inputs, function(key, item) {\r
+        switch (item.type) {\r
+            case 'text':\r
+            case 'textarea':\r
+                item.value = data[key] || "";\r
+                break;\r
+\r
+            case 'checkbox':\r
+                item.selected = data[key] ? true : false;\r
+                break;\r
+                \r
+            case 'radio':\r
+                item.selected = (data[item.radio] || "") == item.value ? true : false;\r
+                break;\r
+            \r
+            case 'select':\r
+                item.selected = data[key] || "";\r
+                break;\r
+        }\r
+    });\r
+};\r
+\r
+// export values from <input> commands\r
+$.contextMenu.getInputValues = function(opt, data) {\r
+    if (data === undefined) {\r
+        data = {};\r
+    }\r
+    \r
+    $.each(opt.inputs, function(key, item) {\r
+        switch (item.type) {\r
+            case 'text':\r
+            case 'textarea':\r
+            case 'select':\r
+                data[key] = item.$input.val();\r
+                break;\r
+\r
+            case 'checkbox':\r
+                data[key] = item.$input.prop('checked');\r
+                break;\r
+                \r
+            case 'radio':\r
+                if (item.$input.prop('checked')) {\r
+                    data[item.radio] = item.value;\r
+                }\r
+                break;\r
+        }\r
+    });\r
+    \r
+    return data;\r
+};\r
+\r
+// find <label for="xyz">\r
+function inputLabel(node) {\r
+    return (node.id && $('label[for="'+ node.id +'"]').val()) || node.name;\r
+}\r
+\r
+// convert <menu> to items object\r
+function menuChildren(items, $children, counter) {\r
+    if (!counter) {\r
+        counter = 0;\r
+    }\r
+    \r
+    $children.each(function() {\r
+        var $node = $(this),\r
+            node = this,\r
+            nodeName = this.nodeName.toLowerCase(),\r
+            label,\r
+            item;\r
+        \r
+        // extract <label><input>\r
+        if (nodeName == 'label' && $node.find('input, textarea, select').length) {\r
+            label = $node.text();\r
+            $node = $node.children().first();\r
+            node = $node.get(0);\r
+            nodeName = node.nodeName.toLowerCase();\r
+        }\r
+        \r
+        /*\r
+         * <menu> accepts flow-content as children. that means <embed>, <canvas> and such are valid menu items.\r
+         * Not being the sadistic kind, $.contextMenu only accepts:\r
+         * <command>, <menuitem>, <hr>, <span>, <p> <input [text, radio, checkbox]>, <textarea>, <select> and of course <menu>.\r
+         * Everything else will be imported as an html node, which is not interfaced with contextMenu.\r
+         */\r
+        \r
+        // http://www.whatwg.org/specs/web-apps/current-work/multipage/commands.html#concept-command\r
+        switch (nodeName) {\r
+            // http://www.whatwg.org/specs/web-apps/current-work/multipage/interactive-elements.html#the-menu-element\r
+            case 'menu':\r
+                item = {name: $node.attr('label'), items: {}};\r
+                menuChildren(item.items, $node.children(), counter);\r
+                break;\r
+            \r
+            // http://www.whatwg.org/specs/web-apps/current-work/multipage/commands.html#using-the-a-element-to-define-a-command\r
+            case 'a':\r
+            // http://www.whatwg.org/specs/web-apps/current-work/multipage/commands.html#using-the-button-element-to-define-a-command\r
+            case 'button':\r
+                item = {\r
+                    name: $node.text(),\r
+                    disabled: !!$node.attr('disabled'),\r
+                    callback: (function(){ return function(){ $node.click(); }; })()\r
+                };\r
+                break;\r
+            \r
+            // http://www.whatwg.org/specs/web-apps/current-work/multipage/commands.html#using-the-command-element-to-define-a-command\r
+\r
+            case 'menuitem':\r
+            case 'command':\r
+                switch ($node.attr('type')) {\r
+                    case undefined:\r
+                    case 'command':\r
+                    case 'menuitem':\r
+                        item = {\r
+                            name: $node.attr('label'),\r
+                            disabled: !!$node.attr('disabled'),\r
+                            callback: (function(){ return function(){ $node.click(); }; })()\r
+                        };\r
+                        break;\r
+                        \r
+                    case 'checkbox':\r
+                        item = {\r
+                            type: 'checkbox',\r
+                            disabled: !!$node.attr('disabled'),\r
+                            name: $node.attr('label'),\r
+                            selected: !!$node.attr('checked')\r
+                        };\r
+                        break;\r
+                        \r
+                    case 'radio':\r
+                        item = {\r
+                            type: 'radio',\r
+                            disabled: !!$node.attr('disabled'),\r
+                            name: $node.attr('label'),\r
+                            radio: $node.attr('radiogroup'),\r
+                            value: $node.attr('id'),\r
+                            selected: !!$node.attr('checked')\r
+                        };\r
+                        break;\r
+                        \r
+                    default:\r
+                        item = undefined;\r
+                }\r
+                break;\r
\r
+            case 'hr':\r
+                item = '-------';\r
+                break;\r
+                \r
+            case 'input':\r
+                switch ($node.attr('type')) {\r
+                    case 'text':\r
+                        item = {\r
+                            type: 'text',\r
+                            name: label || inputLabel(node),\r
+                            disabled: !!$node.attr('disabled'),\r
+                            value: $node.val()\r
+                        };\r
+                        break;\r
+                        \r
+                    case 'checkbox':\r
+                        item = {\r
+                            type: 'checkbox',\r
+                            name: label || inputLabel(node),\r
+                            disabled: !!$node.attr('disabled'),\r
+                            selected: !!$node.attr('checked')\r
+                        };\r
+                        break;\r
+                        \r
+                    case 'radio':\r
+                        item = {\r
+                            type: 'radio',\r
+                            name: label || inputLabel(node),\r
+                            disabled: !!$node.attr('disabled'),\r
+                            radio: !!$node.attr('name'),\r
+                            value: $node.val(),\r
+                            selected: !!$node.attr('checked')\r
+                        };\r
+                        break;\r
+                    \r
+                    default:\r
+                        item = undefined;\r
+                        break;\r
+                }\r
+                break;\r
+                \r
+            case 'select':\r
+                item = {\r
+                    type: 'select',\r
+                    name: label || inputLabel(node),\r
+                    disabled: !!$node.attr('disabled'),\r
+                    selected: $node.val(),\r
+                    options: {}\r
+                };\r
+                $node.children().each(function(){\r
+                    item.options[this.value] = $(this).text();\r
+                });\r
+                break;\r
+                \r
+            case 'textarea':\r
+                item = {\r
+                    type: 'textarea',\r
+                    name: label || inputLabel(node),\r
+                    disabled: !!$node.attr('disabled'),\r
+                    value: $node.val()\r
+                };\r
+                break;\r
+            \r
+            case 'label':\r
+                break;\r
+            \r
+            default:\r
+                item = {type: 'html', html: $node.clone(true)};\r
+                break;\r
+        }\r
+        \r
+        if (item) {\r
+            counter++;\r
+            items['key' + counter] = item;\r
+        }\r
+    });\r
+}\r
+\r
+// convert html5 menu\r
+$.contextMenu.fromMenu = function(element) {\r
+    var $this = $(element),\r
+        items = {};\r
+        \r
+    menuChildren(items, $this.children());\r
+    \r
+    return items;\r
+};\r
+\r
+// make defaults accessible\r
+$.contextMenu.defaults = defaults;\r
+$.contextMenu.types = types;\r
+\r
+})(jQuery);\r
diff --git a/src/redakcja/static/contextmenu/jquery.ui.position.js b/src/redakcja/static/contextmenu/jquery.ui.position.js
new file mode 100755 (executable)
index 0000000..cafdf02
--- /dev/null
@@ -0,0 +1,252 @@
+/*!
+ * jQuery UI Position 1.8.13
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Position
+ */
+(function( $, undefined ) {
+
+$.ui = $.ui || {};
+
+var horizontalPositions = /left|center|right/,
+       verticalPositions = /top|center|bottom/,
+       center = "center",
+       _position = $.fn.position,
+       _offset = $.fn.offset;
+
+$.fn.position = function( options ) {
+       if ( !options || !options.of ) {
+               return _position.apply( this, arguments );
+       }
+
+       // make a copy, we don't want to modify arguments
+       options = $.extend( {}, options );
+
+       var target = $( options.of ),
+               targetElem = target[0],
+               collision = ( options.collision || "flip" ).split( " " ),
+               offset = options.offset ? options.offset.split( " " ) : [ 0, 0 ],
+               targetWidth,
+               targetHeight,
+               basePosition;
+
+       if ( targetElem.nodeType === 9 ) {
+               targetWidth = target.width();
+               targetHeight = target.height();
+               basePosition = { top: 0, left: 0 };
+       // TODO: use $.isWindow() in 1.9
+       } else if ( targetElem.setTimeout ) {
+               targetWidth = target.width();
+               targetHeight = target.height();
+               basePosition = { top: target.scrollTop(), left: target.scrollLeft() };
+       } else if ( targetElem.preventDefault ) {
+               // force left top to allow flipping
+               options.at = "left top";
+               targetWidth = targetHeight = 0;
+               basePosition = { top: options.of.pageY, left: options.of.pageX };
+       } else {
+               targetWidth = target.outerWidth();
+               targetHeight = target.outerHeight();
+               basePosition = target.offset();
+       }
+
+       // force my and at to have valid horizontal and veritcal positions
+       // if a value is missing or invalid, it will be converted to center 
+       $.each( [ "my", "at" ], function() {
+               var pos = ( options[this] || "" ).split( " " );
+               if ( pos.length === 1) {
+                       pos = horizontalPositions.test( pos[0] ) ?
+                               pos.concat( [center] ) :
+                               verticalPositions.test( pos[0] ) ?
+                                       [ center ].concat( pos ) :
+                                       [ center, center ];
+               }
+               pos[ 0 ] = horizontalPositions.test( pos[0] ) ? pos[ 0 ] : center;
+               pos[ 1 ] = verticalPositions.test( pos[1] ) ? pos[ 1 ] : center;
+               options[ this ] = pos;
+       });
+
+       // normalize collision option
+       if ( collision.length === 1 ) {
+               collision[ 1 ] = collision[ 0 ];
+       }
+
+       // normalize offset option
+       offset[ 0 ] = parseInt( offset[0], 10 ) || 0;
+       if ( offset.length === 1 ) {
+               offset[ 1 ] = offset[ 0 ];
+       }
+       offset[ 1 ] = parseInt( offset[1], 10 ) || 0;
+
+       if ( options.at[0] === "right" ) {
+               basePosition.left += targetWidth;
+       } else if ( options.at[0] === center ) {
+               basePosition.left += targetWidth / 2;
+       }
+
+       if ( options.at[1] === "bottom" ) {
+               basePosition.top += targetHeight;
+       } else if ( options.at[1] === center ) {
+               basePosition.top += targetHeight / 2;
+       }
+
+       basePosition.left += offset[ 0 ];
+       basePosition.top += offset[ 1 ];
+
+       return this.each(function() {
+               var elem = $( this ),
+                       elemWidth = elem.outerWidth(),
+                       elemHeight = elem.outerHeight(),
+                       marginLeft = parseInt( $.curCSS( this, "marginLeft", true ) ) || 0,
+                       marginTop = parseInt( $.curCSS( this, "marginTop", true ) ) || 0,
+                       collisionWidth = elemWidth + marginLeft +
+                               ( parseInt( $.curCSS( this, "marginRight", true ) ) || 0 ),
+                       collisionHeight = elemHeight + marginTop +
+                               ( parseInt( $.curCSS( this, "marginBottom", true ) ) || 0 ),
+                       position = $.extend( {}, basePosition ),
+                       collisionPosition;
+
+               if ( options.my[0] === "right" ) {
+                       position.left -= elemWidth;
+               } else if ( options.my[0] === center ) {
+                       position.left -= elemWidth / 2;
+               }
+
+               if ( options.my[1] === "bottom" ) {
+                       position.top -= elemHeight;
+               } else if ( options.my[1] === center ) {
+                       position.top -= elemHeight / 2;
+               }
+
+               // prevent fractions (see #5280)
+               position.left = Math.round( position.left );
+               position.top = Math.round( position.top );
+
+               collisionPosition = {
+                       left: position.left - marginLeft,
+                       top: position.top - marginTop
+               };
+
+               $.each( [ "left", "top" ], function( i, dir ) {
+                       if ( $.ui.position[ collision[i] ] ) {
+                               $.ui.position[ collision[i] ][ dir ]( position, {
+                                       targetWidth: targetWidth,
+                                       targetHeight: targetHeight,
+                                       elemWidth: elemWidth,
+                                       elemHeight: elemHeight,
+                                       collisionPosition: collisionPosition,
+                                       collisionWidth: collisionWidth,
+                                       collisionHeight: collisionHeight,
+                                       offset: offset,
+                                       my: options.my,
+                                       at: options.at
+                               });
+                       }
+               });
+
+               if ( $.fn.bgiframe ) {
+                       elem.bgiframe();
+               }
+               elem.offset( $.extend( position, { using: options.using } ) );
+       });
+};
+
+$.ui.position = {
+       fit: {
+               left: function( position, data ) {
+                       var win = $( window ),
+                               over = data.collisionPosition.left + data.collisionWidth - win.width() - win.scrollLeft();
+                       position.left = over > 0 ? position.left - over : Math.max( position.left - data.collisionPosition.left, position.left );
+               },
+               top: function( position, data ) {
+                       var win = $( window ),
+                               over = data.collisionPosition.top + data.collisionHeight - win.height() - win.scrollTop();
+                       position.top = over > 0 ? position.top - over : Math.max( position.top - data.collisionPosition.top, position.top );
+               }
+       },
+
+       flip: {
+               left: function( position, data ) {
+                       if ( data.at[0] === center ) {
+                               return;
+                       }
+                       var win = $( window ),
+                               over = data.collisionPosition.left + data.collisionWidth - win.width() - win.scrollLeft(),
+                               myOffset = data.my[ 0 ] === "left" ?
+                                       -data.elemWidth :
+                                       data.my[ 0 ] === "right" ?
+                                               data.elemWidth :
+                                               0,
+                               atOffset = data.at[ 0 ] === "left" ?
+                                       data.targetWidth :
+                                       -data.targetWidth,
+                               offset = -2 * data.offset[ 0 ];
+                       position.left += data.collisionPosition.left < 0 ?
+                               myOffset + atOffset + offset :
+                               over > 0 ?
+                                       myOffset + atOffset + offset :
+                                       0;
+               },
+               top: function( position, data ) {
+                       if ( data.at[1] === center ) {
+                               return;
+                       }
+                       var win = $( window ),
+                               over = data.collisionPosition.top + data.collisionHeight - win.height() - win.scrollTop(),
+                               myOffset = data.my[ 1 ] === "top" ?
+                                       -data.elemHeight :
+                                       data.my[ 1 ] === "bottom" ?
+                                               data.elemHeight :
+                                               0,
+                               atOffset = data.at[ 1 ] === "top" ?
+                                       data.targetHeight :
+                                       -data.targetHeight,
+                               offset = -2 * data.offset[ 1 ];
+                       position.top += data.collisionPosition.top < 0 ?
+                               myOffset + atOffset + offset :
+                               over > 0 ?
+                                       myOffset + atOffset + offset :
+                                       0;
+               }
+       }
+};
+
+// offset setter from jQuery 1.4
+if ( !$.offset.setOffset ) {
+       $.offset.setOffset = function( elem, options ) {
+               // set position first, in-case top/left are set even on static elem
+               if ( /static/.test( $.curCSS( elem, "position" ) ) ) {
+                       elem.style.position = "relative";
+               }
+               var curElem   = $( elem ),
+                       curOffset = curElem.offset(),
+                       curTop    = parseInt( $.curCSS( elem, "top",  true ), 10 ) || 0,
+                       curLeft   = parseInt( $.curCSS( elem, "left", true ), 10)  || 0,
+                       props     = {
+                               top:  (options.top  - curOffset.top)  + curTop,
+                               left: (options.left - curOffset.left) + curLeft
+                       };
+               
+               if ( 'using' in options ) {
+                       options.using.call( elem, props );
+               } else {
+                       curElem.css( props );
+               }
+       };
+
+       $.fn.offset = function( options ) {
+               var elem = this[ 0 ];
+               if ( !elem || !elem.ownerDocument ) { return null; }
+               if ( options ) { 
+                       return this.each(function() {
+                               $.offset.setOffset( this, options );
+                       });
+               }
+               return _offset.call( this );
+       };
+}
+
+}( jQuery ));
diff --git a/src/redakcja/static/contextmenu2/images/cut.png b/src/redakcja/static/contextmenu2/images/cut.png
new file mode 100644 (file)
index 0000000..f215d6f
Binary files /dev/null and b/src/redakcja/static/contextmenu2/images/cut.png differ
diff --git a/src/redakcja/static/contextmenu2/images/door.png b/src/redakcja/static/contextmenu2/images/door.png
new file mode 100644 (file)
index 0000000..369fc46
Binary files /dev/null and b/src/redakcja/static/contextmenu2/images/door.png differ
diff --git a/src/redakcja/static/contextmenu2/images/page_white_copy.png b/src/redakcja/static/contextmenu2/images/page_white_copy.png
new file mode 100644 (file)
index 0000000..a9f31a2
Binary files /dev/null and b/src/redakcja/static/contextmenu2/images/page_white_copy.png differ
diff --git a/src/redakcja/static/contextmenu2/images/page_white_delete.png b/src/redakcja/static/contextmenu2/images/page_white_delete.png
new file mode 100644 (file)
index 0000000..af1ecaf
Binary files /dev/null and b/src/redakcja/static/contextmenu2/images/page_white_delete.png differ
diff --git a/src/redakcja/static/contextmenu2/images/page_white_edit.png b/src/redakcja/static/contextmenu2/images/page_white_edit.png
new file mode 100644 (file)
index 0000000..b93e776
Binary files /dev/null and b/src/redakcja/static/contextmenu2/images/page_white_edit.png differ
diff --git a/src/redakcja/static/contextmenu2/images/page_white_paste.png b/src/redakcja/static/contextmenu2/images/page_white_paste.png
new file mode 100644 (file)
index 0000000..5b2cbb3
Binary files /dev/null and b/src/redakcja/static/contextmenu2/images/page_white_paste.png differ
diff --git a/src/redakcja/static/contextmenu2/index.html b/src/redakcja/static/contextmenu2/index.html
new file mode 100644 (file)
index 0000000..4efa62b
--- /dev/null
@@ -0,0 +1,200 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">\r
+\r
+<html xmlns="http://www.w3.org/1999/xhtml">\r
+\r
+       <head>\r
+               <title>jQuery Context Menu Plugin Demo</title>\r
+               <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />\r
+               \r
+               <style type="text/css">\r
+                       BODY,\r
+                       HTML {\r
+                               padding: 0px;\r
+                               margin: 0px;\r
+                       }\r
+                       BODY {\r
+                               font-family: Verdana, Arial, Helvetica, sans-serif;\r
+                               font-size: 11px;\r
+                               background: #FFF;\r
+                               padding: 15px;\r
+                       }\r
+                       \r
+                       H1 {\r
+                               font-family: Georgia, serif;\r
+                               font-size: 20px;\r
+                               font-weight: normal;\r
+                       }\r
+                       \r
+                       H2 {\r
+                               font-family: Georgia, serif;\r
+                               font-size: 16px;\r
+                               font-weight: normal;\r
+                               margin: 0px 0px 10px 0px;\r
+                       }\r
+                       \r
+                       #myDiv {\r
+                               width: 150px;\r
+                               border: solid 1px #2AA7DE;\r
+                               background: #6CC8EF;\r
+                               text-align: center;\r
+                               padding: 4em .5em;\r
+                               margin: 1em;\r
+                               float: left;\r
+                       }\r
+                       \r
+                       #myList {\r
+                               margin: 1em;\r
+                               float: left;\r
+                       }\r
+                       \r
+                       #myList UL {\r
+                               padding: 0px;\r
+                               margin: 0em 1em;\r
+                       }\r
+                       \r
+                       #myList LI {\r
+                               width: 100px;\r
+                               border: solid 1px #2AA7DE;\r
+                               background: #6CC8EF;\r
+                               padding: 5px 5px;\r
+                               margin: 2px 0px;\r
+                               list-style: none;\r
+                       }\r
+                       \r
+                       #options {\r
+                               clear: left;\r
+                       }\r
+                       \r
+                       #options INPUT {\r
+                               font-family: Verdana, Arial, Helvetica, sans-serif;\r
+                               font-size: 11px;\r
+                               width: 150px;\r
+                       }\r
+                       \r
+               </style>                \r
+               \r
+               <script src="../js/lib/jquery-1.4.2.min.js" type="text/javascript"></script>\r
+               <script src="jquery.contextMenu.js" type="text/javascript"></script>\r
+               <link href="jquery.contextMenu.css" rel="stylesheet" type="text/css" />\r
+               \r
+               <script type="text/javascript">\r
+                       \r
+                       $(document).ready( function() {\r
+                               \r
+                               // Show menu when #myDiv is clicked\r
+                               $("#myDiv").contextMenu({\r
+                                       menu: 'myMenu'\r
+                               },\r
+                                       function(action, el, pos) {\r
+                                       alert(\r
+                                               'Action: ' + action + '\n\n' +\r
+                                               'Element ID: ' + $(el).attr('id') + '\n\n' + \r
+                                               'X: ' + pos.x + '  Y: ' + pos.y + ' (relative to element)\n\n' + \r
+                                               'X: ' + pos.docX + '  Y: ' + pos.docY+ ' (relative to document)'\r
+                                               );\r
+                               });\r
+                               \r
+                               // Show menu when a list item is clicked\r
+                               $("#myList UL LI").contextMenu({\r
+                                       menu: 'myMenu'\r
+                               }, function(action, el, pos) {\r
+                                       alert(\r
+                                               'Action: ' + action + '\n\n' +\r
+                                               'Element text: ' + $(el).text() + '\n\n' + \r
+                                               'X: ' + pos.x + '  Y: ' + pos.y + ' (relative to element)\n\n' + \r
+                                               'X: ' + pos.docX + '  Y: ' + pos.docY+ ' (relative to document)'\r
+                                               );\r
+                               });\r
+                               \r
+                               // Disable menus\r
+                               $("#disableMenus").click( function() {\r
+                                       $('#myDiv, #myList UL LI').disableContextMenu();\r
+                                       $(this).attr('disabled', true);\r
+                                       $("#enableMenus").attr('disabled', false);\r
+                               });\r
+                               \r
+                               // Enable menus\r
+                               $("#enableMenus").click( function() {\r
+                                       $('#myDiv, #myList UL LI').enableContextMenu();\r
+                                       $(this).attr('disabled', true);\r
+                                       $("#disableMenus").attr('disabled', false);\r
+                               });\r
+                               \r
+                               // Disable cut/copy\r
+                               $("#disableItems").click( function() {\r
+                                       $('#myMenu').disableContextMenuItems('#cut,#copy');\r
+                                       $(this).attr('disabled', true);\r
+                                       $("#enableItems").attr('disabled', false);\r
+                               });\r
+                               \r
+                               // Enable cut/copy\r
+                               $("#enableItems").click( function() {\r
+                                       $('#myMenu').enableContextMenuItems('#cut,#copy');\r
+                                       $(this).attr('disabled', true);\r
+                                       $("#disableItems").attr('disabled', false);\r
+                               });                             \r
+                               \r
+                       });\r
+                       \r
+               </script>\r
+       </head>\r
+       \r
+       <body>\r
+               \r
+               <h1>jQuery Context Menu Plugin Demo</h1>\r
+               <p>\r
+                       This plugin lets you add context menu functionality to your web applications.\r
+               </p>\r
+               \r
+               <p>\r
+                       <strong>Tip:</strong> Try using your keyboard to make a selection.\r
+               </p>\r
+               \r
+               <p>\r
+                       <a href="http://abeautifulsite.net/2008/09/jquery-context-menu-plugin/">Back to the project page</a>\r
+               </p>\r
+               \r
+               <h2>Demo</h2>\r
+               \r
+               <div id="myDiv">\r
+                       Right click to view the context menu\r
+               </div>\r
+               \r
+               <div id="myList">\r
+                       <ul>\r
+                               <li>Item 1</li>\r
+                               <li>Item 2</li>\r
+                               <li>Item 3</li>\r
+                               <li>Item 4</li>\r
+                               <li>Item 5</li>\r
+                               <li>Item 6</li>\r
+                       </ul>\r
+               </div>\r
+               \r
+               <div id="options">\r
+                       <p>\r
+                               <input type="button" id="disableItems" value="Disable Cut/Copy" />\r
+                               <input type="button" id="enableItems" value="Enable Cut/Copy" disabled="disabled" />\r
+                       </p>\r
+                       \r
+                       <p>\r
+                               <input type="button" id="disableMenus" value="Disable Context Menus" />\r
+                               <input type="button" id="enableMenus" value="Enable Context Menus" disabled="disabled" />\r
+                       </p>\r
+               </div>\r
+               \r
+               <ul id="myMenu" class="contextMenu">\r
+                       <li class="edit"><a href="#edit">Edit</a></li>\r
+                       <li class="cut separator"><a href="#cut">Cut</a>\r
+                         <ul>\r
+                           <li class="pe"><a href="#pe">Pe</a></li>\r
+                         </ul>\r
+                       </li>\r
+                       <li class="copy"><a href="#copy">Copy</a></li>\r
+                       <li class="paste"><a href="#paste">Paste</a></li>\r
+                       <li class="delete"><a href="#delete">Delete</a></li>\r
+                       <li class="quit separator"><a href="#quit">Quit</a></li>\r
+               </ul>\r
+               \r
+       </body>\r
+</html>\r
diff --git a/src/redakcja/static/contextmenu2/jquery.contextMenu.css b/src/redakcja/static/contextmenu2/jquery.contextMenu.css
new file mode 100644 (file)
index 0000000..5b2dd90
--- /dev/null
@@ -0,0 +1,62 @@
+/* Generic context menu styles */\r
+.contextMenu {\r
+       position: absolute;\r
+       width: 120px;\r
+       z-index: 99999;\r
+       border: solid 1px #CCC;\r
+       background: #EEE;\r
+       padding: 0px;\r
+       margin: 0px;\r
+       display: none;\r
+}\r
+\r
+.contextMenu LI {\r
+       list-style: none;\r
+       padding: 0px;\r
+       margin: 0px;\r
+}\r
+\r
+.contextMenu A {\r
+       color: #333;\r
+       text-decoration: none;\r
+       display: block;\r
+       line-height: 20px;\r
+       height: 20px;\r
+       background-position: 6px center;\r
+       background-repeat: no-repeat;\r
+       outline: none;\r
+       padding: 1px 5px;\r
+       padding-left: 28px;\r
+}\r
+\r
+.contextMenu LI.hover A {\r
+       color: #FFF;\r
+       background-color: #3399FF;\r
+}\r
+\r
+.contextMenu LI.disabled A {\r
+       color: #AAA;\r
+       cursor: default;\r
+}\r
+\r
+.contextMenu LI.hover.disabled A {\r
+       background-color: transparent;\r
+}\r
+\r
+.contextMenu LI.separator {\r
+       border-top: solid 1px #CCC;\r
+}\r
+\r
+/*\r
+       Adding Icons\r
+       \r
+       You can add icons to the context menu by adding\r
+       classes to the respective LI element(s)\r
+*/\r
+\r
+.contextMenu LI.edit A { background-image: url(images/page_white_edit.png); }\r
+.contextMenu LI.cut A { background-image: url(images/cut.png); }\r
+.contextMenu LI.copy A { background-image: url(images/page_white_copy.png); }\r
+.contextMenu LI.paste A { background-image: url(images/page_white_paste.png); }\r
+.contextMenu LI.delete A { background-image: url(images/page_white_delete.png); }\r
+.contextMenu LI.quit A { background-image: url(images/door.png); }\r
diff --git a/src/redakcja/static/contextmenu2/jquery.contextMenu.js b/src/redakcja/static/contextmenu2/jquery.contextMenu.js
new file mode 100644 (file)
index 0000000..59c1737
--- /dev/null
@@ -0,0 +1,211 @@
+// jQuery Context Menu Plugin\r
+//\r
+// Version 1.01\r
+//\r
+// Cory S.N. LaViska\r
+// A Beautiful Site (http://abeautifulsite.net/)\r
+//\r
+// More info: http://abeautifulsite.net/2008/09/jquery-context-menu-plugin/\r
+//\r
+// Terms of Use\r
+//\r
+// This plugin is dual-licensed under the GNU General Public License\r
+//   and the MIT License and is copyright A Beautiful Site, LLC.\r
+//\r
+if(jQuery)( function() {\r
+       $.extend($.fn, {\r
+               \r
+               contextMenu: function(o, callback) {\r
+                       // Defaults\r
+                       if( o.menu == undefined ) return false;\r
+                       if( o.inSpeed == undefined ) o.inSpeed = 150;\r
+                       if( o.outSpeed == undefined ) o.outSpeed = 75;\r
+                       // 0 needs to be -1 for expected results (no fade)\r
+                       if( o.inSpeed == 0 ) o.inSpeed = -1;\r
+                       if( o.outSpeed == 0 ) o.outSpeed = -1;\r
+                       // Loop each context menu\r
+                       $(this).each( function() {\r
+                               var el = $(this);\r
+                               var offset = $(el).offset();\r
+                               // Add contextMenu class\r
+                               $('#' + o.menu).addClass('contextMenu');\r
+                               // Simulate a true right click\r
+                               $(this).mousedown( function(e) {\r
+                                       var evt = e;\r
+                                       evt.stopPropagation();\r
+                                       $(this).mouseup( function(e) {\r
+                                               e.stopPropagation();\r
+                                               var srcElement = $(this);\r
+                                               $(this).unbind('mouseup');\r
+                                               if( evt.button == 2 ) {\r
+                                                       // Hide context menus that may be showing\r
+                                                       $(".contextMenu").hide();\r
+                                                       // Get this context menu\r
+                                                       var menu = $('#' + o.menu);\r
+                                                       \r
+                                                       if( $(el).hasClass('disabled') ) return false;\r
+                                                       \r
+                                                       // Detect mouse position\r
+                                                       var d = {}, x, y;\r
+                                                       if( self.innerHeight ) {\r
+                                                               d.pageYOffset = self.pageYOffset;\r
+                                                               d.pageXOffset = self.pageXOffset;\r
+                                                               d.innerHeight = self.innerHeight;\r
+                                                               d.innerWidth = self.innerWidth;\r
+                                                       } else if( document.documentElement &&\r
+                                                               document.documentElement.clientHeight ) {\r
+                                                               d.pageYOffset = document.documentElement.scrollTop;\r
+                                                               d.pageXOffset = document.documentElement.scrollLeft;\r
+                                                               d.innerHeight = document.documentElement.clientHeight;\r
+                                                               d.innerWidth = document.documentElement.clientWidth;\r
+                                                       } else if( document.body ) {\r
+                                                               d.pageYOffset = document.body.scrollTop;\r
+                                                               d.pageXOffset = document.body.scrollLeft;\r
+                                                               d.innerHeight = document.body.clientHeight;\r
+                                                               d.innerWidth = document.body.clientWidth;\r
+                                                       }\r
+                                                       (e.pageX) ? x = e.pageX : x = e.clientX + d.scrollLeft;\r
+                                                       (e.pageY) ? y = e.pageY : y = e.clientY + d.scrollTop;\r
+                                                       \r
+                                                       // Show the menu\r
+                                                       $(document).unbind('click');\r
+                                                       $(menu).css({ top: y, left: x }).fadeIn(o.inSpeed);\r
+                                                       // Hover events\r
+                                                       $(menu).find('A').mouseover( function() {\r
+                                                               $(menu).find('LI.hover').removeClass('hover');\r
+                                                               $(this).parent().addClass('hover');\r
+                                                       }).mouseout( function() {\r
+                                                               $(menu).find('LI.hover').removeClass('hover');\r
+                                                       });\r
+                                                       \r
+                                                       // Keyboard\r
+                                                       $(document).keypress( function(e) {\r
+                                                               switch( e.keyCode ) {\r
+                                                                       case 38: // up\r
+                                                                               if( $(menu).find('LI.hover').size() == 0 ) {\r
+                                                                                       $(menu).find('LI:last').addClass('hover');\r
+                                                                               } else {\r
+                                                                                       $(menu).find('LI.hover').removeClass('hover').prevAll('LI:not(.disabled)').eq(0).addClass('hover');\r
+                                                                                       if( $(menu).find('LI.hover').size() == 0 ) $(menu).find('LI:last').addClass('hover');\r
+                                                                               }\r
+                                                                       break;\r
+                                                                       case 40: // down\r
+                                                                               if( $(menu).find('LI.hover').size() == 0 ) {\r
+                                                                                       $(menu).find('LI:first').addClass('hover');\r
+                                                                               } else {\r
+                                                                                       $(menu).find('LI.hover').removeClass('hover').nextAll('LI:not(.disabled)').eq(0).addClass('hover');\r
+                                                                                       if( $(menu).find('LI.hover').size() == 0 ) $(menu).find('LI:first').addClass('hover');\r
+                                                                               }\r
+                                                                       break;\r
+                                                                       case 13: // enter\r
+                                                                               $(menu).find('LI.hover A').trigger('click');\r
+                                                                       break;\r
+                                                                       case 27: // esc\r
+                                                                               $(document).trigger('click');\r
+                                                                       break\r
+                                                               }\r
+                                                       });\r
+                                                       \r
+                                                       // When items are selected\r
+                                                       $('#' + o.menu).find('A').unbind('click');\r
+                                                       $('#' + o.menu).find('LI:not(.disabled) A').click( function() {\r
+                                                               $(document).unbind('click').unbind('keypress');\r
+                                                               $(".contextMenu").hide();\r
+                                                               // Callback\r
+                                                               if( callback ) callback( $(this).attr('href').substr(1), $(srcElement), {x: x - offset.left, y: y - offset.top, docX: x, docY: y} );\r
+                                                               return false;\r
+                                                       });\r
+                                                       \r
+                                                       // Hide bindings\r
+                                                       setTimeout( function() { // Delay for Mozilla\r
+                                                               $(document).click( function() {\r
+                                                                       $(document).unbind('click').unbind('keypress');\r
+                                                                       $(menu).fadeOut(o.outSpeed);\r
+                                                                       return false;\r
+                                                               });\r
+                                                       }, 0);\r
+                                               }\r
+                                       });\r
+                               });\r
+                               \r
+                               // Disable text selection\r
+                               if( $.browser.mozilla ) {\r
+                                       $('#' + o.menu).each( function() { $(this).css({ 'MozUserSelect' : 'none' }); });\r
+                               } else if( $.browser.msie ) {\r
+                                       $('#' + o.menu).each( function() { $(this).bind('selectstart.disableTextSelect', function() { return false; }); });\r
+                               } else {\r
+                                       $('#' + o.menu).each(function() { $(this).bind('mousedown.disableTextSelect', function() { return false; }); });\r
+                               }\r
+                               // Disable browser context menu (requires both selectors to work in IE/Safari + FF/Chrome)\r
+                               $(el).add($('UL.contextMenu')).bind('contextmenu', function() { return false; });\r
+                               \r
+                       });\r
+                       return $(this);\r
+               },\r
+               \r
+               // Disable context menu items on the fly\r
+               disableContextMenuItems: function(o) {\r
+                       if( o == undefined ) {\r
+                               // Disable all\r
+                               $(this).find('LI').addClass('disabled');\r
+                               return( $(this) );\r
+                       }\r
+                       $(this).each( function() {\r
+                               if( o != undefined ) {\r
+                                       var d = o.split(',');\r
+                                       for( var i = 0; i < d.length; i++ ) {\r
+                                               $(this).find('A[href="' + d[i] + '"]').parent().addClass('disabled');\r
+                                               \r
+                                       }\r
+                               }\r
+                       });\r
+                       return( $(this) );\r
+               },\r
+               \r
+               // Enable context menu items on the fly\r
+               enableContextMenuItems: function(o) {\r
+                       if( o == undefined ) {\r
+                               // Enable all\r
+                               $(this).find('LI.disabled').removeClass('disabled');\r
+                               return( $(this) );\r
+                       }\r
+                       $(this).each( function() {\r
+                               if( o != undefined ) {\r
+                                       var d = o.split(',');\r
+                                       for( var i = 0; i < d.length; i++ ) {\r
+                                               $(this).find('A[href="' + d[i] + '"]').parent().removeClass('disabled');\r
+                                               \r
+                                       }\r
+                               }\r
+                       });\r
+                       return( $(this) );\r
+               },\r
+               \r
+               // Disable context menu(s)\r
+               disableContextMenu: function() {\r
+                       $(this).each( function() {\r
+                               $(this).addClass('disabled');\r
+                       });\r
+                       return( $(this) );\r
+               },\r
+               \r
+               // Enable context menu(s)\r
+               enableContextMenu: function() {\r
+                       $(this).each( function() {\r
+                               $(this).removeClass('disabled');\r
+                       });\r
+                       return( $(this) );\r
+               },\r
+               \r
+               // Destroy context menu(s)\r
+               destroyContextMenu: function() {\r
+                       // Destroy specified context menus\r
+                       $(this).each( function() {\r
+                               // Disable action\r
+                               $(this).unbind('mousedown').unbind('mouseup');\r
+                       });\r
+                       return( $(this) );\r
+               }\r
+               \r
+       });\r
+})(jQuery);
\ No newline at end of file
diff --git a/src/redakcja/static/css/book.css b/src/redakcja/static/css/book.css
new file mode 100644 (file)
index 0000000..f685e41
--- /dev/null
@@ -0,0 +1,432 @@
+body {
+    font-size: 16px;
+    font-family: Georgia, "Times New Roman", serif;
+    line-height: 1.5em;
+    margin: 0;
+}
+
+a {
+    color: blue;
+    text-decoration: none;
+}
+
+#book-text {
+    margin: 3em;
+    max-width: 48em;
+}
+
+table {
+    border-collapse: collapse;
+    width: 100%;
+}
+
+table.border, table.border td, table.border th {
+    border: 1px solid black;
+}
+
+td, th {
+    vertical-align: top;
+}
+
+/* ================================== */
+/* = Header with logo and menu      = */
+/* ================================== */
+#header {
+    margin: 3.4em 0 0 1.4em;
+}
+
+img {
+    border: none;
+    max-width: 48em;
+    margin-top: 1em;
+}
+
+#logo {
+    font-size: 1.5em;
+}
+#logo a {
+    color: black;
+}
+
+#menu {
+    position: fixed;
+    left: 0;
+    top: 0;
+    width: 100%;
+    height: 1.5em;
+    background: #333;
+    color: #FFF;
+    opacity: 0.9;
+    z-index: 99;
+}
+
+#menu ul {
+    list-style: none;
+    padding: 0;
+    margin: 0;
+}
+
+#menu li a {
+    display: block;
+    float: left;
+    height: 1.5em;
+    margin-left: 0.5em;
+    text-align: center;
+    color: #FFF;
+    padding: 0 1em;
+}
+#menu li a.menu {
+    padding-right: 1.5em;
+}
+
+#menu li a.menu:hover, #menu li a.menu:active {
+    color: #000;
+    background: #FFF;
+}
+
+#menu li a.menu.selected {
+    color: #000;
+    background: #FFF;
+}
+#menu a.menu-link {
+    display: block;
+    float: left;
+    height: 1.5em;
+    margin-left: 0.5em;
+    text-align: center;
+    color: #FFF;
+}
+#menu span {
+    color: #888;
+    font-style: italic;
+    font-size: .75em;
+    margin-right: 0.5em;
+}
+
+
+#toc, #themes, #nota_red, #info {
+    position: fixed;
+    left: 0;
+    top: 1.5em;
+    width: 37em;
+    padding: 1.5em;
+    background: #FFF;
+    border-bottom: 0.25em solid #DDD;
+    border-right: 0.25em solid #DDD;
+    display: none;
+    height: 16em;
+    overflow-x: hidden;
+    overflow-y: auto;
+    opacity: 0.9;
+    z-index: 99;
+}
+#download {
+    position: fixed;
+    left: 0;
+    top: 1.5em;
+    width: 37em;
+    padding: 1.5em;
+    background: #FFF;
+    border-bottom: 0.25em solid #DDD;
+    border-right: 0.25em solid #DDD;
+    display: none;
+    height: 10em;
+    overflow-x: hidden;
+    overflow-y: auto;
+    opacity: 0.9;
+    z-index: 99;
+}
+
+#toc ol, #themes ol {
+    list-style: none;
+    padding: 0;
+    margin: 0;
+}
+
+#toc ol li {
+    font-weight: bold;
+}
+
+#toc ol ol {
+    padding: 0 0 1.5em 1.5em;
+    margin: 0;
+}
+
+#toc ol ol li {
+    font-weight: normal;
+}
+
+#toc h2 {
+    display: none;
+}
+
+#toc .anchor {
+    float: none;
+    margin: 0;
+    color: blue;
+    font-size: 16px;
+    position: inherit;
+}
+
+#info p {
+    text-align: justify;
+    margin: 1.5em 0 0;
+}
+
+/* =================================================== */
+/* = Common elements: headings, paragraphs and lines = */
+/* =================================================== */
+h1 {
+    font-size: 3em;
+    margin: 1.5em 0;
+    text-align: center;
+    line-height: 1.5em;
+    font-weight: bold;
+}
+
+h2 {
+    font-size: 2em;
+    margin: 1.5em 0 0;
+    font-weight: bold;
+    line-height: 1.5em;
+}
+
+h3 {
+    font-size: 1.5em;
+    margin: 1.5em 0 0;
+    font-weight: normal;
+    line-height: 1.5em;
+}
+
+h4 {
+    font-size: 1em;
+    margin: 1.5em 0 0;
+    line-height: 1.5em;
+}
+
+p {
+    margin: 0;
+}
+
+/* ======================== */
+/* = Footnotes and themes = */
+/* ======================== */
+.theme-begin {
+    border-left: 0.1em solid #DDDDDD;
+    color: #777;
+    padding: 0 0.5em;
+    width: 7.5em;
+
+    font-style: normal;
+    font-weight: normal;
+    font-variant: normal;
+    letter-spacing: 0;
+    text-transform: none;
+    text-decoration: none;
+
+    font-size: 16px;
+    float: right;
+    margin-right: -9.5em;
+    margin-bottom: 0.5em;
+    clear: both;
+    left: 40em;
+    line-height: 1.5em;
+    text-align: left;
+}
+
+.annotation {
+    font-style: normal;
+    font-weight: normal;
+    font-size: 12px;
+    padding-left: 2px;
+    position: relative;
+    top: -4px;
+}
+
+#footnotes {
+    margin-top: 3em;
+}
+
+#footnotes .annotation {
+    display: block;
+    float: left;
+    width: 2.5em;
+    clear: both;
+}
+
+#footnotes div {
+    margin: 1.5em 0 0 0;
+}
+
+#footnotes p, #footnotes ul {
+    margin-left: 2.5em;
+    font-size: 0.875em;
+}
+
+#footnotes .permalink {
+    font-size: .75em;
+}
+
+blockquote {
+    font-size: 0.875em;
+}
+
+/* ============= */
+/* = Numbering = */
+/* ============= */
+.verse, .paragraph {
+    position:relative;
+}
+.anchor {
+    position: absolute;
+    margin: -0.25em -0.5em;
+    left: -3em;
+    color: #777;
+    font-size: 12px;
+    width: 2em;
+    text-align: center;
+    padding: 0.25em 0.5em;
+    line-height: 1.5em;
+}
+
+.anchor:hover, #book-text .anchor:active {
+    color: #FFF;
+    background-color: #CCC;
+}
+
+/* =================== */
+/* = Custom elements = */
+/* =================== */
+span.author {
+    font-size: 0.5em;
+    display: block;
+    line-height: 1.5em;
+    margin-bottom: 0.25em;
+}
+
+span.collection {
+    font-size: 0.375em;
+    display: block;
+    line-height: 1.5em;
+    margin-bottom: -0.25em;
+}
+
+span.subtitle {
+    font-size: 0.5em;
+    display: block;
+    line-height: 1.5em;
+    margin-top: -0.25em;
+}
+
+span.translator {
+    font-size: 0.375em;
+    display: block;
+    line-height: 1.5em;
+    margin-top: 0.25em;
+}
+
+div.didaskalia {
+    font-style: italic;
+    margin: 0.5em 0 0 1.5em;
+}
+
+div.kwestia {
+    margin: 0.5em 0 0;
+}
+
+div.stanza {
+    margin: 1.5em 0 0;
+}
+
+div.kwestia div.stanza {
+    margin: 0;
+}
+
+p.paragraph {
+    text-align: justify;
+    margin: 1.5em 0 0;
+}
+
+p.motto {
+    text-align: justify;
+    font-style: italic;
+    margin: 1.5em 0 0;
+}
+
+p.motto_podpis {
+    font-size: 0.875em;
+    text-align: right;
+}
+
+div.fragment {
+    border-bottom: 0.1em solid #999;
+    padding-bottom: 1.5em;
+}
+
+div.note p, div.dedication p, div.note p.paragraph, div.dedication p.paragraph {
+    text-align: right;
+    font-style: italic;
+}
+
+div.frame {
+    border: 1px darkgray solid;
+    padding-bottom: 1.5em;
+}
+
+hr.spacer {
+    height: 3em;
+    visibility: hidden;
+}
+
+hr.spacer-line {
+    margin: 1.5em 0;
+    border: none;
+    border-bottom: 0.1em solid #000;
+}
+
+p.spacer-asterisk {
+    padding: 0;
+    margin: 1.5em 0;
+    text-align: center;
+}
+
+div.person-list ol {
+    list-style: none;
+    padding: 0 0 0 1.5em;
+}
+
+p.place-and-time {
+    font-style: italic;
+}
+
+em.math, em.foreign-word, em.book-title, em.didaskalia {
+    font-style: italic;
+}
+
+em.author-emphasis {
+    letter-spacing: 0.1em;
+}
+
+em.person {
+    font-style: normal;
+    font-variant: small-caps;
+}
+
+em.wieksze-odstepy
+{
+    font-style: normal;
+    word-spacing: 1em;
+}
+
+.verse:after {
+    content: "\feff";
+}
+
+
+/* =================================== */
+/* = Hide some elements for printing = */
+/* =================================== */
+
+@media print {
+    #menu {display: none;}
+}
diff --git a/src/redakcja/static/css/book_list.css b/src/redakcja/static/css/book_list.css
new file mode 100644 (file)
index 0000000..86a0de5
--- /dev/null
@@ -0,0 +1,6 @@
+/* icons used in the contex menu of book list */
+
+.context-menu-item.icon-user { background-image: url(../icons/user.png); }
+.context-menu-item.icon-clock { background-image: url(../icons/clock.png); }
+.context-menu-item.icon-ok { background-image: url(../icons/ok.png); }
+.context-menu-item.icon-stop { background-image: url(../icons/stop.png); }
diff --git a/src/redakcja/static/css/dialogs.css b/src/redakcja/static/css/dialogs.css
new file mode 100644 (file)
index 0000000..1032b13
--- /dev/null
@@ -0,0 +1,32 @@
+.dialog {
+       display: none;
+       padding: 5px;
+       text-align: left;
+       cursor: auto;
+       background-color: #E1E1E1; /* #e0ffb8; */
+}
+
+.dialog .help_text {
+       font-size: 11px;
+       color: #2e3536;
+}
+
+.dialog .action_area {
+       padding: 1em 0.5em 0.5em;
+       border-top: 1px solid black;
+       margin-top: 0.5em;
+}
+
+.dialog p {
+       margin: 0.5em;
+}
+
+*[data-ui-error-for] {
+       color: red;
+       font-weight: bold;
+}
+
+#save_dialog textarea, #revert_dialog textarea {
+       width: 90%;
+       margin: 0.2em 4%;
+}
\ No newline at end of file
diff --git a/src/redakcja/static/css/filelist.css b/src/redakcja/static/css/filelist.css
new file mode 100644 (file)
index 0000000..b5e4fc4
--- /dev/null
@@ -0,0 +1,238 @@
+/*
+    Document   : filelist
+    Created on : 2009-09-04, 20:44:44
+    Author     : lreqc
+    Description:
+        Dodatkowe style dla listy plików na stronie głównej.
+*/
+
+body {
+    margin: 0;
+    font-family: verdana, sans-serif;
+    font-size: 10px;
+}
+
+img {
+    border: 0;
+}
+
+th {
+    text-align: left;
+}
+
+td {
+    vertical-align: top;
+    padding: 0 3px;
+}
+.clr {
+    clear: both;
+}
+
+#tabs-nav {
+    padding: 5px 5px 0 10px;
+    background: #ffdfbf;
+    border-bottom: 1px solid #ff8000;
+    position: relative;
+}
+
+#tabs-nav-left {
+    margin-left: 60px;
+}
+
+#tabs-nav-left a {
+    display: block;
+    float: left;
+    padding: 5px 20px 5px 20px;
+    margin-bottom: -1px;
+    border-width: 1px;
+    border-style: solid;
+    border-color: transparent;
+}
+
+#tabs-nav-left .active {
+    background: white;
+    border-color: #ff8000 #ff8000 white #ff8000;
+}
+
+.section {
+    border-top: 1px solid #ffdfbf;
+    margin-top: 2em;
+    padding-top: 1em;
+}
+
+.editable td {
+    padding: 1px;
+}
+.editable input, .editable select, .editable textarea {
+    width: 400px;
+}
+.editable .number-input {
+    width: 100px;
+}
+
+
+#login-box {
+    float: right;
+}
+
+#logo {
+    position: absolute;
+    bottom: 0;
+}
+
+#content {
+    padding: 10px;
+}
+
+
+
+#catalogue_layout_left_column {
+       overflow: visible;
+       float: left;
+       max-width: 60%;
+       padding-right: 2%;
+       border-right: 1px dashed black;
+
+}
+
+#catalogue_layout_right_column {
+       float: left;
+       max-width: 30%;
+       margin-left: 5%;
+}
+
+#last-edited-list ul {
+       margin: 0px;
+}
+
+#last-edited-list li {
+       margin-bottom: 1em;
+}
+
+#last-edited-list .date {
+       font-size: 70%;
+       color: #808080;
+}
+
+a, a:visited, a:active {
+       color: #a05000;
+       text-decoration: none;
+}
+
+a:hover {
+       text-decoration: underline;
+}
+
+
+
+.error {
+    color: red;
+    font-weight: bold;
+}
+
+.success {
+    color: green;
+}
+
+#error-list {
+    color: red;
+}
+
+#skipped-list {
+    color: #666;
+}
+
+.chunkno {
+    font-size: .7em;
+    padding-left: 2em;
+}
+
+
+/* Big cheesy publish button */
+#publish-button {
+        color: black;
+        border: 2px solid black; 
+        border-radius: 20px;
+        box-shadow: 0px 0px 15px #88f;
+        /*moz-border-radius: 20px;
+        -moz-box-shadow: 10px 10px 5px #888;*/
+        font-size:1.5em; 
+        padding: 1em; 
+        background: -moz-linear-gradient(top,  #fff,  #44f);
+        -moz-transition: all 0.5s ease-in-out;        
+        margin: 20px;
+}
+
+#publish-button:hover {
+    -moz-transition: all 0.5s ease-in-out;        
+    -moz-transform: scale(1.1);
+    background: -moz-linear-gradient(top,  #fff,  #88f);
+    -moz-box-shadow: 0px 0px 30px #ff8;
+}
+
+#publish-button:active {
+    background: -moz-linear-gradient(top,  #88f,  #fff);
+}
+
+
+/* book list */
+
+.book-search-column input {
+    width: 96%;
+}
+
+.book-list-user .user-column {
+    display: none;
+}
+
+
+/* wall */
+.wall {
+    padding-left: 0;
+    list-style: none;
+}
+
+.wall li {
+    clear: left;
+    border-top: 1px dotted gray;
+    padding: 0 1em 2em 1em;
+    margin-bottom: 0;
+}
+
+.wall .gravatar {
+    float: left;
+    margin-right: 1em;
+    margin-left: -1em;
+}
+.wall h3 {
+    font-size: 1.25em;
+    margin-top: 0;
+}
+
+.wall .time {
+    /* float:right; */
+}
+
+.wall .anonymous {
+    background-color: #efa;
+}
+
+.wall .comment {
+    background-color: #dfc;
+}
+
+.wall .publish {
+    background-color: #fdc;
+}
+
+div.cover-preview {
+       width: 216px;
+       min-height: 300px;
+       float: left;
+       margin-right: 2em;
+}
+
+img.cover-preview {
+       width: 216px;
+       height: 300px;
+}
diff --git a/src/redakcja/static/css/gallery.css b/src/redakcja/static/css/gallery.css
new file mode 100644 (file)
index 0000000..08e9318
--- /dev/null
@@ -0,0 +1,135 @@
+#sidebar {
+    position: absolute;
+    top: 0px;
+    right: 0px;
+    bottom: 0px;
+    width: 0px;
+    background-color: #FFF;
+}
+
+#side-search {
+    height: 100%;
+    display: none;
+    background-color: #C1C1C1;
+}
+
+#side-search p {
+    padding: .5em;
+}
+
+#side-annotations {
+    display: none;
+}
+
+.annotations-list {
+    position: absolute;
+    left: 0;
+    top: 28px;
+    right: 0;
+    bottom: 0;
+    overflow: auto;
+    padding: 1em 1em 0 1em;
+}
+
+#side-annotations .active {
+    font-weight: bold;
+}
+
+#side-annotations .spinner {
+    position: absolute;
+    left: 0;
+    top: 28px;
+    right: 0;
+    bottom: 0;
+    text-align:center;
+    padding-top: 50%;
+    background: white;
+}
+
+#side-annotations .spinner img {
+    margin-top: -15px;
+}
+
+#side-annotations .akap {
+    font-size: 16px;
+    font-family: "Georgia", "Times New Roman", serif;
+    line-height: 1.5em;
+    margin-bottom: 1em;
+    position: relative;
+}
+
+#side-annotations .src {
+    font-family: monospace;
+    font-size: 10px;
+    font-weight: 400;
+    line-height: 13px;
+    position: absolute;
+    top: 0;
+    bottom: -.5em;
+    display:none;
+    background: white;
+    border: 1px solid #888;
+    width: 100%;
+}
+
+#side-annotations .akap:hover .src {
+    display: block;
+}
+
+/* =========== */
+/* = Gallery = */
+/* =========== */
+
+#side-gallery .error_message
+{
+       background-color: white;
+       color: black;
+       padding: 0.5em 1em;
+}
+
+.change-gallery-panel {
+    width: 100%;
+    border-bottom: 1px solid #777;
+    background-color: #C1C1C1;
+    margin: 0;
+    padding: 2px;
+    z-index: 10;
+}
+
+.gallery-image {
+    position: absolute;
+    top: 30px;
+    right: 0;
+    bottom: 0;
+    left: 0;
+    margin: 0;
+    border: none;
+    background-color: transparent;
+    overflow: hidden;
+}
+
+.gallery-image img {
+    position: absolute;
+    -moz-drag: none;
+    user-select: none;
+    -webkit-user-select: none;
+    -khtml-user-select: none;
+    -moz-user-select: none;
+    cursor: pointer;
+
+       background-color: white;
+       min-height: 100px;
+       min-width: 100px;
+}
+
+.gallery-image img[src=''] {
+       background-color: red;
+}
+
+#motifs-editor .toolbar {
+    height: auto;
+}
+
+#objects-editor .toolbar {
+    height: auto;
+}
diff --git a/src/redakcja/static/css/history.css b/src/redakcja/static/css/history.css
new file mode 100644 (file)
index 0000000..cbc5c52
--- /dev/null
@@ -0,0 +1,159 @@
+#history-view {
+    overflow: auto;
+    position: absolute;
+    top: 30px; /* 0 for no toolbar, 30 for toolbar */
+    bottom: 0px;
+    left: 0px;
+    right: 0px;
+    z-index: 1;
+}
+
+/*
+ * File History
+ */
+#changes-list-container {
+    margin: 1em auto;
+    width: 70%;
+}
+
+table#changes-list-container {
+    border-spacing: 0px 15px;
+}
+
+#changes-list {
+    margin: 0.5em 0.2em;
+}
+
+#changes-list td {
+    padding: 0.5em 1em;
+}
+
+#changes-list .entry {
+    position: relative;
+    padding: 0.5em;
+    padding-left: 3em;
+    margin: 0.5em;
+}
+
+#changes-list .entry:hover {
+    background-color: #f0f0f0;
+}
+
+#changes-list .entry.selected {
+    background-color: #ffec63;
+}
+
+#changes-list .tag {
+    display: inline-block;
+    visibility: hidden;
+    width: 60px;
+    margin: 0 0.5em 0 0;
+    font-size: 11px;
+    padding: 3px 2px;
+    text-align: center;
+    color: black;
+    background: #add8e6;
+    cursor: pointer;
+    vertical-align: middle;
+    -moz-border-radius: 10px;
+    -webkit-border-radius: 10px;
+    border-radius: 10px;
+}
+
+#changes-list .entry:hover .tag {
+    visibility: visible;
+}
+
+#changes-list .tag:hover {
+    background: #bde8f6;
+}
+
+#changes-list *[data-version-tag] {
+    visibility: visible;
+    border: 1px solid black;
+    color: black;
+}
+
+#changes-list *[data-stub-value =
+'version'] {
+    font-weight: bold;
+}
+
+#changes-list td {
+    vertical-align: text-top;
+}
+
+#changes-list *[data-stub-value = 'description'] {
+    font-size: .8em;
+    color: gray;
+    white-space: pre-line;
+}
+
+#changes-list *[data-stub-value = 'published'] {
+    font-size: .7em;
+    color: gray;
+}
+
+/*
+ * Graphical DIFF view
+ *
+ */
+#history-view .diff_table {
+    width: 90%;
+}
+
+.editor.DiffPerspective {
+    overflow-y: scroll;
+}
+
+.diff_table {
+    border-width: 1px;
+    border-style: solid;
+    border-color: black;
+    empty-cells: show;
+    border-spacing: 0px;
+}
+
+.diff_table td {
+    border-width: 0px 1px 1px 0px;
+    border-style: dotted;
+    border-color: grey;
+    font-size: 10px;
+    line-height: 20px;
+    font-family: monospace;
+    padding: 0px;
+    white-space: pre-line;
+    /*word-wrap:break-word;
+     word-break:break-all; */
+}
+
+.diff_table th {
+    border-width: 0px 1px 1px 0px;
+    border-style: solid;
+    border-color: black;
+    background: #e5ffe5;
+}
+
+/* .diff_table td.left, .diff_table td.right {
+ width: 50%;
+ }*/
+.diff_table tr.change {
+    background-color: #dcdcdc;
+}
+
+.diff_mark {
+    display: inline-block;
+    padding: 2px;
+}
+
+.diff_mark_removed {
+    background-color: #ff9c94;
+}
+
+.diff_mark_added {
+    background-color: #90ee90;
+}
+
+.diff_mark_changed {
+    background-color: yellow;
+}
diff --git a/src/redakcja/static/css/html.css b/src/redakcja/static/css/html.css
new file mode 100644 (file)
index 0000000..3b99681
--- /dev/null
@@ -0,0 +1,684 @@
+/* Style widoku HTML. Nie należy tu ustawiać position ani marginesów */
+.htmlview {
+    counter-reset: main;
+    font-size: 16px;
+    font-family: "Georgia", "Times New Roman", serif;
+    line-height: 1.5em;
+    padding: 3em 3em 3em 45px;
+    overflow-y: scroll;
+    overflow-x: auto;
+}
+
+.htmlview[data-tag-names-visible] {
+    padding-left: 90px;
+}
+
+/*
+ .htmlview[data-tag-names-visible] *[x-editable]:not(*[x-common])::before {
+ display: block;
+ float: left;
+ clear: left;
+ content: attr(x-node);
+ font-weight: bold;
+ font-style: normal;
+ font-variant:normal;
+ font-size: 8px;
+ line-height: 8px;
+ margin-bottom: 4px;
+
+ padding: 3px 4px;
+ vertical-align: super;
+ background-color:#add8e6;
+ margin-left: -80px;
+ width: 70px;
+ text-align: center;
+
+ -webkit-border-radius: 4px;
+ }
+ */
+.htmlview *[x-node = 'RDF'][x-ns = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'] {
+    display: none;
+}
+
+.htmlview * {
+    position: relative;
+}
+
+.htmlview div {
+    max-width: 36em;
+}
+
+.htmlview #toc {
+    display: none;
+}
+
+.htmlview a {
+    color: blue;
+    text-decoration: none;
+}
+
+.htmlview a:hover {
+    text-decoration: none;
+}
+
+.htmlview h1 {
+    font-size: 3em;
+    margin: 1.5em 0;
+    text-align: center;
+    line-height: 1.5em;
+    font-weight: bold;
+}
+
+.htmlview h2 {
+    font-size: 2em;
+    margin: 1.5em 0 0;
+    font-weight: bold;
+    line-height: 1.5em;
+}
+
+.htmlview h3 {
+    font-size: 1.5em;
+    margin: 1.5em 0 0;
+    font-weight: normal;
+    line-height: 1.5em;
+}
+
+.htmlview h4 {
+    font-size: 1em;
+    margin: 1.5em 0 0;
+    line-height: 1.5em;
+}
+
+.htmlview p {
+    margin: 0;
+}
+
+.htmlview #footnotes div {
+    margin: 1.5em 0 0 0;
+}
+
+.htmlview #footnotes p {
+    margin-left: 2.5em;
+    font-size: 0.875em;
+}
+
+.htmlview blockquote {
+    font-size: 0.875em;
+}
+
+.htmlview pre {
+       overflow-x: auto; /* Use horizontal scroller if needed; for Firefox 2, not needed in Firefox 3 */
+       white-space: pre-wrap; /* css-3 */
+       white-space: -moz-pre-wrap !important; /* Mozilla, since 1999 */
+       white-space: -pre-wrap; /* Opera 4-6 */
+       white-space: -o-pre-wrap; /* Opera 7 */ /*
+       width: 99%; */
+       word-wrap: break-word; /* Internet Explorer 5.5+ */
+}
+
+/* ============= */
+/* = Numbering = */
+/* ============= */
+.htmlview .anchor {
+    position: relative;
+    margin: 0;
+    left: -2.2em;
+    color: #777;
+    font-size: 12px;
+    width: 2em;
+    text-align: center;
+    padding: 0.25em 0.7em;
+    line-height: 1.5em;
+}
+
+.htmlview .anchor:hover, .htmlview .anchor:active {
+    color: #FFF;
+    background-color: #CCC;
+}
+
+/* =================== */
+/* = Custom elements = */
+/* =================== */
+.htmlview .autor_utwor {
+    font-size: 0.5em;
+    display: block;
+    line-height: 1.5em;
+    margin-bottom: 0.25em;
+}
+
+.htmlview .dzielo_nadrzedne {
+    /* */
+}
+
+.htmlview .podtytul {
+    /* */
+}
+
+.htmlview .didaskalia {
+    font-style: italic;
+    margin: 0.5em 0 0 1.5em;
+}
+
+.htmlview .kwestia {
+    margin: 0.5em 0 0;
+}
+
+.htmlview .strofa {
+    margin: 1.5em 0 0.5em auto;
+}
+
+/* wersy */
+.htmlview *[x-verse]:after {
+    content: "\feff";
+}
+
+.htmlview .strofa .wers_wciety, .htmlview .strofa .wers_wciety[data-wlf-typ='1'] {
+    margin-left: 1em;
+}
+
+.htmlview .strofa .wers_wciety[data-wlf-typ='2'], .htmlview .strofa .wers_cd {
+    margin-left: 2em;
+}
+
+.htmlview .strofa .wers_wciety[data-wlf-typ='3'] {
+    margin-left: 3em;
+}
+
+.htmlview .strofa .wers_wciety[data-wlf-typ='4'] {
+    margin-left: 4em;
+}
+
+.htmlview .strofa .wers_wciety[data-wlf-typ='5'] {
+    margin-left: 5em;
+}
+
+.htmlview .strofa .wers_wciety[data-wlf-typ='6'] {
+    margin-left: 6em;
+}
+
+.htmlview .strofa .wers_do_prawej {
+    text-align: right;
+}
+
+/* błędne wersy */
+.htmlview *:not(.strofa) > *[x-verse]::after {
+    content: "Ten wers znajduje się poza strofą.";
+    display: inline;
+    background: red;
+    font-size: 8pt;
+    border: 1px solid black;
+    -moz-border-radius: 10px;
+    -webkit-border-radius: 10px;
+    padding: 1px 1em;
+    margin-left: 1em;
+    vertical-align: super;
+}
+
+.htmlview .kwestia .strofa {
+    margin: 0;
+}
+
+.htmlview .akap, .htmlview .akap_cd, .htmlview .akap_dialog {
+    text-align: justify;
+    margin: 1.5em 0 0;
+}
+
+.htmlview .motto_container {
+       display: inline-block;
+       margin: 1.5em 0 0;
+       clear: right;
+}
+
+.htmlview .motto {
+    text-align: justify;
+    font-style: italic;
+}
+
+.htmlview p.motto_podpis {
+       position: relative;
+       right: -3em;
+       text-align: right;
+}
+
+.htmlview div.fragment {
+    border-bottom: 0.1em solid #999;
+    padding-bottom: 1.5em;
+}
+
+.htmlview div.nota p, .htmlview div.dedykacja p {
+    text-align: right;
+    font-style: italic;
+}
+
+.htmlview div.ramka {
+    border: 1px darkgray solid;
+}
+
+.htmlview .nota_red {
+    background-color: #eee;
+    border: 1px solid #888;
+    -moz-border-radius: 10px;
+    -webkit-border-radius: 10px;
+    display: block;
+    padding: 0 1em 1em 1em;
+}
+
+.htmlview hr.sekcja_swiatlo {
+    margin: 2em 0;
+    visibility: hidden;
+}
+
+.htmlview hr.separator_linia {
+    margin: 1.5em 0;
+    border: none;
+    border-bottom: 0.1em solid #000;
+}
+
+.htmlview p.sekcja_asterysk {
+    padding: 0;
+    margin: 1.5em 0;
+    text-align: center;
+}
+
+.htmlview p.sekcja_asterysk:after {
+    content: "*";
+}
+
+.htmlview div.lista_osob ol {
+    list-style: none;
+    padding: 0 0 0 1.5em;
+}
+
+.htmlview p.miejsce_czas {
+    font-style: italic;
+}
+
+.htmlview .mat, .htmlview .slowo_obce, .htmlview .tytul_dziela, .htmlview .didaskalia {
+    font-style: italic;
+}
+
+.htmlview .wyroznienie {
+    letter-spacing: 0.1em;
+}
+
+.htmlview .osoba {
+    font-style: normal;
+    font-variant: small-caps;
+}
+
+.htmlview em.wieksze_odstepy {
+    font-style: normal;
+    word-spacing: 1em;
+}
+
+.htmlview .parse-warning {
+    display: block;
+    font-size: 10pt;
+    background: #C0C0C0;
+    margin: 1em;
+}
+
+.parse-warning .message {
+    color: purple;
+    font-weight: bold;
+}
+
+/* Uwaga/Extra */
+.htmlview .uwaga {
+    background-color: #96e0e4;
+    border: 1px solid black;
+    -moz-border-radius: 10px;
+    -webkit-border-radius: 10px;
+    display: block;
+    font-size: 10pt;
+    line-height: 12pt;
+    padding: 2px 1em;
+    float: right;
+    max-width: 20%;
+    max-height: 24pt;
+    margin-left: 0.5em;
+    overflow: hidden;
+       z-index: 500;
+}
+
+div[x-node] > .uwaga {
+    float: none;
+    padding: 0.5em 1em;
+    margin: 1em;
+    max-width: 100%;
+    max-height: 100%;
+}
+
+.htmlview .uwaga:hover {
+    max-height: 100%;
+}
+
+.htmlview .annotation-inline-box .uwaga {
+    max-height: 100%;
+}
+
+/* Motywy */
+/* ======================== */
+/* = Footnotes and themes = */
+/* ======================== */
+.htmlview .begin, .htmlview .end {
+    background: green;
+}
+
+.htmlview .motyw {
+    /* position: absolute; */
+    float: right;
+    left: auto;
+    clear: right;
+    width: 10em;
+
+    font-style: normal;
+    font-weight: normal;
+    font-variant: normal;
+    letter-spacing: 0;
+    text-transform: none;
+    text-decoration: none;
+
+    font-size: 13px;
+    line-height: 18px;
+    background-color: #fff;
+    /*    border: 1px solid gray;
+
+     border-right: none;
+     */
+    z-index: 1;
+    -moz-user-select: -moz-none;
+    -webkit-user-select: none;
+    user-select: none;
+    margin-right: -12em;
+    text-align: left;
+    color: #999;
+}
+
+.htmlview .canon {
+    color: black;
+}
+
+.htmlview .noncanon {
+    color: #d00;
+}
+
+.htmlview .motyw[x-editable] {
+    border-left: 4px solid #DDD;
+    padding: 0.2em 0.2em 0.2em 0.5em;
+    margin-top: 0.2em;
+}
+
+/*
+ * Przypisy
+ */
+/* Znaczniki w tekście */
+.annotation {
+    font-style: normal;
+}
+
+.htmlview .annotation:before {
+    content: "[" counter(main) "]";
+    counter-increment: main;
+       vertical-align: super;
+    text-decoration: none;
+       font-size: 66%;
+}
+
+.htmlview .annotation:hover {
+    background-color: #ffcccc;
+}
+.htmlview .pe .annotation:hover {
+    background-color: #96e0e4;
+}
+*.htmlview *.annotation-inline-box {
+    position: static;
+}
+
+/*
+ * Przypisy w tekście
+ */
+.htmlview .annotation-inline-box:hover > span[x-annotation-box]{
+    display: block;
+}
+
+.htmlview .annotation-inline-box > span[x-annotation-box] {
+    display: none;
+    width: 300px;
+    font-size: 10pt;
+    line-height: 12pt;
+    font-weight: normal;
+    font-style: normal;
+    background: #fffe93;
+    border: 1px solid black;
+    border-radius: 10px;
+    -moz-border-radius: 10px;
+    -webkit-border-radius: 10px;
+    padding: 3px 5px;
+    text-decoration: none;
+    z-index: 600;
+}
+
+/*
+ * Przypisy na końcu utworu (aktualnie nieuzywane)
+ */
+.htmlview .annotations-block {
+    counter-reset: secondary;
+}
+
+.htmlview .annotations-block .annotation-body {
+    position: relative;
+    padding-left: 2.5em;
+    padding-top: 0.2em;
+}
+
+.htmlview .annotations-block .annotation-backref {
+    position: absolute;
+    top: 0.4em;
+    left: -0.4em;
+    width: 2.5em;
+    text-align: right;
+}
+
+.htmlview .annotations-block .annotation-backref:before {
+    content: "[" counter(secondary) "]";
+    counter-increment: secondary;
+}
+
+/*
+ * EDITABLE ELEMENTS
+ */
+.htmlview *[x-editable] {
+    position: relative;
+    padding: 2px;
+    margin-left: 0;
+    border: 1px solid transparent;
+}
+
+/* focused editable element */
+.htmlview *[x-editable]:hover {
+    z-index: 900;
+}
+
+.htmlview *[x-editable][x-open] {
+    visibility: hidden;
+}
+
+.edit-button, .delete-button, .accept-button, .tytul-button, .wyroznienie-button, .slowo-button, .znak-button {
+    position: absolute;
+    top: -21px;
+    left: -1px;
+    width: 72px;
+    height: 28px;
+    display: block;
+    /*    margin: 4px 0 2px 0;*/
+    padding: 5px 5px 2px 5px;
+    background-color: #FAFAFA;
+    /*    z-index: 3000;*/
+    /*    color: #FFF;
+     z-index: 1500;
+     */
+    border: 1px solid #DDD;
+    border-bottom: none;
+}
+
+.delete-button {
+    left: 70px;
+}
+
+.tytul-button {
+    left:150px;
+    width:100px;
+}
+
+.wyroznienie-button {
+    left:250px;
+    width:100px;
+}
+.slowo-button {
+    left:350px;
+    width:100px;
+}
+.znak-button {
+    left:450px;
+    width:100px;
+}
+
+.edit-button:hover, .edit-button:active,
+.delete-button:hover, .delete-button:active,
+.accept-button:hover, .accept-button:active,
+.tytul-button:hover, .tytul-button:active,
+.wyroznienie-button:hover, .wyroznienie-button:active,
+.slowo-button:hover, .slowo-button:active,
+.znak-button:hover, .znak-button:active {
+    /*    color: #FFF;*/
+    background-color: #999;
+    color: #FFF;
+}
+
+/*
+ * VISIBILITY RULES
+ */
+.default-menu {
+    visibility: inherit;
+    opacity: 0.2;
+}
+
+.default-menu:hover {
+    opacity: 1;
+    z-index: 5000;
+}
+
+.htmlview *[x-annotation-box] > .default-menu {
+    opacity: 1;
+}
+
+.htmlview *[x-editable][x-open] > .default-menu {
+    visibility: hidden;
+}
+
+.htmlview *[x-editable][x-open] *[x-annotation-box] > .default-menu {
+    visibility: hidden;
+}
+
+.htmlview *[x-editable] > .edit-menu {
+    visibility: hidden;
+}
+
+.htmlview *[x-editable] *[x-annotation-box] > .edit-menu {
+    visibility: hidden;
+}
+
+.htmlview *[x-editable][x-open] > .edit-menu {
+    visibility: visible;
+}
+
+.htmlview *[x-editable][x-open] *[x-annotation-box] > .edit-menu {
+    visibility: visible;
+}
+
+.html-editarea {
+    border: 0;
+    background-color: gray;
+    padding: 1px;
+    z-index: 2000;
+    position: absolute;
+}
+
+.html-editarea textarea {
+    position: absolute;
+    top: 0;
+    margin: 0;
+    padding: 0;
+    width: 100%;
+    height: 100%;
+    z-index: 0;
+    font-size: 10pt;
+    /*    background-color: ivory;*/
+}
+
+.htmlview .out-of-flow-text {
+    display: block;
+    font-family: monospace;
+    border: 2px solid red !important;
+    white-space: pre-line;
+}
+
+.htmlview .out-of-flow-text::before {
+    content: "Tekst w tej ramce nie jest otagowany!";
+    background-color: #ff6c6c;
+    color: black;
+    font-size: 10pt;
+    line-height: 12pt;
+    border: 1px solid black;
+    -moz-border-radius: 10px;
+    -webkit-border-radius: 10px;
+    padding: 5px 1em;
+    margin: 0 0 0 1em;
+    text-align: justify;
+    display: inline;
+    float: right;
+    max-width: 25%;
+}
+
+.unknown-tag {
+    background-color: yellow;
+    margin: -0.25em;
+    padding: 0.25em;
+    border: 1px solid orange;
+}
+
+.alien {
+    color: red;
+}
+
+/* specialChars */
+#specialCharsContainer {
+    text-align: center; 
+    width: 600px; 
+    height: 400px;
+    padding:20px; 
+    background-color: gray; 
+    position: absolute; 
+    top: 20px; 
+    left: 20px; 
+    z-index:1000;
+    overflow:auto;
+}
+#specialCharsContainer a {
+    color: white;
+    font-weight: bold;
+} 
+
+#tableSpecialChars td input {
+    background-color: transparent;
+    border:0;
+    color: white;
+} 
+
+#tableSpecialChars td input.recentSymbol {
+    background-color: white;
+    border:0;
+    color: black;
+} 
diff --git a/src/redakcja/static/css/html_print.css b/src/redakcja/static/css/html_print.css
new file mode 100644 (file)
index 0000000..808f095
--- /dev/null
@@ -0,0 +1,246 @@
+/* Style widoku HTML. Nie należy tu ustawiać position ani marginesów */
+.htmlview {
+    font-size: 16px;
+    font-family: Georgia, "Times New Roman", serif;
+    line-height: 1.5em;
+    padding: 3em;
+}
+
+.htmlview div {
+    max-width: 36em;
+}
+
+.htmlview #toc {
+    display: none;
+}
+
+.htmlview a {
+    color: blue;
+    text-decoration: none;
+}
+
+.htmlview h1 {
+    font-size: 3em;
+    margin: 1.5em 0;
+    text-align: center;
+    line-height: 1.5em;
+    font-weight: bold;
+}
+
+.htmlview h2 {
+    font-size: 2em;
+    margin: 1.5em 0 0;
+    font-weight: bold;
+    line-height: 1.5em;
+}
+
+.htmlview h3 {
+    font-size: 1.5em;
+    margin: 1.5em 0 0;
+    font-weight: normal;
+    line-height: 1.5em;
+}
+
+.htmlview h4 {
+    font-size: 1em;
+    margin: 1.5em 0 0;
+    line-height: 1.5em;
+}
+
+.htmlview p {
+    margin: 0;
+}
+
+/* ======================== */
+/* = Footnotes and themes = */
+/* ======================== */
+.htmlview .theme-begin {
+    border-left: 0.1em solid #DDDDDD;
+    color: #777;
+    padding: 0 0.5em;
+    width: 7.5em;
+
+
+    font-style: normal;
+    font-weight: normal;
+    font-variant: normal;
+    letter-spacing: 0;
+    text-transform: none;
+    text-decoration: none;
+
+    font-size: 16px;
+    float: right;
+    margin-right: -9.5em;
+    clear: both;
+    left: 40em;
+    line-height: 1.5em;
+    text-align: left;
+}
+
+.htmlview .annotation {
+    font-style: normal;
+    font-weight: normal;
+    font-size: 12px;
+}
+
+.htmlview #footnotes .annotation {
+    display: block;
+    float: left;
+    width: 2.5em;
+    clear: both;
+}
+
+.htmlview #footnotes div {
+    margin: 1.5em 0 0 0;
+}
+
+.htmlview #footnotes p {
+    margin-left: 2.5em;
+    font-size: 0.875em;
+}
+
+.htmlview blockquote {
+    font-size: 0.875em;
+}
+
+/* ============= */
+/* = Numbering = */
+/* ============= */
+.htmlview p {
+    position: relative;
+}
+
+.htmlview .anchor {
+    position: absolute;
+    margin: 0em;
+    left: -3em;
+    color: #777;
+    font-size: 12px;
+    width: 2em;
+    text-align: center;
+    padding: 0.25em 0.5em;
+    line-height: 1.5em;
+}
+
+.htmlview .anchor:hover, .htmlview .anchor:active {
+    color: #FFF;
+    background-color: #CCC;
+}
+
+/* =================== */
+/* = Custom elements = */
+/* =================== */
+.htmlview span.author {
+    font-size: 0.5em;
+    display: block;
+    line-height: 1.5em;
+    margin-bottom: 0.25em;
+}
+
+.htmlview span.collection {
+    font-size: 0.375em;
+    display: block;
+    line-height: 1.5em;
+    margin-bottom: -0.25em;
+}
+
+.htmlview span.subtitle {
+    font-size: 0.5em;
+    display: block;
+    line-height: 1.5em;
+    margin-top: -0.25em;
+}
+
+.htmlview div.didaskalia {
+    font-style: italic;
+    margin: 0.5em 0 0 1.5em;
+}
+
+.htmlview div.kwestia {
+    margin: 0.5em 0 0;
+}
+
+.htmlview div.stanza {
+    margin: 1.5em 0 0;
+}
+
+.htmlview div.kwestia div.stanza {
+    margin: 0;
+}
+
+.htmlview p.paragraph {
+    text-align: justify;
+    margin: 1.5em 0 0;
+}
+
+.htmlview p.motto {
+    text-align: justify;
+    font-style: italic;
+    margin: 1.5em 0 0;
+}
+
+.htmlview p.motto_podpis {
+    font-size: 0.875em;
+    text-align: right;
+}
+
+.htmlview div.fragment {
+    border-bottom: 0.1em solid #999;
+    padding-bottom: 1.5em;
+}
+
+.htmlview div.note p, .htmlview div.dedication p,
+.htmlview div.note p.paragraph, .htmlview div.dedication p.paragraph {
+    text-align: right;
+    font-style: italic;
+}
+
+.htmlview div.frame {
+    border: 1px darkgray solid;
+}
+
+.htmlview hr.spacer {
+    height: 3em;
+    visibility: hidden;
+}
+
+.htmlview hr.spacer-line {
+    margin: 1.5em 0;
+    border: none;
+    border-bottom: 0.1em solid #000;
+}
+
+.htmlview p.spacer-asterisk {
+    padding: 0;
+    margin: 1.5em 0;
+    text-align: center;
+}
+
+.htmlview div.person-list ol {
+    list-style: none;
+    padding: 0 0 0 1.5em;
+}
+
+.htmlview p.place-and-time {
+    font-style: italic;
+}
+
+.htmlview em.math, .htmlview em.foreign-word,
+.htmlview em.book-title, .htmlview em.didaskalia {
+    font-style: italic;
+}
+
+.htmlview em.author-emphasis {
+    letter-spacing: 0.1em;
+}
+
+.htmlview em.person {
+    font-style: normal;
+    font-variant: small-caps;
+}
+
+.htmlview em.wieksze-odstepy
+{
+    font-style: normal;
+    word-spacing: 1em;
+}
diff --git a/src/redakcja/static/css/imgareaselect-default.css b/src/redakcja/static/css/imgareaselect-default.css
new file mode 100644 (file)
index 0000000..78c13dc
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * imgAreaSelect default style
+ */
+
+.imgareaselect-border1 {
+       background: url(../img/jquery.imgareaselect/border-v.gif) repeat-y left top;
+}
+
+.imgareaselect-border2 {
+    background: url(../img/jquery.imgareaselect/border-h.gif) repeat-x left top;
+}
+
+.imgareaselect-border3 {
+    background: url(../img/jquery.imgareaselect/border-v.gif) repeat-y right top;
+}
+
+.imgareaselect-border4 {
+    background: url(../img/jquery.imgareaselect/border-h.gif) repeat-x left bottom;
+}
+
+.imgareaselect-border1, .imgareaselect-border2,
+.imgareaselect-border3, .imgareaselect-border4 {
+    opacity: 0.5;
+    filter: alpha(opacity=50);
+}
+
+.imgareaselect-handle {
+    background-color: #fff;
+    border: solid 1px #000;
+    opacity: 0.5;
+    filter: alpha(opacity=50);
+}
+
+.imgareaselect-outer {
+    background-color: #000;
+    opacity: 0.5;
+    filter: alpha(opacity=50);
+}
+
+.imgareaselect-selection {  
+}
diff --git a/src/redakcja/static/css/jquery.autocomplete.css b/src/redakcja/static/css/jquery.autocomplete.css
new file mode 100644 (file)
index 0000000..f349b9e
--- /dev/null
@@ -0,0 +1,44 @@
+.ac_results {
+       padding: 0px;
+       border: 1px solid black;
+       background-color: white;
+       overflow: hidden;
+       z-index: 99999;
+}
+
+.ac_results ul {
+       width: 100%;
+       list-style-position: outside;
+       list-style: none;
+       padding: 0;
+       margin: 0;
+}
+
+.ac_results li {
+       margin: 0px;
+       padding: 2px 5px;
+       cursor: default;
+       display: block;
+       /*
+       if width will be 100% horizontal scrollbar will apear
+       when scroll mode will be used
+       */
+       /*width: 100%;*/
+       font: menu;
+       font-size: 12px;
+       /*
+       it is very important, if line-height not setted or setted
+       in relative units scroll will be broken in firefox
+       */
+       line-height: 16px;
+       overflow: hidden;
+}
+
+.ac_odd {
+       background-color: #eee;
+}
+
+.ac_over {
+       background-color: #0A246A;
+       color: white;
+}
diff --git a/src/redakcja/static/css/master.css b/src/redakcja/static/css/master.css
new file mode 100644 (file)
index 0000000..0394598
--- /dev/null
@@ -0,0 +1,407 @@
+a, a:visited, a:active {
+       color: blue;
+       text-decoration: none;
+}
+
+a:hover {
+       text-decoration: underline;
+}
+
+
+body {
+    margin: 0;
+    overflow: hidden;
+    padding: 0;
+    min-width: 960px;
+    width: 100%;
+    height: 100%;
+}
+
+.vsplitbar {
+    position: absolute;
+    top: 0px;
+    bottom: 0;
+    right: 0;
+    width: 26px;
+    /*background: #C1C1C1 url(../img/gallery.png) no-repeat scroll center center;*/
+    border-left: 2px solid #999;
+    border-right: 2px solid #999;
+    cursor: pointer;  
+    background: #C1C1C1; 
+    z-index:100;
+    cursor: col-resize;
+}
+
+.vsplitbar:hover {
+       background-color: #E6E6E6;
+}
+
+.vsplitbar p {
+    font: 12px Helvetica,Verdana,sans-serif;
+
+  -moz-transform: rotate(270deg);
+  -moz-transform-origin: 50% 50%;
+  -webkit-transform: rotate(270deg);
+  -webkit-transform-origin: 50% 50%; 
+  -o-transform: rotate(-270deg);
+  -o-transform-origin:  bottom left;
+  filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1);
+  margin: 250px auto;
+}
+
+#drag-layer {
+    position:absolute;
+    top:0;
+    bottom:0;
+    left:0;
+    right:0;
+    z-index:1000;
+    display: none;
+    cursor: col-resize;
+}
+
+.editor {
+    position: absolute;
+    top: 0px;
+    bottom: 0;
+    left: 0;
+    right: 30px;
+    overflow: hidden;
+}
+
+.sideless .editor {
+    right: 0;
+}
+.image-object {
+    padding-left: 1em;
+    font: 12px Sans, Helvetica, Verdana, sans-serif;
+}
+.image-object:hover {
+    cursor: pointer;
+}
+#objects-list .delete {
+    padding-left: 3px;
+    font: 10px Sans, Helvetica, Verdana, sans-serif;
+}
+#objects-list .delete:hover {
+    cursor: pointer;
+}
+
+#objects-list .active {
+    color: #800;
+}
+
+
+#editor.readonly .editor {
+       right: 0px;
+}
+
+#html-view {
+    overflow: auto;
+    position: absolute;
+    top: 33px;
+    bottom: 0;
+    left: 0;
+    right: 0;
+       z-index: 1;
+}
+
+#splitter {
+       position: fixed;
+       top: 31px;
+       left: 0px;
+       right: 0px;
+       bottom: 0px;
+       overflow: hidden;
+}
+
+
+#header {
+       position: fixed;
+       top: 0px;
+       right: 0px;
+       left: 0px;
+       height: 30px;
+       border-bottom: 1px solid #999;
+
+    margin: 0;
+    padding: 0;
+    background-color: #C1C1C1;
+    background-image: -webkit-gradient(linear, left top, left bottom, from(#C1C1C1), color-stop(0.9, #A2A2A2));
+
+       /* Firefox 3.6 */
+       background-image: -moz-linear-gradient(top left, #C1C1C1, #A2A2A2, 90%);
+
+    font: 11px Helvetica, Verdana, sans-serif;
+    font-weight: bold;
+
+    z-index: 100;
+}
+
+
+#header.saving {
+       background-color: #E1C1C1;
+}
+#header.saving #save-button {
+    display: none;
+}
+#save-attempt-info {
+    color: #801000;
+    display: none;
+}
+.saving #save-attempt-info {
+    display: inline;
+    font-weight: normal;
+}
+
+
+#header.out-of-date {
+    background-color: #E1C1C1;
+}
+#header.out-of-date #save-button {
+    display: none;
+}
+#out-of-date-info {
+    color: #801000;
+    display: none;
+}
+.out-of-date #out-of-date-info {
+    display: inline;
+}
+
+
+
+#header h1, #header h1 a {
+    margin: 0;
+    padding: 0;
+    font: 9px Helvetica, Verdana, sans-serif;
+    font-weight: bold;
+    float: left;
+    padding: 3px 3px 2px 3px;
+    color: #222;
+    #line-height: 20px;
+}
+
+#header h1 a {
+    text-decoration: none;
+    color: #222;
+}
+
+#header img {
+    border: 0;
+}
+
+.tabs {
+       overflow: hidden;
+    margin: 0;
+       padding: 0;
+       height: 31px;
+       border: 0px;
+       padding-left: 1em;
+    float: left;
+}
+
+.tabs a {
+    color: black;
+}
+
+#tabs-right {
+    float: right;
+    padding-right: 1em;
+}
+
+.tabs li {
+       margin-top: 6px;
+       margin-bottom: 0px;
+
+    -webkit-user-select: none;
+    cursor: pointer;
+    display: block;
+    float: left;
+
+    font-weight: bold;
+    color: #222;
+    margin-left: 4px;
+
+    background-color: #A2A2A2;
+
+       -moz-box-shadow: 1px -1px 2px rgba(127, 127, 127, 0.25);
+    -webkit-box-shadow: 1px -1px 2px rgba(127, 127, 127, 0.25);
+
+    border: 1px solid #999;
+       border-bottom-width: 0px;
+       -moz-border-radius: 4px 4px 0px 0px;
+       -webkit-border-radius: 4px;
+    -webkit-border-bottom-left-radius: 0px;
+    -webkit-border-bottom-right-radius: 0px;
+}
+
+#tabs li {
+    height: 18px;
+    padding-left: 12px;
+    padding-right: 12px;
+    padding-top: 5px;
+}
+
+.tabs li.active {
+    background-color: #C1C1C1;
+}
+
+
+#tabs-right li {
+    height: 20px;
+    padding-left: 12px;
+    padding-right: 12px;
+    padding-top: 3px;
+}
+
+
+
+#tools {
+       float: right;
+       clear: right;
+       overflow: hidden;
+    margin: 0;
+       padding: 0;
+       height: 30px;
+       margin-right: 5px;
+       line-height: 30px;
+       font-size: 10px;
+       vertical-align: middle;
+}
+
+/* Remove extra padding in Firefox */
+button::-moz-focus-inner {
+    border: 0;
+    padding: 0;
+}
+
+p { margin: 0;}
+
+#body-wrap {
+    height: 100%;
+    width: 100%;
+}
+
+#content {
+    height: 100%;
+    width: 100%;
+}
+
+#loading-overlay {
+    background-color: #FFF;
+    position: absolute;
+    z-index: 1000;
+    padding: 0;
+    top: 0;
+    left: 0;
+    width: 100%;
+    height: 100%;
+}
+
+#loading-message {
+    position: absolute;
+    height: 20px;
+    width: 120px;
+    left: 50%;
+    margin-left: -60px;
+    top: 50%;
+    margin-top: -10px;
+/*    text-align: center;*/
+}
+
+#loading-message img {
+    float: left;
+    margin-right: 10px;
+    margin-top: -6px;
+}
+
+/*
+ * CodeMirror
+ */
+
+.CodeMirror-line-numbers {
+       padding: 0px;
+       padding-top: 5px;
+       text-align: right;
+       overflow: hidden;
+       width: 40px;
+       border-right: 1px solid black;
+       background-color: #e6e6fa;
+}
+
+.CodeMirror-line-numbers div {
+       display: block;
+       font-family:"Lucida Console", monospace;
+    font-size: 13px;
+    line-height: 18px;
+       padding-right: 5px;
+}
+
+img.tabclose {
+       padding-left: 8px;
+       width: 16px;
+       height: 16px;
+       vertical-align: middle;
+       vertical-align: text-bottom;
+}
+
+/*
+ * HTML Editor view
+ */
+
+.htmlview {
+       z-index: 1;
+       overflow: hidden;
+}
+
+.htmlview .active[x-editable] {
+    background-color: #FAFAFA;
+    border: 1px solid #DDD;
+/*    -webkit-transition: all 1s linear;*/
+}
+
+.blockOverlay {
+       background-color: black;
+       opacity: 0.4;
+}
+
+.poezja_cyt {
+    margin:1.5em 2em 0;
+    font-size:0.875em
+}
+
+.wers_akap {
+       padding-left: 1em;
+}
+
+.saveNotify {
+    position:absolute; 
+    bottom:22px; 
+    right:7px; 
+    z-index:800;
+    background-color: #FFFF69; 
+    padding:10px; 
+    border: 1px solid black;
+    border-radius: 5px;
+    -moz-border-radius: 15px;
+}
+
+.notifyTip {
+    font-size:12px; float:right;
+}
+
+.saveNotify span {
+    font-weight: bold;
+}
+
+
+
+.scrolled {
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    overflow: auto;
+}
diff --git a/src/redakcja/static/css/summary.css b/src/redakcja/static/css/summary.css
new file mode 100644 (file)
index 0000000..5111ad5
--- /dev/null
@@ -0,0 +1,30 @@
+#summary-view-editor {
+       background: white;
+}
+
+#summary-view {
+       padding: 1em;
+}
+
+#summary-view .summary-cover-area {
+       float: left;
+       margin: 1em;
+       margin-right: 2em;
+}
+
+#summary-view .book-cover {
+       height: 300px;
+       width: 216px;
+}
+
+#summary-view p {
+       margin: 0.5em;
+}
+
+#summary-view label {
+       font-weight: bold;
+}
+
+#summary-view .book-cover {
+
+}
diff --git a/src/redakcja/static/css/toolbar.css b/src/redakcja/static/css/toolbar.css
new file mode 100644 (file)
index 0000000..7b96820
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * Toolbars
+ */
+
+.toolbar {
+    border-bottom: 1px solid #777;
+    background-color: #C1C1C1;
+    margin: 0px;
+    padding: 0px;
+    z-index: 100;
+    height: 28px;
+    position: relative;
+}
+
+.toolbar > .group_selector {
+       position: absolute;
+
+    margin: 1px 5px 1px 0;
+    border: 1px solid #999;
+    padding: 1px;
+
+    top: 0px;
+    left: 0px;
+    bottom: 0px;
+    width: 180px;
+}
+
+.toolbar > .group_selector + button {
+       position: absolute;
+       width: 20px;
+       top: 0px;
+    left: 180px;
+    bottom: 0px;
+}
+
+.toolbar > .button_group_container {
+       position: absolute;
+       top: 0px;
+       left: 200px;
+       right: 20px;
+       bottom: 0px;
+
+       overflow: hidden;
+}
+
+.toolbar > .button_group_container + button {
+       position: absolute;
+       width: 20px;
+       top: 0px;
+    right: 0px;
+    bottom: 0px;
+}
+
+.toolbar ul.button_group {
+       margin: 0;
+       padding: 0;
+       width: 10000%;
+}
+
+.toolbar ul.button_group li {
+       display: inline-block;
+       margin: 0;
+       padding: 0;
+}
+
+
+.toolbar .button_group button {
+    display: block;
+    margin: 4px 0 2px 0;
+    padding: 2px 5px;
+    border: none;
+    background: none;
+    color: #424242;
+}
+
+.toolbar .button_group button img {
+       margin: 0;
+       padding: 0;
+       margin-bottom: -3px;
+}
+
+.toolbar .button_group button:hover,
+.toolbar .button_group button:active {
+    background: #777;
+    color: #FFF;
+    -webkit-border-radius: 10px;
+    -moz-border-radius: 10px;
+}
\ No newline at end of file
diff --git a/src/redakcja/static/css/xmlcolors_20100906.css b/src/redakcja/static/css/xmlcolors_20100906.css
new file mode 100644 (file)
index 0000000..7446450
--- /dev/null
@@ -0,0 +1,63 @@
+html {
+  cursor: text;
+}
+
+.editbox {
+  margin: .4em;
+  padding: 0;
+  /* fnp changes */
+  margin-top: 5px;
+  margin-left: 5px;
+  font-family: "Lucida Console", monospace;
+  font-size: 13px;
+  line-height: 18px;
+  /* end fnp changes */
+  color: black;
+}
+
+.editbox p {
+  margin: 0;
+}
+
+span.xml-tagname {
+  color: #A0B;
+}
+
+span.xml-attribute {
+  color: #281;
+}
+
+span.xml-punctuation {
+  color: black;
+}
+
+span.xml-attname {
+  color: #00F;
+}
+
+span.xml-comment {
+  color: #A70;
+}
+
+span.xml-cdata {
+  color: #48A;
+}
+
+span.xml-processing {
+  color: #999;
+}
+
+span.xml-entity {
+  color: #A22;
+}
+
+span.xml-error {
+ color: #F00 !important;
+  /* fnp changes */
+  font-weight: bolder;
+  /* end fnp changes */
+}
+
+span.xml-text {
+  color: black;
+}
diff --git a/src/redakcja/static/email_mangler/email_mangler.js b/src/redakcja/static/email_mangler/email_mangler.js
new file mode 100755 (executable)
index 0000000..03c1a91
--- /dev/null
@@ -0,0 +1,21 @@
+var rot13 = function(s){
+    return s.replace(/[a-zA-Z]/g, function(c){
+        return String.fromCharCode((c <= "Z" ? 90 : 122) >= (c = c.charCodeAt(0) + 13) ? c : c - 26);
+    });
+};
+
+(function($) {
+    $(function() {
+
+        $(".mangled").each(function() {
+            $this = $(this);
+            var email = rot13($this.attr('data-addr1')) + '@' +
+                rot13($this.attr('data-addr2'));
+            $this.attr('href', "mailto:" + email);
+            $this.html(email);
+        });
+
+
+    });
+})(jQuery);
+
diff --git a/src/redakcja/static/icons/clock.png b/src/redakcja/static/icons/clock.png
new file mode 100644 (file)
index 0000000..57e496f
Binary files /dev/null and b/src/redakcja/static/icons/clock.png differ
diff --git a/src/redakcja/static/icons/close.png b/src/redakcja/static/icons/close.png
new file mode 100644 (file)
index 0000000..5c08b05
Binary files /dev/null and b/src/redakcja/static/icons/close.png differ
diff --git a/src/redakcja/static/icons/go-home.png b/src/redakcja/static/icons/go-home.png
new file mode 100644 (file)
index 0000000..9d62109
Binary files /dev/null and b/src/redakcja/static/icons/go-home.png differ
diff --git a/src/redakcja/static/icons/go-next.png b/src/redakcja/static/icons/go-next.png
new file mode 100644 (file)
index 0000000..6ef8de7
Binary files /dev/null and b/src/redakcja/static/icons/go-next.png differ
diff --git a/src/redakcja/static/icons/go-previous.png b/src/redakcja/static/icons/go-previous.png
new file mode 100644 (file)
index 0000000..659cd90
Binary files /dev/null and b/src/redakcja/static/icons/go-previous.png differ
diff --git a/src/redakcja/static/icons/image-x-generic.png b/src/redakcja/static/icons/image-x-generic.png
new file mode 100644 (file)
index 0000000..68da502
Binary files /dev/null and b/src/redakcja/static/icons/image-x-generic.png differ
diff --git a/src/redakcja/static/icons/ok.png b/src/redakcja/static/icons/ok.png
new file mode 100644 (file)
index 0000000..ea094f4
Binary files /dev/null and b/src/redakcja/static/icons/ok.png differ
diff --git a/src/redakcja/static/icons/revert.png b/src/redakcja/static/icons/revert.png
new file mode 100644 (file)
index 0000000..de96265
Binary files /dev/null and b/src/redakcja/static/icons/revert.png differ
diff --git a/src/redakcja/static/icons/revert_.png b/src/redakcja/static/icons/revert_.png
new file mode 100755 (executable)
index 0000000..afdf20d
Binary files /dev/null and b/src/redakcja/static/icons/revert_.png differ
diff --git a/src/redakcja/static/icons/stop.png b/src/redakcja/static/icons/stop.png
new file mode 100644 (file)
index 0000000..adf1b73
Binary files /dev/null and b/src/redakcja/static/icons/stop.png differ
diff --git a/src/redakcja/static/icons/system-search.png b/src/redakcja/static/icons/system-search.png
new file mode 100644 (file)
index 0000000..fd7f0b0
Binary files /dev/null and b/src/redakcja/static/icons/system-search.png differ
diff --git a/src/redakcja/static/icons/user.png b/src/redakcja/static/icons/user.png
new file mode 100644 (file)
index 0000000..45390a6
Binary files /dev/null and b/src/redakcja/static/icons/user.png differ
diff --git a/src/redakcja/static/icons/zoom.png b/src/redakcja/static/icons/zoom.png
new file mode 100644 (file)
index 0000000..6033b4d
Binary files /dev/null and b/src/redakcja/static/icons/zoom.png differ
diff --git a/src/redakcja/static/icons/zoom_in.png b/src/redakcja/static/icons/zoom_in.png
new file mode 100644 (file)
index 0000000..c7feedc
Binary files /dev/null and b/src/redakcja/static/icons/zoom_in.png differ
diff --git a/src/redakcja/static/icons/zoom_out.png b/src/redakcja/static/icons/zoom_out.png
new file mode 100644 (file)
index 0000000..fdd7124
Binary files /dev/null and b/src/redakcja/static/icons/zoom_out.png differ
diff --git a/src/redakcja/static/img/angel-left.png b/src/redakcja/static/img/angel-left.png
new file mode 100644 (file)
index 0000000..7744103
Binary files /dev/null and b/src/redakcja/static/img/angel-left.png differ
diff --git a/src/redakcja/static/img/angel-right.png b/src/redakcja/static/img/angel-right.png
new file mode 100644 (file)
index 0000000..df85d33
Binary files /dev/null and b/src/redakcja/static/img/angel-right.png differ
diff --git a/src/redakcja/static/img/arrow-down.png b/src/redakcja/static/img/arrow-down.png
new file mode 100644 (file)
index 0000000..0e32315
Binary files /dev/null and b/src/redakcja/static/img/arrow-down.png differ
diff --git a/src/redakcja/static/img/arrow-up.png b/src/redakcja/static/img/arrow-up.png
new file mode 100644 (file)
index 0000000..cdf9cf6
Binary files /dev/null and b/src/redakcja/static/img/arrow-up.png differ
diff --git a/src/redakcja/static/img/gallery.png b/src/redakcja/static/img/gallery.png
new file mode 100644 (file)
index 0000000..851a678
Binary files /dev/null and b/src/redakcja/static/img/gallery.png differ
diff --git a/src/redakcja/static/img/jquery.imgareaselect/border-anim-h.gif b/src/redakcja/static/img/jquery.imgareaselect/border-anim-h.gif
new file mode 100644 (file)
index 0000000..ec9f5da
Binary files /dev/null and b/src/redakcja/static/img/jquery.imgareaselect/border-anim-h.gif differ
diff --git a/src/redakcja/static/img/jquery.imgareaselect/border-anim-v.gif b/src/redakcja/static/img/jquery.imgareaselect/border-anim-v.gif
new file mode 100644 (file)
index 0000000..331cc90
Binary files /dev/null and b/src/redakcja/static/img/jquery.imgareaselect/border-anim-v.gif differ
diff --git a/src/redakcja/static/img/jquery.imgareaselect/border-h.gif b/src/redakcja/static/img/jquery.imgareaselect/border-h.gif
new file mode 100644 (file)
index 0000000..a2aa5b0
Binary files /dev/null and b/src/redakcja/static/img/jquery.imgareaselect/border-h.gif differ
diff --git a/src/redakcja/static/img/jquery.imgareaselect/border-v.gif b/src/redakcja/static/img/jquery.imgareaselect/border-v.gif
new file mode 100644 (file)
index 0000000..4bfd555
Binary files /dev/null and b/src/redakcja/static/img/jquery.imgareaselect/border-v.gif differ
diff --git a/src/redakcja/static/img/logo-220.png b/src/redakcja/static/img/logo-220.png
new file mode 100644 (file)
index 0000000..9b15e88
Binary files /dev/null and b/src/redakcja/static/img/logo-220.png differ
diff --git a/src/redakcja/static/img/logo.png b/src/redakcja/static/img/logo.png
new file mode 100644 (file)
index 0000000..398f45d
Binary files /dev/null and b/src/redakcja/static/img/logo.png differ
diff --git a/src/redakcja/static/img/pr-icon.png b/src/redakcja/static/img/pr-icon.png
new file mode 100644 (file)
index 0000000..fc0f9fe
Binary files /dev/null and b/src/redakcja/static/img/pr-icon.png differ
diff --git a/src/redakcja/static/img/sample_cover.png b/src/redakcja/static/img/sample_cover.png
new file mode 100644 (file)
index 0000000..f7a678e
Binary files /dev/null and b/src/redakcja/static/img/sample_cover.png differ
diff --git a/src/redakcja/static/img/spinner.gif b/src/redakcja/static/img/spinner.gif
new file mode 100644 (file)
index 0000000..c69e937
Binary files /dev/null and b/src/redakcja/static/img/spinner.gif differ
diff --git a/src/redakcja/static/img/wl-orange.png b/src/redakcja/static/img/wl-orange.png
new file mode 100644 (file)
index 0000000..d5c56d0
Binary files /dev/null and b/src/redakcja/static/img/wl-orange.png differ
diff --git a/src/redakcja/static/img/wl-orange.svg b/src/redakcja/static/img/wl-orange.svg
new file mode 100644 (file)
index 0000000..a7ef60b
--- /dev/null
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.91 r13725"
+   width="171.35579"
+   height="172.75761"
+   viewBox="0 0 171.35578 172.75761"
+   sodipodi:docname="wl-orange.svg"
+   inkscape:export-filename="/home/janek/Desktop/pr-icon.png"
+   inkscape:export-xdpi="8.4035673"
+   inkscape:export-ydpi="8.4035673">
+  <metadata
+     id="metadata8">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <defs
+     id="defs6" />
+  <sodipodi:namedview
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1"
+     objecttolerance="10"
+     gridtolerance="10"
+     guidetolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:window-width="1012"
+     inkscape:window-height="652"
+     id="namedview4"
+     showgrid="false"
+     fit-margin-top="0"
+     fit-margin-left="0"
+     fit-margin-right="0"
+     fit-margin-bottom="0"
+     inkscape:zoom="1.5132586"
+     inkscape:cx="81.786073"
+     inkscape:cy="104.54976"
+     inkscape:window-x="444"
+     inkscape:window-y="29"
+     inkscape:window-maximized="0"
+     inkscape:current-layer="svg2" />
+  <path
+     style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#ff8000;stroke-width:6;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.89411765;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+     d="M 69.761219,87.45616 35.01474,92.310571 2.9997126,63.566463 38.619672,51.969714 73.499413,79.979772 c 2.693747,2.268439 7.087505,-2.82549 5.220188,-5.333568 l -28.116626,-29.712"
+     id="path3338"
+     inkscape:connector-curvature="0"
+     sodipodi:nodetypes="ccccccc" />
+  <path
+     sodipodi:nodetypes="ccccccc"
+     inkscape:connector-curvature="0"
+     id="path4140"
+     d="M 83.899635,69.761218 79.045224,35.014739 107.78933,2.9997126 119.38608,38.619671 91.376023,73.499408 c -2.929265,3.189374 2.329871,9.069983 4.672742,6.54184 L 126.42159,50.602974"
+     style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#ff8000;stroke-width:6;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.89411765;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
+  <path
+     style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#ff8000;stroke-width:6;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.89411765;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+     d="m 101.59457,85.301455 34.74649,-4.854411 32.01502,28.744106 -35.61996,11.59675 -34.879733,-28.010057 c -2.693755,-2.433646 -8.739571,2.164664 -6.54184,4.672742 l 29.438273,30.372825"
+     id="path4142"
+     inkscape:connector-curvature="0"
+     sodipodi:nodetypes="ccccccc" />
+  <path
+     sodipodi:nodetypes="ccccccc"
+     inkscape:connector-curvature="0"
+     id="path4144"
+     d="m 86.054345,102.99639 4.85441,34.74649 -28.74411,32.01502 -11.59675,-35.61996 28.01006,-34.879733 c 3.230746,-3.230746 -2.164666,-8.904777 -4.67274,-6.54184 l -30.37283,29.438273"
+     style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#ff8000;stroke-width:6;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.89411765;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
+</svg>
diff --git a/src/redakcja/static/js/book_text/book.js b/src/redakcja/static/js/book_text/book.js
new file mode 100644 (file)
index 0000000..335fe39
--- /dev/null
@@ -0,0 +1,61 @@
+$(function() {
+    function scrollToAnchor(anchor) {
+        if (anchor) {
+            var anchor_name = anchor.slice(1);
+            var element = $('a[name="' + anchor_name + '"]');
+            if (element.length > 0) {
+                $.scrollTo(element, 500, {offset: {top: -50, left: 0}});
+                foot_elem = $('#footnotes a[name="' + anchor_name + '"]');
+                if (foot_elem.length > 0) {
+                    $(element).parent().highlightFade('yellow');
+                }
+                window.location.hash = anchor;
+            }
+        }
+    }
+
+    $.highlightFade.defaults.speed = 3000;
+    $('#toc').hide();
+    if ($('#toc li').length == 0) {
+        $('#menu li a[href="#toc"]').remove();
+    }
+    if ($('#nota_red').length == 0) {
+        $('#menu li a[href="#nota_red"]').remove();
+    }
+
+    // On page load, scroll to anchor
+    scrollToAnchor(window.location.hash)
+
+    $('#toc, #themes, #book-text').delegate('click', 'a', function(event) {
+        event.preventDefault();
+        $('#menu li a.selected').click();
+        scrollToAnchor($(this).attr('href'));
+    });
+
+    $('#menu li a.menu').toggle(function() {
+        $('#menu li a.selected').click();
+        $(this).addClass('selected');
+        $($(this).attr('href')).slideDown('fast');
+    }, function() {
+        $(this).removeClass('selected');
+        $($(this).attr('href')).slideUp('fast');
+    });
+    
+
+    if (window.getSelection) {
+        $('.theme-begin').click(function() {
+            var selection = window.getSelection();
+            selection.removeAllRanges();
+            var range = document.createRange();
+
+            var e = $(".theme-end[fid='" + $(this).attr('fid') + "']")[0];
+
+            if (e) {
+                range.setStartAfter(this);
+                range.setEndBefore(e);
+                selection.addRange(range);
+            }
+        });
+    }
+
+});
diff --git a/src/redakcja/static/js/book_text/jquery.eventdelegation.js b/src/redakcja/static/js/book_text/jquery.eventdelegation.js
new file mode 100644 (file)
index 0000000..52fce07
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * jQuery Event Delegation Plugin - jquery.eventdelegation.js
+ * Fast flexible event handling
+ *
+ * January 2008 - Randy Morey (http://dev.distilldesign.com/)
+ */
+
+(function ($) {
+       /* setup list of allowed events for event delegation
+        * only events that bubble are appropriate
+        */
+       var allowed = {};
+       $.each([
+               'click',
+               'dblclick',
+               'mousedown',
+               'mouseup',
+               'mousemove',
+               'mouseover',
+               'mouseout',
+               'keydown',
+               'keypress',
+               'keyup'
+               ], function(i, eventName) {
+                       allowed[eventName] = true;
+       });
+
+       $.fn.extend({
+               delegate: function (event, selector, f) {
+                       return $(this).each(function () {
+                               if (allowed[event])
+                                       $(this).bind(event, function (e) {
+                                               var el = $(e.target),
+                                                       result = false;
+
+                                               while (!$(el).is('body')) {
+                                                       if ($(el).is(selector)) {
+                                                               result = f.apply($(el)[0], [e]);
+                                                               if (result === false)
+                                                                       e.preventDefault();
+                                                               return;
+                                                       }
+
+                                                       el = $(el).parent();
+                                               }
+                                       });
+                       });
+               },
+               undelegate: function (event) {
+                       return $(this).each(function () {
+                               $(this).unbind(event);
+                       });
+               }
+       });
+})(jQuery);
\ No newline at end of file
diff --git a/src/redakcja/static/js/book_text/jquery.highlightfade.js b/src/redakcja/static/js/book_text/jquery.highlightfade.js
new file mode 100644 (file)
index 0000000..bbe39f0
--- /dev/null
@@ -0,0 +1,150 @@
+/**
+ *  jQuery Plugin highlightFade (jquery.offput.ca/highlightFade)
+ *  (c) 2006 Blair Mitchelmore (offput.ca) blair@offput.ca
+ */
+/**
+ * This is version 0.7 of my highlightFade plugin. It follows the yellow fade technique of Web 2.0 fame
+ * but expands it to allow any starting colour and allows you to specify the end colour as well.
+ *
+ * For the moment, I'm done with this plug-in. Unless I come upon a really cool feature it should have
+ * this plug-in will only receive updates to ensure future compatibility with jQuery.
+ *
+ * As of now (Aug. 16, 2006) the plugin has been written with the 1.0.1 release of jQuery (rev 249) which
+ * is available from http://jquery.com/src/jquery-1.0.1.js
+ *
+ * A note regarding rgb() syntax: I noticed that most browsers implement rgb syntax as either an integer
+ * (0-255) or percentage (0-100%) value for each field, that is, rgb(i/p,i/p,i/p); however, the W3C
+ * standard clearly defines it as "either three integer values or three percentage values" [http://www.w3.org/TR/CSS21/syndata.html]
+ * which I choose to follow despite the error redundancy of the typical behaviour browsers employ.
+ *
+ * Changelog:
+ *
+ *    0.7:
+ *        - Added the awesome custom attribute support written by George Adamson (slightly modified)
+ *        - Removed bgColor plugin dependency seeing as attr is customizable now...
+ *    0.6:
+ *        - Abstracted getBGColor into its own plugin with optional test and data retrieval functions
+ *        - Converted all $ references to jQuery references as John's code seems to be shifting away
+ *          from that and I don't want to have to update this for a long time.
+ *    0.5:
+ *        - Added simple argument syntax for only specifying start colour of event
+ *        - Removed old style argument syntax
+ *        - Added 'interval', 'final, and 'end' properties
+ *        - Renamed 'color' property to 'start'
+ *        - Added second argument to $.highlightFade.getBGColor to bypass the e.highlighting check
+ *    0.4:
+ *        - Added rgb(%,%,%) color syntax
+ *    0.3:
+ *        - Fixed bug when event was called while parent was also running event corrupting the
+ *          the background colour of the child
+ *    0.2:
+ *        - Fixed bug where an unspecified onComplete function made the page throw continuous errors
+ *        - Fixed bug where multiple events on the same element would speed each subsequent event
+ *    0.1:
+ *        - Initial Release
+ *
+ * @author          Blair Mitchelmore (blair@offput.ca)
+ * @version         0.5
+ */
+jQuery.fn.highlightFade = function(settings) {
+       var o = (settings && settings.constructor == String) ? {start: settings} : settings || {};
+       var d = jQuery.highlightFade.defaults;
+       var i = o['interval'] || d['interval'];
+       var a = o['attr'] || d['attr'];
+       var ts = {
+               'linear': function(s,e,t,c) { return parseInt(s+(c/t)*(e-s)); },
+               'sinusoidal': function(s,e,t,c) { return parseInt(s+Math.sin(((c/t)*90)*(Math.PI/180))*(e-s)); },
+               'exponential': function(s,e,t,c) { return parseInt(s+(Math.pow(c/t,2))*(e-s)); }
+       };
+       var t = (o['iterator'] && o['iterator'].constructor == Function) ? o['iterator'] : ts[o['iterator']] || ts[d['iterator']] || ts['linear'];
+       if (d['iterator'] && d['iterator'].constructor == Function) t = d['iterator'];
+       return this.each(function() {
+               if (!this.highlighting) this.highlighting = {};
+               var e = (this.highlighting[a]) ? this.highlighting[a].end : jQuery.highlightFade.getBaseValue(this,a) || [255,255,255];
+               var c = jQuery.highlightFade.getRGB(o['start'] || o['colour'] || o['color'] || d['start'] || [255,255,128]);
+               var s = jQuery.speed(o['speed'] || d['speed']);
+               var r = o['final'] || (this.highlighting[a] && this.highlighting[a].orig) ? this.highlighting[a].orig : jQuery.curCSS(this,a);
+               if (o['end'] || d['end']) r = jQuery.highlightFade.asRGBString(e = jQuery.highlightFade.getRGB(o['end'] || d['end']));
+               if (typeof o['final'] != 'undefined') r = o['final'];
+               if (this.highlighting[a] && this.highlighting[a].timer) window.clearInterval(this.highlighting[a].timer);
+               this.highlighting[a] = { steps: ((s.duration) / i), interval: i, currentStep: 0, start: c, end: e, orig: r, attr: a };
+               jQuery.highlightFade(this,a,o['complete'],t);
+       });
+};
+
+jQuery.highlightFade = function(e,a,o,t) {
+       e.highlighting[a].timer = window.setInterval(function() {
+               var newR = t(e.highlighting[a].start[0],e.highlighting[a].end[0],e.highlighting[a].steps,e.highlighting[a].currentStep);
+               var newG = t(e.highlighting[a].start[1],e.highlighting[a].end[1],e.highlighting[a].steps,e.highlighting[a].currentStep);
+               var newB = t(e.highlighting[a].start[2],e.highlighting[a].end[2],e.highlighting[a].steps,e.highlighting[a].currentStep);
+               jQuery(e).css(a,jQuery.highlightFade.asRGBString([newR,newG,newB]));
+               if (e.highlighting[a].currentStep++ >= e.highlighting[a].steps) {
+                       jQuery(e).css(a,e.highlighting[a].orig || '');
+                       window.clearInterval(e.highlighting[a].timer);
+                       e.highlighting[a] = null;
+                       if (o && o.constructor == Function) o.call(e);
+               }
+       },e.highlighting[a].interval);
+};
+
+jQuery.highlightFade.defaults = {
+       start: [255,255,128],
+       interval: 50,
+       speed: 400,
+       attr: 'backgroundColor'
+};
+
+jQuery.highlightFade.getRGB = function(c,d) {
+       var result;
+       if (c && c.constructor == Array && c.length == 3) return c;
+       if (result = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(c))
+               return [parseInt(result[1]),parseInt(result[2]),parseInt(result[3])];
+       else if (result = /rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(c))
+               return [parseFloat(result[1])*2.55,parseFloat(result[2])*2.55,parseFloat(result[3])*2.55];
+       else if (result = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(c))
+               return [parseInt("0x" + result[1]),parseInt("0x" + result[2]),parseInt("0x" + result[3])];
+       else if (result = /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(c))
+               return [parseInt("0x"+ result[1] + result[1]),parseInt("0x" + result[2] + result[2]),parseInt("0x" + result[3] + result[3])];
+       else
+               return jQuery.highlightFade.checkColorName(c) || d || null;
+};
+
+jQuery.highlightFade.asRGBString = function(a) {
+       return "rgb(" + a.join(",") + ")";
+};
+
+jQuery.highlightFade.getBaseValue = function(e,a,b) {
+       var s, t;
+       b = b || false;
+       t = a = a || jQuery.highlightFade.defaults['attr'];
+       do {
+               s = jQuery(e).css(t || 'backgroundColor');
+               if ((s  != '' && s != 'transparent') || (e.tagName.toLowerCase() == "body") || (!b && e.highlighting && e.highlighting[a] && e.highlighting[a].end)) break;
+               t = false;
+       } while (e = e.parentNode);
+       if (!b && e.highlighting && e.highlighting[a] && e.highlighting[a].end) s = e.highlighting[a].end;
+       if (s == undefined || s == '' || s == 'transparent') s = [255,255,255];
+       return jQuery.highlightFade.getRGB(s);
+};
+
+jQuery.highlightFade.checkColorName = function(c) {
+       if (!c) return null;
+       switch(c.replace(/^\s*|\s*$/g,'').toLowerCase()) {
+               case 'aqua': return [0,255,255];
+               case 'black': return [0,0,0];
+               case 'blue': return [0,0,255];
+               case 'fuchsia': return [255,0,255];
+               case 'gray': return [128,128,128];
+               case 'green': return [0,128,0];
+               case 'lime': return [0,255,0];
+               case 'maroon': return [128,0,0];
+               case 'navy': return [0,0,128];
+               case 'olive': return [128,128,0];
+               case 'purple': return [128,0,128];
+               case 'red': return [255,0,0];
+               case 'silver': return [192,192,192];
+               case 'teal': return [0,128,128];
+               case 'white': return [255,255,255];
+               case 'yellow': return [255,255,0];
+       }
+};
diff --git a/src/redakcja/static/js/book_text/jquery.scrollto.js b/src/redakcja/static/js/book_text/jquery.scrollto.js
new file mode 100644 (file)
index 0000000..c403ab9
--- /dev/null
@@ -0,0 +1,194 @@
+/**
+ * jQuery.ScrollTo
+ * Copyright (c) 2007-2008 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com
+ * Dual licensed under MIT and GPL.
+ * Date: 9/11/2008
+ *
+ * @projectDescription Easy element scrolling using jQuery.
+ * http://flesler.blogspot.com/2007/10/jqueryscrollto.html
+ * Tested with jQuery 1.2.6. On FF 2/3, IE 6/7, Opera 9.2/5 and Safari 3. on Windows.
+ *
+ * @author Ariel Flesler
+ * @version 1.4
+ *
+ * @id jQuery.scrollTo
+ * @id jQuery.fn.scrollTo
+ * @param {String, Number, DOMElement, jQuery, Object} target Where to scroll the matched elements.
+ *       The different options for target are:
+ *             - A number position (will be applied to all axes).
+ *             - A string position ('44', '100px', '+=90', etc ) will be applied to all axes
+ *             - A jQuery/DOM element ( logically, child of the element to scroll )
+ *             - A string selector, that will be relative to the element to scroll ( 'li:eq(2)', etc )
+ *             - A hash { top:x, left:y }, x and y can be any kind of number/string like above.
+ * @param {Number} duration The OVERALL length of the animation, this argument can be the settings object instead.
+ * @param {Object,Function} settings Optional set of settings or the onAfter callback.
+ *      @option {String} axis Which axis must be scrolled, use 'x', 'y', 'xy' or 'yx'.
+ *      @option {Number} duration The OVERALL length of the animation.
+ *      @option {String} easing The easing method for the animation.
+ *      @option {Boolean} margin If true, the margin of the target element will be deducted from the final position.
+ *      @option {Object, Number} offset Add/deduct from the end position. One number for both axes or { top:x, left:y }.
+ *      @option {Object, Number} over Add/deduct the height/width multiplied by 'over', can be { top:x, left:y } when using both axes.
+ *      @option {Boolean} queue If true, and both axis are given, the 2nd axis will only be animated after the first one ends.
+ *      @option {Function} onAfter Function to be called after the scrolling ends.
+ *      @option {Function} onAfterFirst If queuing is activated, this function will be called after the first scrolling ends.
+ * @return {jQuery} Returns the same jQuery object, for chaining.
+ *
+ * @desc Scroll to a fixed position
+ * @example $('div').scrollTo( 340 );
+ *
+ * @desc Scroll relatively to the actual position
+ * @example $('div').scrollTo( '+=340px', { axis:'y' } );
+ *
+ * @dec Scroll using a selector (relative to the scrolled element)
+ * @example $('div').scrollTo( 'p.paragraph:eq(2)', 500, { easing:'swing', queue:true, axis:'xy' } );
+ *
+ * @ Scroll to a DOM element (same for jQuery object)
+ * @example var second_child = document.getElementById('container').firstChild.nextSibling;
+ *                     $('#container').scrollTo( second_child, { duration:500, axis:'x', onAfter:function(){
+ *                             alert('scrolled!!');
+ *                     }});
+ *
+ * @desc Scroll on both axes, to different values
+ * @example $('div').scrollTo( { top: 300, left:'+=200' }, { axis:'xy', offset:-20 } );
+ */
+;(function( $ ){
+
+       var $scrollTo = $.scrollTo = function( target, duration, settings ){
+               $(window).scrollTo( target, duration, settings );
+       };
+
+       $scrollTo.defaults = {
+               axis:'y',
+               duration:1
+       };
+
+       // Returns the element that needs to be animated to scroll the window.
+       // Kept for backwards compatibility (specially for localScroll & serialScroll)
+       $scrollTo.window = function( scope ){
+               return $(window).scrollable();
+       };
+
+       // Hack, hack, hack... stay away!
+       // Returns the real elements to scroll (supports window/iframes, documents and regular nodes)
+       $.fn.scrollable = function(){
+               return this.map(function(){
+                       // Just store it, we might need it
+                       var win = this.parentWindow || this.defaultView,
+                               // If it's a document, get its iframe or the window if it's THE document
+                               elem = this.nodeName == '#document' ? win.frameElement || win : this,
+                               // Get the corresponding document
+                               doc = elem.contentDocument || (elem.contentWindow || elem).document,
+                               isWin = elem.setInterval;
+
+                       return elem.nodeName == 'IFRAME' || isWin && $.browser.safari ? doc.body
+                               : isWin ? doc.documentElement
+                               : this;
+               });
+       };
+
+       $.fn.scrollTo = function( target, duration, settings ){
+               if( typeof duration == 'object' ){
+                       settings = duration;
+                       duration = 0;
+               }
+               if( typeof settings == 'function' )
+                       settings = { onAfter:settings };
+
+               settings = $.extend( {}, $scrollTo.defaults, settings );
+               // Speed is still recognized for backwards compatibility
+               duration = duration || settings.speed || settings.duration;
+               // Make sure the settings are given right
+               settings.queue = settings.queue && settings.axis.length > 1;
+
+               if( settings.queue )
+                       // Let's keep the overall duration
+                       duration /= 2;
+               settings.offset = both( settings.offset );
+               settings.over = both( settings.over );
+
+               return this.scrollable().each(function(){
+                       var elem = this,
+                               $elem = $(elem),
+                               targ = target, toff, attr = {},
+                               win = $elem.is('html,body');
+
+                       switch( typeof targ ){
+                               // A number will pass the regex
+                               case 'number':
+                               case 'string':
+                                       if( /^([+-]=)?\d+(px)?$/.test(targ) ){
+                                               targ = both( targ );
+                                               // We are done
+                                               break;
+                                       }
+                                       // Relative selector, no break!
+                                       targ = $(targ,this);
+                               case 'object':
+                                       // DOMElement / jQuery
+                                       if( targ.is || targ.style )
+                                               // Get the real position of the target
+                                               toff = (targ = $(targ)).offset();
+                       }
+                       $.each( settings.axis.split(''), function( i, axis ){
+                               var Pos = axis == 'x' ? 'Left' : 'Top',
+                                       pos = Pos.toLowerCase(),
+                                       key = 'scroll' + Pos,
+                                       old = elem[key],
+                                       Dim = axis == 'x' ? 'Width' : 'Height',
+                                       dim = Dim.toLowerCase();
+
+                               if( toff ){// jQuery / DOMElement
+                                       attr[key] = toff[pos] + ( win ? 0 : old - $elem.offset()[pos] );
+
+                                       // If it's a dom element, reduce the margin
+                                       if( settings.margin ){
+                                               attr[key] -= parseInt(targ.css('margin'+Pos)) || 0;
+                                               attr[key] -= parseInt(targ.css('border'+Pos+'Width')) || 0;
+                                       }
+
+                                       attr[key] += settings.offset[pos] || 0;
+
+                                       if( settings.over[pos] )
+                                               // Scroll to a fraction of its width/height
+                                               attr[key] += targ[dim]() * settings.over[pos];
+                               }else
+                                       attr[key] = targ[pos];
+
+                               // Number or 'number'
+                               if( /^\d+$/.test(attr[key]) )
+                                       // Check the limits
+                                       attr[key] = attr[key] <= 0 ? 0 : Math.min( attr[key], max(Dim) );
+
+                               // Queueing axes
+                               if( !i && settings.queue ){
+                                       // Don't waste time animating, if there's no need.
+                                       if( old != attr[key] )
+                                               // Intermediate animation
+                                               animate( settings.onAfterFirst );
+                                       // Don't animate this axis again in the next iteration.
+                                       delete attr[key];
+                               }
+                       });
+                       animate( settings.onAfter );
+
+                       function animate( callback ){
+                               $elem.animate( attr, duration, settings.easing, callback && function(){
+                                       callback.call(this, target, settings);
+                               });
+                       };
+                       function max( Dim ){
+                               var attr ='scroll'+Dim,
+                                       doc = elem.ownerDocument;
+
+                               return win
+                                               ? Math.max( doc.documentElement[attr], doc.body[attr]  )
+                                               : elem[attr];
+                       };
+               }).end();
+       };
+
+       function both( val ){
+               return typeof val == 'object' ? val : { top:val, left:val };
+       };
+
+})( jQuery );
\ No newline at end of file
diff --git a/src/redakcja/static/js/button_scripts.js b/src/redakcja/static/js/button_scripts.js
new file mode 100644 (file)
index 0000000..37f37a7
--- /dev/null
@@ -0,0 +1,367 @@
+(function() {
+  var slice = Array.prototype.slice;
+
+  function update(array, args) {
+    var arrayLength = array.length, length = args.length;
+    while (length--) array[arrayLength + length] = args[length];
+    return array;
+  };
+
+  function merge(array, args) {
+    array = slice.call(array, 0);
+    return update(array, args);
+  };
+
+  Function.prototype.bind = function(context) {
+    if (arguments.length < 2 && typeof arguments[0] === 'undefined') {
+      return this;
+    }
+    var __method = this;
+    var args = slice.call(arguments, 1);
+    return function() {
+      var a = merge(args, arguments);
+      return __method.apply(context, a);
+    }
+  }
+
+})();
+
+function nblck_each(array, body, after) {
+       $.each(array, function(i) {
+               body(this, i);
+       });
+
+       after();
+};
+
+function nblck_map(array, func, after) {
+       var acc = [];
+
+       nblck_each(array, function(elem, index) {
+               acc.push(func(elem, index));
+       }, function(){
+               after(acc);
+       });
+};
+
+function ScriptletCenter()
+{
+    this.scriptlets = {};
+
+    this.scriptlets['insert_text'] = function(context, params, text, move_forward, move_up, done)
+    {
+        done(params.text, move_forward, move_up);
+    }.bind(this);
+
+    this.scriptlets['insert_tag'] = function(context, params, text, move_forward, move_up, done)
+    {
+        var padding_top = '';
+        for (var i=params.padding_top; i; i--)
+            padding_top += '\n';
+
+        var start_tag = '<'+params.tag;
+        var cursor_inside = false;
+
+        for (var attr in params.attrs) {
+            start_tag += ' '+attr+'="' + params.attrs[attr] + '"';
+        }
+
+        start_tag += '>';
+        var end_tag = '</'+params.tag+'>';
+
+        var padding_bottom = '';
+        for (var i=params.padding_bottom; i; i--)
+            padding_bottom += '\n';
+
+        if(text.length > 0) {
+            // tokenize
+            var output = '';
+            var token = '';
+            for(var index=0; index < text.length; index++)
+            {
+                if (text[index].match(/\s/)) { // whitespace
+                    token += text[index];
+                }
+                else { // character
+                    output += token;
+                    if(output == token) output += padding_top + start_tag;
+                    token = '';
+                    output += text[index];
+                }
+            }
+
+            if( output[output.length-1] == '\\' ) {
+                output = output.substr(0, output.length-1) + end_tag + padding_bottom + '\\';
+            } else {
+                output += end_tag + padding_bottom;
+            }
+            output += token;
+
+            // keep cursor inside tag if some previous scriptlet has already moved it
+            cursor_inside = move_forward != 0 || move_up != 0;
+        }
+        else {
+            if(params.nocontent) {
+                output = padding_top + "<"+params.tag +" />" + padding_bottom;
+            }
+            else {
+                output = padding_top + start_tag + end_tag + padding_bottom;
+                cursor_inside = true;
+            }
+        }
+
+        if (cursor_inside) {
+            move_forward -= params.tag.length + 3;
+            move_up += params.padding_bottom || 0;
+        }
+
+        done(output, move_forward, move_up);
+    }.bind(this);
+
+    this.scriptlets['lineregexp'] = function(context, params, text, move_forward, move_up, done) {
+               var self = this;
+
+        var exprs = $.map(params.exprs, function(expr) {
+            var opts = "g";
+            if(expr.length > 2) {
+                opts = expr[2];
+            } return {
+                rx: new RegExp(expr[0], opts),
+                repl: expr[1]
+                };
+        });
+
+        if(!text) done(text, move_forward, move_up);
+
+        var changed = 0;
+        var lines = text.split('\n');
+
+               nblck_map(lines, function(line, index) {
+            var old_line = line;
+            $(exprs).each(function() {
+                var expr = this;
+                line = line.replace(expr.rx, expr.repl);
+            });
+
+                       $progress.html(index);
+
+            if(old_line != line) changed += 1;
+            return line;
+        }, function(newlines) {
+            if(changed > 0) {
+                text = newlines.join('\n');
+            };
+
+            done(text, move_forward, move_up);
+               });
+    }.bind(this);
+
+    this.scriptlets['fulltextregexp'] = function(context, params, text, move_forward, move_up, done) {
+               var self = this;
+
+        var exprs = $.map(params.exprs, function(expr) {
+            var opts = "mg";
+            if(expr.length > 2) {
+                opts = expr[2];
+            }
+            return {
+                rx: new RegExp(expr[0], opts),
+                repl: expr[1]
+                };
+        });
+
+        if(!text) done(text, move_forward, move_up);
+
+               nblck_each(exprs, function(expr, index) {
+                       $progress.html(600 + index);
+            text = text.replace(expr.rx, expr.repl);
+        }, function() {
+                       done(text, move_forward, move_up);
+               });
+    }.bind(this);
+
+    this.scriptlets['macro'] = function(context, params, text, move_forward, move_up, done) {
+        var self = this;
+               var i = 0;
+
+               function next(text, move_forward, move_up) {
+               if (i < params.length) {
+                               var e = params[i];
+                               i = i + 1;
+                               self.scriptlets[e[0]](context, e[1], text, move_forward, move_up, next);
+                       }
+                       else {
+                               done(text, move_forward, move_up);
+                       }
+        };
+
+               next(text, move_forward, move_up);
+    }.bind(this);
+
+    this.scriptlets['lowercase'] = function(context, params, text, move_forward, move_up, done)
+    {
+        if(!text) done(text, move_forward, move_up);
+        done(text.toLowerCase(), move_forward, move_up);
+    }.bind(this);
+
+
+    this.scriptlets["insert_stanza"] = function(context, params, text, move_forward, move_up, done) {
+        var padding_top = '';
+        for (var i=params.padding_top; i; i--)
+            padding_top += '\n';
+
+        var padding_bottom = '';
+        for (var i=params.padding_bottom; i; i--)
+            padding_bottom += '\n';
+
+        if(text) {
+            var verses = text.split('\n');
+            text = ''; var buf = ''; var ebuf = '';
+            var first = true;
+
+            for(var i=0;  i < verses.length; i++) {
+                var verse = verses[i].replace(/^\s+/, "").replace(/\s+$/, "");
+                if(verse) {
+                    text += (buf ? buf + '/\n' : '') + ebuf;
+                    buf = (first ? '<strofa>\n' : '') + verses[i];
+                    ebuf = '';
+                    first = false;
+                } else {
+                    ebuf += '\n' + verses[i];
+                }
+            }
+            text = padding_top + text + buf + '\n</strofa>' + padding_bottom + ebuf;
+        }
+        else {
+            text = padding_top + "<strofa></strofa>" + padding_bottom;
+            move_forward -= "</strofa>".length;
+            move_up += params.padding_bottom || 0;
+        }
+
+        done(text, move_forward, move_up);
+    }.bind(this);
+
+
+    this.scriptlets['autotag'] = function(context, params, text, move_forward, move_up, done)
+    {
+        if(!text.match(/^\n+$/)) done(text, move_forward, move_up);
+
+        var output = '';
+
+        function insert_done(text, mf, mu) {
+            output += text;
+        }
+
+        if (!params.split) params.split = 2;
+        if (!params.padding) params.padding = 3;
+
+        if (params.tag == 'strofa')
+            tagger = this.scriptlets['insert_stanza'];
+        else
+            tagger = this.scriptlets['insert_tag'];
+
+        var padding_top = text.match(/^\n+/)
+        output = padding_top ? padding_top[0] : '';
+
+        padding = '';
+        for(var i=params.padding; i; --i) {
+            padding += "\n";
+        }
+
+        text = text.substr(output.length);
+        var chunk_reg = new RegExp("^([\\s\\S]+?)(\\n{"+params.split+",}|$)");
+        while (match = text.match(chunk_reg)) {
+            if (params.tag == 'akap' && match[1].match(/^---/))
+                tag = 'akap_dialog';
+            else tag = params.tag;
+            tagger(context, {tag: tag}, match[1], 0, 0, insert_done);
+            if (match[2].length > params.padding)
+                output += match[2];
+            else
+                output += padding;
+            text = text.substr(match[0].length)
+        }
+
+        output += text;
+
+        done(output, move_forward, move_up);
+    }.bind(this);
+
+
+    this.scriptlets['slugify'] = function(context, params, text, move_forward, move_up, done)
+    {
+        done(slugify(text.replace(/_/g, '-')), move_forward, move_up);
+    }.bind(this);
+
+}
+
+ScriptletCenter.prototype.callInteractive = function(opts) {
+       $progress = $('<span>Executing script</span>');
+       var self = this;
+
+       /* This won't work, 'cause the JS below might be synchronous :( */
+       /* var timer = setTimeout(function() {
+           $.blockUI({message: $progress});
+           timer = null;
+       }, 1000); */
+
+       $.blockUI({message: $progress, showOverlay: false});
+
+    $('#save-button').attr('disabled', true);
+    var input = self.XMLEditorSelectedText(opts.context);
+    window.setTimeout(function() {
+        self.scriptlets[opts.action](opts.context, opts.extra, input, 0, 0, function(output, move_forward, move_up){
+            /*if(timer)
+                clearTimeout(timer);
+            else */
+            if (input != output) {
+                self.XMLEditorReplaceSelectedText(opts.context, output)
+            }
+            if (move_forward || move_up) {
+                try {
+                    self.XMLEditorMoveCursorForward(opts.context, move_forward, move_up)
+                }
+                catch(e) {}
+            }
+            $.unblockUI({onUnblock: function() { $('#save-button').attr('disabled', null)}}); // done
+        });
+    }, 0);
+
+}
+
+ScriptletCenter.prototype.XMLEditorSelectedText = function(editor) {
+
+    return editor.selection();
+};
+
+ScriptletCenter.prototype.XMLEditorReplaceSelectedText = function(editor, replacement)
+{
+       $progress.html("Replacing text");
+       editor.replaceSelection(replacement);
+};
+
+ScriptletCenter.prototype.XMLEditorMoveCursorForward = function(panel, right, up) {
+    var pos = panel.cursorPosition();
+    if (up) {
+        line = pos.line;
+        while (up < 0) {
+            line = panel.nextLine(line);
+            ++up;
+        }
+        while (up > 0) {
+            line = panel.prevLine(line);
+            --up;
+        }
+        len = panel.lineContent(line).length;
+        panel.selectLines(line, len + right);
+    }
+    else {
+        panel.selectLines(pos.line, pos.character + right);
+    }
+};
+
+var scriptletCenter;
+
+$(function() {
+    scriptletCenter = new ScriptletCenter();
+});
diff --git a/src/redakcja/static/js/catalogue/book_list.js b/src/redakcja/static/js/catalogue/book_list.js
new file mode 100644 (file)
index 0000000..9d2511d
--- /dev/null
@@ -0,0 +1,96 @@
+(function($) {
+    $(function() {
+
+    // clicking on book checks chunks, too
+    $("input[name=select_book]").change(function(ev) {
+        $book = $(this);
+        $book.closest("table").find("input[name=select_chunk][data-book-id=" + $book.val() + "]").attr("checked", $book.is(':checked'));
+    });
+
+    // initialize context menu
+
+   var get_ids = function() {
+       return $.map($("input[name=select_chunk]:checked"), function(ele, idx) {
+           return ele.value;
+           }).concat(
+               $.map($("input[name=select_book][data-chunk-id!=]:checked"), function(ele, idx) {
+                   return $(ele).attr("data-chunk-id");
+                   })).join();
+   };
+
+    var get_callback = function(form_field_name) {
+        var $form = $("#chunk_mass_edit");
+        var $field = $("[name=" + form_field_name + "]", $form);
+        var $ids_field = $("[name=ids]").val(get_ids());
+        var usable_callback = function(value) {
+            $field.val(value);
+            $ids_field.val(get_ids());
+            $.post($form.attr("action"),
+               $form.serialize(),
+               function(data, status) {
+                   location.reload(true);
+               }
+            );
+            return true;
+        };
+        return usable_callback;
+    };
+
+    var get_items = function(field, callback) {
+        var d = {};
+        $.each($("select[name="+field+"] option[value!=]"),
+            function(idx, ele) {
+                d[field + "_" + idx] = {
+                    name: $(ele).text(), 
+                    callback: function() {callback($(ele).attr('value'));}
+                };
+            });
+        return d;
+    };
+
+    var user_callback = get_callback('user');
+    var users = [
+        get_items("user", user_callback),
+        {sep: '----'},
+        get_items("other-user", user_callback)
+    ];
+    var current_user_items = user_items = {};
+    var i = 0;
+    var more_label = $("label[for=mass_edit_more_users]").text();
+    for (user_table in users) {
+        for (user in users[user_table]) {
+            if (i && i % 20 == 0) {
+                var more_items = {};
+                current_user_items['more'] = {
+                    name: more_label,
+                    items: more_items
+                };
+                current_user_items = more_items;
+            }
+            current_user_items[user] = users[user_table][user];
+            i += 1;
+        }
+    }
+    $.contextMenu({
+        selector: '#file-list',
+        items: {
+            stage: { 
+                name: $("label[for=mass_edit_stage]").text(),
+                items: get_items("stage", get_callback('stage')),
+                icon: "clock",
+            },
+            user: { 
+                name: $("label[for=mass_edit_user]").text(),
+                items: user_items,
+                icon: "user",
+            },
+            project: {
+                name: $("label[for=mass_edit_project]").text(),
+                items: get_items("project", get_callback('project')),
+            },
+        },
+    });
+
+
+    });
+})(jQuery);
diff --git a/src/redakcja/static/js/catalogue/catalogue.js b/src/redakcja/static/js/catalogue/catalogue.js
new file mode 100755 (executable)
index 0000000..9d2bd95
--- /dev/null
@@ -0,0 +1,32 @@
+(function($) {
+    $(function() {
+
+
+        $('.filter').change(function() {
+            document.filter[this.name].value = this.value;
+            document.filter.submit();
+        });
+
+        $('.check-filter').change(function() {
+            document.filter[this.name].value = this.checked ? '1' : '';
+            document.filter.submit();
+        });
+
+        $('.text-filter').each(function() {
+            var inp = this;
+            $(inp).parent().submit(function() {
+                document.filter[inp.name].value = inp.value;
+                document.filter.submit();
+                return false;
+            });
+        });
+
+
+        $('.autoslug-source').change(function() {
+            $('.autoslug').attr('value', slugify(this.value));
+        });
+
+
+    });
+})(jQuery);
+
diff --git a/src/redakcja/static/js/lib/codemirror-0.8/codemirror.js b/src/redakcja/static/js/lib/codemirror-0.8/codemirror.js
new file mode 100644 (file)
index 0000000..57e44be
--- /dev/null
@@ -0,0 +1,538 @@
+/* CodeMirror main module
+ *
+ * Implements the CodeMirror constructor and prototype, which take care
+ * of initializing the editor frame, and providing the outside interface.
+ */
+
+// The CodeMirrorConfig object is used to specify a default
+// configuration. If you specify such an object before loading this
+// file, the values you put into it will override the defaults given
+// below. You can also assign to it after loading.
+var CodeMirrorConfig = window.CodeMirrorConfig || {};
+
+var CodeMirror = (function(){
+  function setDefaults(object, defaults) {
+    for (var option in defaults) {
+      if (!object.hasOwnProperty(option))
+        object[option] = defaults[option];
+    }
+  }
+  function forEach(array, action) {
+    for (var i = 0; i < array.length; i++)
+      action(array[i]);
+  }
+
+  // These default options can be overridden by passing a set of
+  // options to a specific CodeMirror constructor. See manual.html for
+  // their meaning.
+  setDefaults(CodeMirrorConfig, {
+    stylesheet: [],
+    path: "",
+    parserfile: [],
+    basefiles: ["util.js", "stringstream.js", "select.js", "undo.js", "editor.js", "tokenize.js"],
+    iframeClass: null,
+    passDelay: 200,
+    passTime: 50,
+    lineNumberDelay: 200,
+    lineNumberTime: 50,
+    continuousScanning: false,
+    saveFunction: null,
+    onChange: null,
+    undoDepth: 50,
+    undoDelay: 800,
+    disableSpellcheck: true,
+    textWrapping: true,
+    readOnly: false,
+    width: "",
+    height: "300px",
+    minHeight: 100,
+    autoMatchParens: false,
+    parserConfig: null,
+    tabMode: "indent", // or "spaces", "default", "shift"
+    reindentOnLoad: false,
+    activeTokens: null,
+    cursorActivity: null,
+    lineNumbers: false,
+    indentUnit: 2,
+    domain: null
+  });
+
+  function addLineNumberDiv(container) {
+    var nums = document.createElement("DIV"),
+        scroller = document.createElement("DIV");
+    nums.style.position = "absolute";
+    nums.style.height = "100%";
+    if (nums.style.setExpression) {
+      try {nums.style.setExpression("height", "this.previousSibling.offsetHeight + 'px'");}
+      catch(e) {} // Seems to throw 'Not Implemented' on some IE8 versions
+    }
+    nums.style.top = "0px";
+    nums.style.left = "0px";
+    nums.style.overflow = "hidden";
+    container.appendChild(nums);
+    scroller.className = "CodeMirror-line-numbers";
+    nums.appendChild(scroller);
+    scroller.innerHTML = "<div>1</div>";
+    return nums;
+  }
+
+  function frameHTML(options) {
+    if (typeof options.parserfile == "string")
+      options.parserfile = [options.parserfile];
+    if (typeof options.stylesheet == "string")
+      options.stylesheet = [options.stylesheet];
+
+    var html = ["<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\"><html><head>"];
+    // Hack to work around a bunch of IE8-specific problems.
+    html.push("<meta http-equiv=\"X-UA-Compatible\" content=\"IE=EmulateIE7\"/>");
+    forEach(options.stylesheet, function(file) {
+      html.push("<link rel=\"stylesheet\" type=\"text/css\" href=\"" + file + "\"/>");
+    });
+    forEach(options.basefiles.concat(options.parserfile), function(file) {
+      if (!/^https?:/.test(file)) file = options.path + file;
+      html.push("<script type=\"text/javascript\" src=\"" + file + "\"><" + "/script>");
+    });
+    html.push("</head><body style=\"border-width: 0;\" class=\"editbox\" spellcheck=\"" +
+              (options.disableSpellcheck ? "false" : "true") + "\"></body></html>");
+    return html.join("");
+  }
+
+  var internetExplorer = document.selection && window.ActiveXObject && /MSIE/.test(navigator.userAgent);
+
+  function CodeMirror(place, options) {
+    // Use passed options, if any, to override defaults.
+    this.options = options = options || {};
+    setDefaults(options, CodeMirrorConfig);
+
+    // Backward compatibility for deprecated options.
+    if (options.dumbTabs) options.tabMode = "spaces";
+    else if (options.normalTab) options.tabMode = "default";
+
+    var frame = this.frame = document.createElement("IFRAME");
+    if (options.iframeClass) frame.className = options.iframeClass;
+    frame.frameBorder = 0;
+    frame.style.border = "0";
+    frame.style.width = '100%';
+    frame.style.height = '100%';
+    // display: block occasionally suppresses some Firefox bugs, so we
+    // always add it, redundant as it sounds.
+    frame.style.display = "block";
+
+    var div = this.wrapping = document.createElement("DIV");
+    div.style.position = "relative";
+    div.className = "CodeMirror-wrapping";
+    div.style.width = options.width;
+    div.style.height = (options.height == "dynamic") ? options.minHeight + "px" : options.height;
+    // This is used by Editor.reroutePasteEvent
+    var teHack = this.textareaHack = document.createElement("TEXTAREA");
+    div.appendChild(teHack);
+    teHack.style.position = "absolute";
+    teHack.style.left = "-10000px";
+    teHack.style.width = "10px";
+
+    // Link back to this object, so that the editor can fetch options
+    // and add a reference to itself.
+    frame.CodeMirror = this;
+    if (options.domain && internetExplorer) {
+      this.html = frameHTML(options);
+      frame.src = "javascript:(function(){document.open();" +
+        (options.domain ? "document.domain=\"" + options.domain + "\";" : "") +
+        "document.write(window.frameElement.CodeMirror.html);document.close();})()";
+    }
+    else {
+      frame.src = "javascript:false";
+    }
+
+    if (place.appendChild) place.appendChild(div);
+    else place(div);
+    div.appendChild(frame);
+    if (options.lineNumbers) this.lineNumbers = addLineNumberDiv(div);
+
+    this.win = frame.contentWindow;
+    if (!options.domain || !internetExplorer) {
+      this.win.document.open();
+      this.win.document.write(frameHTML(options));
+      this.win.document.close();
+    }
+  }
+
+  CodeMirror.prototype = {
+    init: function() {
+      if (this.options.initCallback) this.options.initCallback(this);
+      if (this.options.lineNumbers) this.activateLineNumbers();
+      if (this.options.reindentOnLoad) this.reindent();
+      if (this.options.height == "dynamic") this.setDynamicHeight();
+    },
+
+    getCode: function() {return this.editor.getCode();},
+    setCode: function(code) {this.editor.importCode(code);},
+    selection: function() {this.focusIfIE(); return this.editor.selectedText();},
+    reindent: function() {this.editor.reindent();},
+    reindentSelection: function() {this.focusIfIE(); this.editor.reindentSelection(null);},
+
+    focusIfIE: function() {
+      // in IE, a lot of selection-related functionality only works when the frame is focused
+      if (this.win.select.ie_selection) this.focus();
+    },
+    focus: function() {
+      this.win.focus();
+      if (this.editor.selectionSnapshot) // IE hack
+        this.win.select.setBookmark(this.win.document.body, this.editor.selectionSnapshot);
+    },
+    replaceSelection: function(text) {
+      this.focus();
+      this.editor.replaceSelection(text);
+      return true;
+    },
+    replaceChars: function(text, start, end) {
+      this.editor.replaceChars(text, start, end);
+    },
+    getSearchCursor: function(string, fromCursor, caseFold) {
+      return this.editor.getSearchCursor(string, fromCursor, caseFold);
+    },
+
+    undo: function() {this.editor.history.undo();},
+    redo: function() {this.editor.history.redo();},
+    historySize: function() {return this.editor.history.historySize();},
+    clearHistory: function() {this.editor.history.clear();},
+
+    grabKeys: function(callback, filter) {this.editor.grabKeys(callback, filter);},
+    ungrabKeys: function() {this.editor.ungrabKeys();},
+
+    setParser: function(name, parserConfig) {this.editor.setParser(name, parserConfig);},
+    setSpellcheck: function(on) {this.win.document.body.spellcheck = on;},
+    setStylesheet: function(names) {
+      if (typeof names === "string") names = [names];
+      var activeStylesheets = {};
+      var matchedNames = {};
+      var links = this.win.document.getElementsByTagName("link");
+      // Create hashes of active stylesheets and matched names.
+      // This is O(n^2) but n is expected to be very small.
+      for (var x = 0, link; link = links[x]; x++) {
+        if (link.rel.indexOf("stylesheet") !== -1) {
+          for (var y = 0; y < names.length; y++) {
+            var name = names[y];
+            if (link.href.substring(link.href.length - name.length) === name) {
+              activeStylesheets[link.href] = true;
+              matchedNames[name] = true;
+            }
+          }
+        }
+      }
+      // Activate the selected stylesheets and disable the rest.
+      for (var x = 0, link; link = links[x]; x++) {
+        if (link.rel.indexOf("stylesheet") !== -1) {
+          link.disabled = !(link.href in activeStylesheets);
+        }
+      }
+      // Create any new stylesheets.
+      for (var y = 0; y < names.length; y++) {
+        var name = names[y];
+        if (!(name in matchedNames)) {
+          var link = this.win.document.createElement("link");
+          link.rel = "stylesheet";
+          link.type = "text/css";
+          link.href = name;
+          this.win.document.getElementsByTagName('head')[0].appendChild(link);
+        }
+      }
+    },
+    setTextWrapping: function(on) {
+      if (on == this.options.textWrapping) return;
+      this.win.document.body.style.whiteSpace = on ? "" : "nowrap";
+      this.options.textWrapping = on;
+      if (this.lineNumbers) {
+        this.setLineNumbers(false);
+        this.setLineNumbers(true);
+      }
+    },
+    setIndentUnit: function(unit) {this.win.indentUnit = unit;},
+    setUndoDepth: function(depth) {this.editor.history.maxDepth = depth;},
+    setTabMode: function(mode) {this.options.tabMode = mode;},
+    setLineNumbers: function(on) {
+      if (on && !this.lineNumbers) {
+        this.lineNumbers = addLineNumberDiv(this.wrapping);
+        this.activateLineNumbers();
+      }
+      else if (!on && this.lineNumbers) {
+        this.wrapping.removeChild(this.lineNumbers);
+        this.wrapping.style.marginLeft = "";
+        this.lineNumbers = null;
+      }
+    },
+
+    cursorPosition: function(start) {this.focusIfIE(); return this.editor.cursorPosition(start);},
+    firstLine: function() {return this.editor.firstLine();},
+    lastLine: function() {return this.editor.lastLine();},
+    nextLine: function(line) {return this.editor.nextLine(line);},
+    prevLine: function(line) {return this.editor.prevLine(line);},
+    lineContent: function(line) {return this.editor.lineContent(line);},
+    setLineContent: function(line, content) {this.editor.setLineContent(line, content);},
+    removeLine: function(line){this.editor.removeLine(line);},
+    insertIntoLine: function(line, position, content) {this.editor.insertIntoLine(line, position, content);},
+    selectLines: function(startLine, startOffset, endLine, endOffset) {
+      this.win.focus();
+      this.editor.selectLines(startLine, startOffset, endLine, endOffset);
+    },
+    nthLine: function(n) {
+      var line = this.firstLine();
+      for (; n > 1 && line !== false; n--)
+        line = this.nextLine(line);
+      return line;
+    },
+    lineNumber: function(line) {
+      var num = 0;
+      while (line !== false) {
+        num++;
+        line = this.prevLine(line);
+      }
+      return num;
+    },
+    jumpToLine: function(line) {
+      if (typeof line == "number") line = this.nthLine(line);
+      this.selectLines(line, 0);
+      this.win.focus();
+    },
+    currentLine: function() { // Deprecated, but still there for backward compatibility
+      return this.lineNumber(this.cursorLine());
+    },
+    cursorLine: function() {
+      return this.cursorPosition().line;
+    },
+    cursorCoords: function(start) {return this.editor.cursorCoords(start);},
+
+    activateLineNumbers: function() {
+      var frame = this.frame, win = frame.contentWindow, doc = win.document, body = doc.body,
+          nums = this.lineNumbers, scroller = nums.firstChild, self = this;
+      var barWidth = null;
+
+      function sizeBar() {
+        if (frame.offsetWidth == 0) return;
+        for (var root = frame; root.parentNode; root = root.parentNode);
+        if (!nums.parentNode || root != document || !win.Editor) {
+          // Clear event handlers (their nodes might already be collected, so try/catch)
+          try{clear();}catch(e){}
+          clearInterval(sizeInterval);
+          return;
+        }
+
+        /*if (nums.offsetWidth != barWidth) {
+          barWidth = nums.offsetWidth;
+          frame.parentNode.style.paddingLeft = barWidth + "px";
+        }*/
+      }
+      function doScroll() {
+        nums.scrollTop = body.scrollTop || doc.documentElement.scrollTop || 0;
+      }
+      // Cleanup function, registered by nonWrapping and wrapping.
+      var clear = function(){};
+      sizeBar();
+      var sizeInterval = setInterval(sizeBar, 500);
+
+      function ensureEnoughLineNumbers(fill) {
+        var lineHeight = scroller.firstChild.offsetHeight;
+        if (lineHeight == 0) return;
+        var targetHeight = 50 + Math.max(body.offsetHeight, Math.max(frame.offsetHeight, body.scrollHeight || 0)),
+            lastNumber = Math.ceil(targetHeight / lineHeight);
+        for (var i = scroller.childNodes.length; i <= lastNumber; i++) {
+          var div = document.createElement("DIV");
+          div.appendChild(document.createTextNode(fill ? String(i + 1) : "\u00a0"));
+          scroller.appendChild(div);
+        }
+      }
+
+      function nonWrapping() {
+        function update() {
+          ensureEnoughLineNumbers(true);
+          doScroll();
+        }
+        self.updateNumbers = update;
+        var onScroll = win.addEventHandler(win, "scroll", doScroll, true),
+            onResize = win.addEventHandler(win, "resize", update, true);
+        clear = function(){
+          onScroll(); onResize();
+          if (self.updateNumbers == update) self.updateNumbers = null;
+        };
+        update();
+      }
+
+      function wrapping() {
+        var node, lineNum, next, pos, changes = [], styleNums = self.options.styleNumbers;
+
+        function setNum(n, node) {
+          // Does not typically happen (but can, if you mess with the
+          // document during the numbering)
+          if (!lineNum) lineNum = scroller.appendChild(document.createElement("DIV"));
+          if (styleNums) styleNums(lineNum, node, n);
+          // Changes are accumulated, so that the document layout
+          // doesn't have to be recomputed during the pass
+          changes.push(lineNum); changes.push(n);
+          pos = lineNum.offsetHeight + lineNum.offsetTop;
+          lineNum = lineNum.nextSibling;
+        }
+        function commitChanges() {
+          for (var i = 0; i < changes.length; i += 2)
+            changes[i].innerHTML = changes[i + 1];
+          changes = [];
+        }
+        function work() {
+          if (!scroller.parentNode || scroller.parentNode != self.lineNumbers) return;
+
+          var endTime = new Date().getTime() + self.options.lineNumberTime;
+          while (node) {
+            setNum(next++, node.previousSibling);
+            for (; node && !win.isBR(node); node = node.nextSibling) {
+              var bott = node.offsetTop + node.offsetHeight;
+              while (scroller.offsetHeight && bott - 3 > pos) setNum("&nbsp;");
+            }
+            if (node) node = node.nextSibling;
+            if (new Date().getTime() > endTime) {
+              commitChanges();
+              pending = setTimeout(work, self.options.lineNumberDelay);
+              return;
+            }
+          }
+          while (lineNum) setNum(next++);
+          commitChanges();
+          doScroll();
+        }
+        function start(firstTime) {
+          doScroll();
+          ensureEnoughLineNumbers(firstTime);
+          node = body.firstChild;
+          lineNum = scroller.firstChild;
+          pos = 0;
+          next = 1;
+          work();
+        }
+
+        start(true);
+        var pending = null;
+        function update() {
+          if (pending) clearTimeout(pending);
+          if (self.editor.allClean()) start();
+          else pending = setTimeout(update, 200);
+        }
+        self.updateNumbers = update;
+        var onScroll = win.addEventHandler(win, "scroll", doScroll, true),
+            onResize = win.addEventHandler(win, "resize", update, true);
+        clear = function(){
+          if (pending) clearTimeout(pending);
+          if (self.updateNumbers == update) self.updateNumbers = null;
+          onScroll();
+          onResize();
+        };
+      }
+      (this.options.textWrapping || this.options.styleNumbers ? wrapping : nonWrapping)();
+    },
+
+    setDynamicHeight: function() {
+      var self = this, activity = self.options.cursorActivity, win = self.win, body = win.document.body,
+          lineHeight = null, timeout = null, vmargin = 2 * self.frame.offsetTop;
+      body.style.overflowY = "hidden";
+      win.document.documentElement.style.overflowY = "hidden";
+      this.frame.scrolling = "no";
+
+      function updateHeight() {
+        for (var span = body.firstChild, sawBR = false; span; span = span.nextSibling)
+          if (win.isSpan(span) && span.offsetHeight) {
+            lineHeight = span.offsetHeight;
+            if (!sawBR) vmargin = 2 * (self.frame.offsetTop + span.offsetTop + body.offsetTop + (internetExplorer ? 10 : 0));
+            break;
+          }
+        if (lineHeight)
+          self.wrapping.style.height = Math.max(vmargin + lineHeight * (body.getElementsByTagName("BR").length + 1),
+                                                self.options.minHeight) + "px";
+      }
+      setTimeout(updateHeight, 100);
+      self.options.cursorActivity = function(x) {
+        if (activity) activity(x);
+        clearTimeout(timeout);
+        timeout = setTimeout(updateHeight, 200);
+      };
+    }
+  };
+
+  CodeMirror.InvalidLineHandle = {toString: function(){return "CodeMirror.InvalidLineHandle";}};
+
+  CodeMirror.replace = function(element) {
+    if (typeof element == "string")
+      element = document.getElementById(element);
+    return function(newElement) {
+      element.parentNode.replaceChild(newElement, element);
+    };
+  };
+
+  CodeMirror.fromTextArea = function(area, options) {
+    if (typeof area == "string")
+      area = document.getElementById(area);
+
+    options = options || {};
+    if (area.style.width && options.width == null)
+      options.width = area.style.width;
+    if (area.style.height && options.height == null)
+      options.height = area.style.height;
+    if (options.content == null) options.content = area.value;
+
+    if (area.form) {
+      function updateField() {
+        area.value = mirror.getCode();
+      }
+      if (typeof area.form.addEventListener == "function")
+        area.form.addEventListener("submit", updateField, false);
+      else
+        area.form.attachEvent("onsubmit", updateField);
+      var realSubmit = area.form.submit;
+      function wrapSubmit() {
+        updateField();
+        // Can't use realSubmit.apply because IE6 is too stupid
+        area.form.submit = realSubmit;
+        area.form.submit();
+        area.form.submit = wrapSubmit;
+      }
+      area.form.submit = wrapSubmit;
+    }
+
+    function insert(frame) {
+      if (area.nextSibling)
+        area.parentNode.insertBefore(frame, area.nextSibling);
+      else
+        area.parentNode.appendChild(frame);
+    }
+
+    area.style.display = "none";
+    var mirror = new CodeMirror(insert, options);
+    mirror.toTextArea = function() {
+      area.parentNode.removeChild(mirror.wrapping);
+      area.style.display = "";
+      if (area.form) {
+        area.form.submit = realSubmit;
+        if (typeof area.form.removeEventListener == "function")
+          area.form.removeEventListener("submit", updateField, false);
+        else
+          area.form.detachEvent("onsubmit", updateField);
+      }
+    };
+
+    return mirror;
+  };
+
+  CodeMirror.isProbablySupported = function() {
+    // This is rather awful, but can be useful.
+    var match;
+    if (window.opera)
+      return Number(window.opera.version()) >= 9.52;
+    else if (/Apple Computers, Inc/.test(navigator.vendor) && (match = navigator.userAgent.match(/Version\/(\d+(?:\.\d+)?)\./)))
+      return Number(match[1]) >= 3;
+    else if (document.selection && window.ActiveXObject && (match = navigator.userAgent.match(/MSIE (\d+(?:\.\d*)?)\b/)))
+      return Number(match[1]) >= 6;
+    else if (match = navigator.userAgent.match(/gecko\/(\d{8})/i))
+      return Number(match[1]) >= 20050901;
+    else if (match = navigator.userAgent.match(/AppleWebKit\/(\d+)/))
+      return Number(match[1]) >= 525;
+    else
+      return null;
+  };
+
+  return CodeMirror;
+})();
diff --git a/src/redakcja/static/js/lib/codemirror-0.8/editor.js b/src/redakcja/static/js/lib/codemirror-0.8/editor.js
new file mode 100644 (file)
index 0000000..07410d2
--- /dev/null
@@ -0,0 +1,1514 @@
+/* The Editor object manages the content of the editable frame. It
+ * catches events, colours nodes, and indents lines. This file also
+ * holds some functions for transforming arbitrary DOM structures into
+ * plain sequences of <span> and <br> elements
+ */
+
+var internetExplorer = document.selection && window.ActiveXObject && /MSIE/.test(navigator.userAgent);
+var webkit = /AppleWebKit/.test(navigator.userAgent);
+var safari = /Apple Computers, Inc/.test(navigator.vendor);
+var gecko = /gecko\/(\d{8})/i.test(navigator.userAgent);
+// TODO this is related to the backspace-at-end-of-line bug. Remove
+// this if Opera gets their act together, make the version check more
+// broad if they don't.
+var brokenOpera = window.opera && /Version\/10.[56]/.test(navigator.userAgent);
+
+// Make sure a string does not contain two consecutive 'collapseable'
+// whitespace characters.
+function makeWhiteSpace(n) {
+  var buffer = [], nb = true;
+  for (; n > 0; n--) {
+    buffer.push((nb || n == 1) ? nbsp : " ");
+    nb ^= true;
+  }
+  return buffer.join("");
+}
+
+// Create a set of white-space characters that will not be collapsed
+// by the browser, but will not break text-wrapping either.
+function fixSpaces(string) {
+  if (string.charAt(0) == " ") string = nbsp + string.slice(1);
+  return string.replace(/\t/g, function() {return makeWhiteSpace(indentUnit);})
+    .replace(/[ \u00a0]{2,}/g, function(s) {return makeWhiteSpace(s.length);});
+}
+
+function cleanText(text) {
+  return text.replace(/\u00a0/g, " ");
+}
+
+// Create a SPAN node with the expected properties for document part
+// spans.
+function makePartSpan(value, doc) {
+  var text = value;
+  if (value.nodeType == 3) text = value.nodeValue;
+  else value = doc.createTextNode(text);
+
+  var span = doc.createElement("SPAN");
+  span.isPart = true;
+  span.appendChild(value);
+  span.currentText = text;
+  return span;
+}
+
+var Editor = (function(){
+  // The HTML elements whose content should be suffixed by a newline
+  // when converting them to flat text.
+  var newlineElements = {"P": true, "DIV": true, "LI": true};
+
+  function asEditorLines(string) {
+    var tab = makeWhiteSpace(indentUnit);
+    return map(string.replace(/\t/g, tab).replace(/\u00a0/g, " ").replace(/\r\n?/g, "\n").split("\n"), fixSpaces);
+  }
+
+  // Helper function for traverseDOM. Flattens an arbitrary DOM node
+  // into an array of textnodes and <br> tags.
+  function simplifyDOM(root, atEnd) {
+    var doc = root.ownerDocument;
+    var result = [];
+    var leaving = true;
+
+    function simplifyNode(node, top) {
+      if (node.nodeType == 3) {
+        var text = node.nodeValue = fixSpaces(node.nodeValue.replace(/\r/g, "").replace(/\n/g, " "));
+        if (text.length) leaving = false;
+        result.push(node);
+      }
+      else if (isBR(node) && node.childNodes.length == 0) {
+        leaving = true;
+        result.push(node);
+      }
+      else {
+        for (var n = node.firstChild; n; n = n.nextSibling) simplifyNode(n);
+        if (!leaving && newlineElements.hasOwnProperty(node.nodeName.toUpperCase())) {
+          leaving = true;
+          if (!atEnd || !top)
+            result.push(doc.createElement("BR"));
+        }
+      }
+    }
+
+    simplifyNode(root, true);
+    return result;
+  }
+
+  // Creates a MochiKit-style iterator that goes over a series of DOM
+  // nodes. The values it yields are strings, the textual content of
+  // the nodes. It makes sure that all nodes up to and including the
+  // one whose text is being yielded have been 'normalized' to be just
+  // <span> and <br> elements.
+  function traverseDOM(start){
+    var owner = start.ownerDocument;
+    var nodeQueue = [];
+
+    // Create a function that can be used to insert nodes after the
+    // one given as argument.
+    function pointAt(node){
+      var parent = node.parentNode;
+      var next = node.nextSibling;
+      return function(newnode) {
+        parent.insertBefore(newnode, next);
+      };
+    }
+    var point = null;
+
+    // This an Opera-specific hack -- always insert an empty span
+    // between two BRs, because Opera's cursor code gets terribly
+    // confused when the cursor is between two BRs.
+    var afterBR = true;
+
+    // Insert a normalized node at the current point. If it is a text
+    // node, wrap it in a <span>, and give that span a currentText
+    // property -- this is used to cache the nodeValue, because
+    // directly accessing nodeValue is horribly slow on some browsers.
+    // The dirty property is used by the highlighter to determine
+    // which parts of the document have to be re-highlighted.
+    function insertPart(part){
+      var text = "\n";
+      if (part.nodeType == 3) {
+        select.snapshotChanged();
+        part = makePartSpan(part, owner);
+        text = part.currentText;
+        afterBR = false;
+      }
+      else {
+        if (afterBR && window.opera)
+          point(makePartSpan("", owner));
+        afterBR = true;
+      }
+      part.dirty = true;
+      nodeQueue.push(part);
+      point(part);
+      return text;
+    }
+
+    // Extract the text and newlines from a DOM node, insert them into
+    // the document, and return the textual content. Used to replace
+    // non-normalized nodes.
+    function writeNode(node, end) {
+      var simplified = simplifyDOM(node, end);
+      for (var i = 0; i < simplified.length; i++)
+        simplified[i] = insertPart(simplified[i]);
+      return simplified.join("");
+    }
+
+    // Check whether a node is a normalized <span> element.
+    function partNode(node){
+      if (node.isPart && node.childNodes.length == 1 && node.firstChild.nodeType == 3) {
+        node.currentText = node.firstChild.nodeValue;
+        return !/[\n\t\r]/.test(node.currentText);
+      }
+      return false;
+    }
+
+    // Advance to next node, return string for current node.
+    function next() {
+      if (!start) throw StopIteration;
+      var node = start;
+      start = node.nextSibling;
+
+      if (partNode(node)){
+        nodeQueue.push(node);
+        afterBR = false;
+        return node.currentText;
+      }
+      else if (isBR(node)) {
+        if (afterBR && window.opera)
+          node.parentNode.insertBefore(makePartSpan("", owner), node);
+        nodeQueue.push(node);
+        afterBR = true;
+        return "\n";
+      }
+      else {
+        var end = !node.nextSibling;
+        point = pointAt(node);
+        removeElement(node);
+        return writeNode(node, end);
+      }
+    }
+
+    // MochiKit iterators are objects with a next function that
+    // returns the next value or throws StopIteration when there are
+    // no more values.
+    return {next: next, nodes: nodeQueue};
+  }
+
+  // Determine the text size of a processed node.
+  function nodeSize(node) {
+    return isBR(node) ? 1 : node.currentText.length;
+  }
+
+  // Search backwards through the top-level nodes until the next BR or
+  // the start of the frame.
+  function startOfLine(node) {
+    while (node && !isBR(node)) node = node.previousSibling;
+    return node;
+  }
+  function endOfLine(node, container) {
+    if (!node) node = container.firstChild;
+    else if (isBR(node)) node = node.nextSibling;
+
+    while (node && !isBR(node)) node = node.nextSibling;
+    return node;
+  }
+
+  function time() {return new Date().getTime();}
+
+  // Client interface for searching the content of the editor. Create
+  // these by calling CodeMirror.getSearchCursor. To use, call
+  // findNext on the resulting object -- this returns a boolean
+  // indicating whether anything was found, and can be called again to
+  // skip to the next find. Use the select and replace methods to
+  // actually do something with the found locations.
+  function SearchCursor(editor, string, fromCursor, caseFold) {
+    this.editor = editor;
+    if (caseFold == undefined) {
+      caseFold = (string == string.toLowerCase());
+    }
+    this.caseFold = caseFold;
+    if (caseFold) string = string.toLowerCase();
+    this.history = editor.history;
+    this.history.commit();
+
+    // Are we currently at an occurrence of the search string?
+    this.atOccurrence = false;
+    // The object stores a set of nodes coming after its current
+    // position, so that when the current point is taken out of the
+    // DOM tree, we can still try to continue.
+    this.fallbackSize = 15;
+    var cursor;
+    // Start from the cursor when specified and a cursor can be found.
+    if (fromCursor && (cursor = select.cursorPos(this.editor.container))) {
+      this.line = cursor.node;
+      this.offset = cursor.offset;
+    }
+    else {
+      this.line = null;
+      this.offset = 0;
+    }
+    this.valid = !!string;
+
+    // Create a matcher function based on the kind of string we have.
+    var target = string.split("\n"), self = this;
+    this.matches = (target.length == 1) ?
+      // For one-line strings, searching can be done simply by calling
+      // indexOf on the current line.
+      function() {
+        var line = cleanText(self.history.textAfter(self.line).slice(self.offset));
+        var match = (self.caseFold ? line.toLowerCase() : line).indexOf(string);
+        if (match > -1)
+          return {from: {node: self.line, offset: self.offset + match},
+                  to: {node: self.line, offset: self.offset + match + string.length}};
+      } :
+      // Multi-line strings require internal iteration over lines, and
+      // some clunky checks to make sure the first match ends at the
+      // end of the line and the last match starts at the start.
+      function() {
+        var firstLine = cleanText(self.history.textAfter(self.line).slice(self.offset));
+        var match = (self.caseFold ? firstLine.toLowerCase() : firstLine).lastIndexOf(target[0]);
+        if (match == -1 || match != firstLine.length - target[0].length)
+          return false;
+        var startOffset = self.offset + match;
+
+        var line = self.history.nodeAfter(self.line);
+        for (var i = 1; i < target.length - 1; i++) {
+          var lineText = cleanText(self.history.textAfter(line));
+          if ((self.caseFold ? lineText.toLowerCase() : lineText) != target[i])
+            return false;
+          line = self.history.nodeAfter(line);
+        }
+
+        var lastLine = cleanText(self.history.textAfter(line));
+        if ((self.caseFold ? lastLine.toLowerCase() : lastLine).indexOf(target[target.length - 1]) != 0)
+          return false;
+
+        return {from: {node: self.line, offset: startOffset},
+                to: {node: line, offset: target[target.length - 1].length}};
+      };
+  }
+
+  SearchCursor.prototype = {
+    findNext: function() {
+      if (!this.valid) return false;
+      this.atOccurrence = false;
+      var self = this;
+
+      // Go back to the start of the document if the current line is
+      // no longer in the DOM tree.
+      if (this.line && !this.line.parentNode) {
+        this.line = null;
+        this.offset = 0;
+      }
+
+      // Set the cursor's position one character after the given
+      // position.
+      function saveAfter(pos) {
+        if (self.history.textAfter(pos.node).length > pos.offset) {
+          self.line = pos.node;
+          self.offset = pos.offset + 1;
+        }
+        else {
+          self.line = self.history.nodeAfter(pos.node);
+          self.offset = 0;
+        }
+      }
+
+      while (true) {
+        var match = this.matches();
+        // Found the search string.
+        if (match) {
+          this.atOccurrence = match;
+          saveAfter(match.from);
+          return true;
+        }
+        this.line = this.history.nodeAfter(this.line);
+        this.offset = 0;
+        // End of document.
+        if (!this.line) {
+          this.valid = false;
+          return false;
+        }
+      }
+    },
+
+    select: function() {
+      if (this.atOccurrence) {
+        select.setCursorPos(this.editor.container, this.atOccurrence.from, this.atOccurrence.to);
+        select.scrollToCursor(this.editor.container);
+      }
+    },
+
+    replace: function(string) {
+      if (this.atOccurrence) {
+        var end = this.editor.replaceRange(this.atOccurrence.from, this.atOccurrence.to, string);
+        this.line = end.node;
+        this.offset = end.offset;
+        this.atOccurrence = false;
+      }
+    }
+  };
+
+  // The Editor object is the main inside-the-iframe interface.
+  function Editor(options) {
+    this.options = options;
+    window.indentUnit = options.indentUnit;
+    this.parent = parent;
+    this.doc = document;
+    var container = this.container = this.doc.body;
+    this.win = window;
+    this.history = new UndoHistory(container, options.undoDepth, options.undoDelay, this);
+    var self = this;
+
+    if (!Editor.Parser)
+      throw "No parser loaded.";
+    if (options.parserConfig && Editor.Parser.configure)
+      Editor.Parser.configure(options.parserConfig);
+
+    if (!options.readOnly)
+      select.setCursorPos(container, {node: null, offset: 0});
+
+    this.dirty = [];
+    this.importCode(options.content || "");
+    this.history.onChange = options.onChange;
+
+    if (!options.readOnly) {
+      if (options.continuousScanning !== false) {
+        this.scanner = this.documentScanner(options.passTime);
+        this.delayScanning();
+      }
+
+      function setEditable() {
+        // Use contentEditable instead of designMode on IE, since designMode frames
+        // can not run any scripts. It would be nice if we could use contentEditable
+        // everywhere, but it is significantly flakier than designMode on every
+        // single non-IE browser.
+        if (document.body.contentEditable != undefined && internetExplorer)
+          document.body.contentEditable = "true";
+        else
+          document.designMode = "on";
+
+        document.documentElement.style.borderWidth = "0";
+        if (!options.textWrapping)
+          container.style.whiteSpace = "nowrap";
+      }
+
+      // If setting the frame editable fails, try again when the user
+      // focus it (happens when the frame is not visible on
+      // initialisation, in Firefox).
+      try {
+        setEditable();
+      }
+      catch(e) {
+        var focusEvent = addEventHandler(document, "focus", function() {
+          focusEvent();
+          setEditable();
+        }, true);
+      }
+
+      addEventHandler(document, "keydown", method(this, "keyDown"));
+      addEventHandler(document, "keypress", method(this, "keyPress"));
+      addEventHandler(document, "keyup", method(this, "keyUp"));
+
+      function cursorActivity() {self.cursorActivity(false);}
+      addEventHandler(document.body, "mouseup", cursorActivity);
+      addEventHandler(document.body, "cut", cursorActivity);
+
+      // workaround for a gecko bug [?] where going forward and then
+      // back again breaks designmode (no more cursor)
+      if (gecko)
+        addEventHandler(this.win, "pagehide", function(){self.unloaded = true;});
+
+      addEventHandler(document.body, "paste", function(event) {
+        cursorActivity();
+        var text = null;
+        try {
+          var clipboardData = event.clipboardData || window.clipboardData;
+          if (clipboardData) text = clipboardData.getData('Text');
+        }
+        catch(e) {}
+        if (text !== null) {
+          event.stop();
+          self.replaceSelection(text);
+          select.scrollToCursor(self.container);
+        }
+      });
+
+      if (this.options.autoMatchParens)
+        addEventHandler(document.body, "click", method(this, "scheduleParenHighlight"));
+    }
+    else if (!options.textWrapping) {
+      container.style.whiteSpace = "nowrap";
+    }
+  }
+
+  function isSafeKey(code) {
+    return (code >= 16 && code <= 18) || // shift, control, alt
+           (code >= 33 && code <= 40); // arrows, home, end
+  }
+
+  Editor.prototype = {
+    // Import a piece of code into the editor.
+    importCode: function(code) {
+      this.history.push(null, null, asEditorLines(code));
+      this.history.reset();
+    },
+
+    // Extract the code from the editor.
+    getCode: function() {
+      if (!this.container.firstChild)
+        return "";
+
+      var accum = [];
+      select.markSelection(this.win);
+      forEach(traverseDOM(this.container.firstChild), method(accum, "push"));
+      select.selectMarked();
+      return cleanText(accum.join(""));
+    },
+
+    checkLine: function(node) {
+      if (node === false || !(node == null || node.parentNode == this.container))
+        throw parent.CodeMirror.InvalidLineHandle;
+    },
+
+    cursorPosition: function(start) {
+      if (start == null) start = true;
+      var pos = select.cursorPos(this.container, start);
+      if (pos) return {line: pos.node, character: pos.offset};
+      else return {line: null, character: 0};
+    },
+
+    firstLine: function() {
+      return null;
+    },
+
+    lastLine: function() {
+      if (this.container.lastChild) return startOfLine(this.container.lastChild);
+      else return null;
+    },
+
+    nextLine: function(line) {
+      this.checkLine(line);
+      var end = endOfLine(line, this.container);
+      return end || false;
+    },
+
+    prevLine: function(line) {
+      this.checkLine(line);
+      if (line == null) return false;
+      return startOfLine(line.previousSibling);
+    },
+
+    visibleLineCount: function() {
+      var line = this.container.firstChild;
+      while (line && isBR(line)) line = line.nextSibling; // BR heights are unreliable
+      if (!line) return false;
+      var innerHeight = (window.innerHeight
+                         || document.documentElement.clientHeight
+                         || document.body.clientHeight);
+      return Math.floor(innerHeight / line.offsetHeight);
+    },
+
+    selectLines: function(startLine, startOffset, endLine, endOffset) {
+      this.checkLine(startLine);
+      var start = {node: startLine, offset: startOffset}, end = null;
+      if (endOffset !== undefined) {
+        this.checkLine(endLine);
+        end = {node: endLine, offset: endOffset};
+      }
+      select.setCursorPos(this.container, start, end);
+      select.scrollToCursor(this.container);
+    },
+
+    lineContent: function(line) {
+      var accum = [];
+      for (line = line ? line.nextSibling : this.container.firstChild;
+           line && !isBR(line); line = line.nextSibling)
+        accum.push(nodeText(line));
+      return cleanText(accum.join(""));
+    },
+
+    setLineContent: function(line, content) {
+      this.history.commit();
+      this.replaceRange({node: line, offset: 0},
+                        {node: line, offset: this.history.textAfter(line).length},
+                        content);
+      this.addDirtyNode(line);
+      this.scheduleHighlight();
+    },
+
+    removeLine: function(line) {
+      var node = line ? line.nextSibling : this.container.firstChild;
+      while (node) {
+        var next = node.nextSibling;
+        removeElement(node);
+        if (isBR(node)) break;
+        node = next;
+      }
+      this.addDirtyNode(line);
+      this.scheduleHighlight();
+    },
+
+    insertIntoLine: function(line, position, content) {
+      var before = null;
+      if (position == "end") {
+        before = endOfLine(line, this.container);
+      }
+      else {
+        for (var cur = line ? line.nextSibling : this.container.firstChild; cur; cur = cur.nextSibling) {
+          if (position == 0) {
+            before = cur;
+            break;
+          }
+          var text = nodeText(cur);
+          if (text.length > position) {
+            before = cur.nextSibling;
+            content = text.slice(0, position) + content + text.slice(position);
+            removeElement(cur);
+            break;
+          }
+          position -= text.length;
+        }
+      }
+
+      var lines = asEditorLines(content), doc = this.container.ownerDocument;
+      for (var i = 0; i < lines.length; i++) {
+        if (i > 0) this.container.insertBefore(doc.createElement("BR"), before);
+        this.container.insertBefore(makePartSpan(lines[i], doc), before);
+      }
+      this.addDirtyNode(line);
+      this.scheduleHighlight();
+    },
+
+    // Retrieve the selected text.
+    selectedText: function() {
+      var h = this.history;
+      h.commit();
+
+      var start = select.cursorPos(this.container, true),
+          end = select.cursorPos(this.container, false);
+      if (!start || !end) return "";
+
+      if (start.node == end.node)
+        return h.textAfter(start.node).slice(start.offset, end.offset);
+
+      var text = [h.textAfter(start.node).slice(start.offset)];
+      for (var pos = h.nodeAfter(start.node); pos != end.node; pos = h.nodeAfter(pos))
+        text.push(h.textAfter(pos));
+      text.push(h.textAfter(end.node).slice(0, end.offset));
+      return cleanText(text.join("\n"));
+    },
+
+    // Replace the selection with another piece of text.
+    replaceSelection: function(text) {
+      this.history.commit();
+
+      var start = select.cursorPos(this.container, true),
+          end = select.cursorPos(this.container, false);
+      if (!start || !end) return;
+
+      end = this.replaceRange(start, end, text);
+      select.setCursorPos(this.container, end);
+    },
+
+    cursorCoords: function(start) {
+      var sel = select.cursorPos(this.container, start);
+      if (!sel) return null;
+      var off = sel.offset, node = sel.node, doc = this.win.document, self = this;
+      function measureFromNode(node, xOffset) {
+        var y = -(self.win.document.body.scrollTop || self.win.document.documentElement.scrollTop || 0),
+            x = -(self.win.document.body.scrollLeft || self.win.document.documentElement.scrollLeft || 0) + xOffset;
+        forEach([node, self.win.frameElement], function(n) {
+          while (n) {x += n.offsetLeft; y += n.offsetTop;n = n.offsetParent;}
+        });
+        return {x: x, y: y, yBot: y + node.offsetHeight};
+      }
+      function withTempNode(text, f) {
+        var node = doc.createElement("SPAN");
+        node.appendChild(doc.createTextNode(text));
+        try {return f(node);}
+        finally {if (node.parentNode) node.parentNode.removeChild(node);}
+      }
+
+      while (off) {
+        node = node ? node.nextSibling : this.container.firstChild;
+        var txt = nodeText(node);
+        if (off < txt.length)
+          return withTempNode(txt.substr(0, off), function(tmp) {
+            tmp.style.position = "absolute"; tmp.style.visibility = "hidden";
+            tmp.className = node.className;
+            self.container.appendChild(tmp);
+            return measureFromNode(node, tmp.offsetWidth);
+          });
+        off -= txt.length;
+      }
+      if (node && isSpan(node))
+        return measureFromNode(node, node.offsetWidth);
+      else if (node && node.nextSibling && isSpan(node.nextSibling))
+        return measureFromNode(node.nextSibling, 0);
+      else
+        return withTempNode("\u200b", function(tmp) {
+          if (node) node.parentNode.insertBefore(tmp, node.nextSibling);
+          else self.container.insertBefore(tmp, self.container.firstChild);
+          return measureFromNode(tmp, 0);
+        });
+    },
+
+    reroutePasteEvent: function() {
+      if (this.capturingPaste || window.opera) return;
+      this.capturingPaste = true;
+      var te = window.frameElement.CodeMirror.textareaHack;
+      parent.focus();
+      te.value = "";
+      te.focus();
+
+      var self = this;
+      this.parent.setTimeout(function() {
+        self.capturingPaste = false;
+        self.win.focus();
+        if (self.selectionSnapshot) // IE hack
+          self.win.select.setBookmark(self.container, self.selectionSnapshot);
+        var text = te.value;
+        if (text) {
+          self.replaceSelection(text);
+          select.scrollToCursor(self.container);
+        }
+      }, 10);
+    },
+
+    replaceRange: function(from, to, text) {
+      var lines = asEditorLines(text);
+      lines[0] = this.history.textAfter(from.node).slice(0, from.offset) + lines[0];
+      var lastLine = lines[lines.length - 1];
+      lines[lines.length - 1] = lastLine + this.history.textAfter(to.node).slice(to.offset);
+      var end = this.history.nodeAfter(to.node);
+      this.history.push(from.node, end, lines);
+      return {node: this.history.nodeBefore(end),
+              offset: lastLine.length};
+    },
+
+    getSearchCursor: function(string, fromCursor, caseFold) {
+      return new SearchCursor(this, string, fromCursor, caseFold);
+    },
+
+    // Re-indent the whole buffer
+    reindent: function() {
+      if (this.container.firstChild)
+        this.indentRegion(null, this.container.lastChild);
+    },
+
+    reindentSelection: function(direction) {
+      if (!select.somethingSelected(this.win)) {
+        this.indentAtCursor(direction);
+      }
+      else {
+        var start = select.selectionTopNode(this.container, true),
+            end = select.selectionTopNode(this.container, false);
+        if (start === false || end === false) return;
+        this.indentRegion(start, end, direction);
+      }
+    },
+
+    grabKeys: function(eventHandler, filter) {
+      this.frozen = eventHandler;
+      this.keyFilter = filter;
+    },
+    ungrabKeys: function() {
+      this.frozen = "leave";
+    },
+
+    setParser: function(name, parserConfig) {
+      Editor.Parser = window[name];
+      parserConfig = parserConfig || this.options.parserConfig;
+      if (parserConfig && Editor.Parser.configure)
+        Editor.Parser.configure(parserConfig);
+
+      if (this.container.firstChild) {
+        forEach(this.container.childNodes, function(n) {
+          if (n.nodeType != 3) n.dirty = true;
+        });
+        this.addDirtyNode(this.firstChild);
+        this.scheduleHighlight();
+      }
+    },
+
+    // Intercept enter and tab, and assign their new functions.
+    keyDown: function(event) {
+      if (this.frozen == "leave") {this.frozen = null; this.keyFilter = null;}
+      if (this.frozen && (!this.keyFilter || this.keyFilter(event.keyCode, event))) {
+        event.stop();
+        this.frozen(event);
+        return;
+      }
+
+      var code = this.lastKeyDownCode = event.keyCode;
+      // Don't scan when the user is typing.
+      this.delayScanning();
+      // Schedule a paren-highlight event, if configured.
+      if (this.options.autoMatchParens)
+        this.scheduleParenHighlight();
+
+      // The various checks for !altKey are there because AltGr sets both
+      // ctrlKey and altKey to true, and should not be recognised as
+      // Control.
+      if (code == 13) { // enter
+        if (event.ctrlKey && !event.altKey) {
+          this.reparseBuffer();
+        }
+        else {
+          select.insertNewlineAtCursor(this.win);
+          this.indentAtCursor();
+          select.scrollToCursor(this.container);
+        }
+        event.stop();
+      }
+      else if (code == 9 && this.options.tabMode != "default" && !event.ctrlKey) { // tab
+        this.handleTab(!event.shiftKey);
+        event.stop();
+      }
+      else if (code == 32 && event.shiftKey && this.options.tabMode == "default") { // space
+        this.handleTab(true);
+        event.stop();
+      }
+      else if (code == 36 && !event.shiftKey && !event.ctrlKey) { // home
+        if (this.home()) event.stop();
+      }
+      else if (code == 35 && !event.shiftKey && !event.ctrlKey) { // end
+        if (this.end()) event.stop();
+      }
+      // Only in Firefox is the default behavior for PgUp/PgDn correct.
+      else if (code == 33 && !event.shiftKey && !event.ctrlKey && !gecko) { // PgUp
+        if (this.pageUp()) event.stop();
+      }
+      else if (code == 34 && !event.shiftKey && !event.ctrlKey && !gecko) {  // PgDn
+        if (this.pageDown()) event.stop();
+      }
+      else if ((code == 219 || code == 221) && event.ctrlKey && !event.altKey) { // [, ]
+        this.highlightParens(event.shiftKey, true);
+        event.stop();
+      }
+      else if (event.metaKey && !event.shiftKey && (code == 37 || code == 39)) { // Meta-left/right
+        var cursor = select.selectionTopNode(this.container);
+        if (cursor !== false && this.container.firstChild) {
+          if (code == 37) select.focusAfterNode(startOfLine(cursor), this.container);
+          else {
+            var end = endOfLine(cursor, this.container);
+            select.focusAfterNode(end ? end.previousSibling : this.container.lastChild, this.container);
+          }
+          event.stop();
+        }
+      }
+      else if ((event.ctrlKey || event.metaKey) && !event.altKey) {
+        if ((event.shiftKey && code == 90) || code == 89) { // shift-Z, Y
+          select.scrollToNode(this.history.redo());
+          event.stop();
+        }
+        else if (code == 90 || (safari && code == 8)) { // Z, backspace
+          select.scrollToNode(this.history.undo());
+          event.stop();
+        }
+        else if (code == 83 && this.options.saveFunction) { // S
+          this.options.saveFunction();
+          event.stop();
+        }
+        else if (internetExplorer && code == 86) {
+          this.reroutePasteEvent();
+        }
+      }
+      this.keyUpOrPressAfterLastKeyDown = false;
+    },
+
+    // Check for characters that should re-indent the current line,
+    // and prevent Opera from handling enter and tab anyway.
+    keyPress: function(event) {
+      this.keyUpOrPressAfterLastKeyDown = true;
+      var electric = Editor.Parser.electricChars, self = this;
+      // Hack for Opera, and Firefox on OS X, in which stopping a
+      // keydown event does not prevent the associated keypress event
+      // from happening, so we have to cancel enter and tab again
+      // here.
+      if ((this.frozen && (!this.keyFilter || this.keyFilter(event.keyCode || event.code, event))) ||
+          event.code == 13 || (event.code == 9 && this.options.tabMode != "default") ||
+          (event.code == 32 && event.shiftKey && this.options.tabMode == "default"))
+        event.stop();
+      else if (electric && electric.indexOf(event.character) != -1)
+        this.parent.setTimeout(function(){self.indentAtCursor(null);}, 0);
+      else if ((event.character == "v" || event.character == "V")
+               && (event.ctrlKey || event.metaKey) && !event.altKey) // ctrl-V
+        this.reroutePasteEvent();
+      // Work around a bug where pressing backspace at the end of a
+      // line often causes the cursor to jump to the start of the line
+      // in Opera 10.60.
+      else if (brokenOpera && event.code == 8) {
+        var sel = select.selectionTopNode(this.container), self = this,
+            next = sel ? sel.nextSibling : this.container.firstChild;
+        if (sel !== false && next && isBR(next))
+          this.parent.setTimeout(function(){
+            if (select.selectionTopNode(self.container) == next)
+              select.focusAfterNode(next.previousSibling, self.container);
+          }, 20);
+      }
+    },
+
+    // Mark the node at the cursor dirty when a non-safe key is
+    // released.
+    keyUp: function(event) {
+      this.keyUpOrPressAfterLastKeyDown = true;
+      this.cursorActivity(isSafeKey(event.keyCode));
+    },
+
+    // Indent the line following a given <br>, or null for the first
+    // line. If given a <br> element, this must have been highlighted
+    // so that it has an indentation method. Returns the whitespace
+    // element that has been modified or created (if any).
+    indentLineAfter: function(start, direction) {
+      // whiteSpace is the whitespace span at the start of the line,
+      // or null if there is no such node.
+      var whiteSpace = start ? start.nextSibling : this.container.firstChild;
+      if (whiteSpace && !hasClass(whiteSpace, "whitespace"))
+        whiteSpace = null;
+
+      // Sometimes the start of the line can influence the correct
+      // indentation, so we retrieve it.
+      var firstText = whiteSpace ? whiteSpace.nextSibling : (start ? start.nextSibling : this.container.firstChild);
+      var nextChars = (start && firstText && firstText.currentText) ? firstText.currentText : "";
+
+      // Ask the lexical context for the correct indentation, and
+      // compute how much this differs from the current indentation.
+      var newIndent = 0, curIndent = whiteSpace ? whiteSpace.currentText.length : 0;
+      if (direction != null && this.options.tabMode == "shift")
+        newIndent = direction ? curIndent + indentUnit : Math.max(0, curIndent - indentUnit)
+      else if (start)
+        newIndent = start.indentation(nextChars, curIndent, direction);
+      else if (Editor.Parser.firstIndentation)
+        newIndent = Editor.Parser.firstIndentation(nextChars, curIndent, direction);
+      var indentDiff = newIndent - curIndent;
+
+      // If there is too much, this is just a matter of shrinking a span.
+      if (indentDiff < 0) {
+        if (newIndent == 0) {
+          if (firstText) select.snapshotMove(whiteSpace.firstChild, firstText.firstChild, 0);
+          removeElement(whiteSpace);
+          whiteSpace = null;
+        }
+        else {
+          select.snapshotMove(whiteSpace.firstChild, whiteSpace.firstChild, indentDiff, true);
+          whiteSpace.currentText = makeWhiteSpace(newIndent);
+          whiteSpace.firstChild.nodeValue = whiteSpace.currentText;
+        }
+      }
+      // Not enough...
+      else if (indentDiff > 0) {
+        // If there is whitespace, we grow it.
+        if (whiteSpace) {
+          whiteSpace.currentText = makeWhiteSpace(newIndent);
+          whiteSpace.firstChild.nodeValue = whiteSpace.currentText;
+          select.snapshotMove(whiteSpace.firstChild, whiteSpace.firstChild, indentDiff, true);
+        }
+        // Otherwise, we have to add a new whitespace node.
+        else {
+          whiteSpace = makePartSpan(makeWhiteSpace(newIndent), this.doc);
+          whiteSpace.className = "whitespace";
+          if (start) insertAfter(whiteSpace, start);
+          else this.container.insertBefore(whiteSpace, this.container.firstChild);
+          select.snapshotMove(firstText && (firstText.firstChild || firstText),
+                              whiteSpace.firstChild, newIndent, false, true);
+        }
+      }
+      if (indentDiff != 0) this.addDirtyNode(start);
+    },
+
+    // Re-highlight the selected part of the document.
+    highlightAtCursor: function() {
+      var pos = select.selectionTopNode(this.container, true);
+      var to = select.selectionTopNode(this.container, false);
+      if (pos === false || to === false) return false;
+
+      select.markSelection(this.win);
+      if (this.highlight(pos, endOfLine(to, this.container), true, 20) === false)
+        return false;
+      select.selectMarked();
+      return true;
+    },
+
+    // When tab is pressed with text selected, the whole selection is
+    // re-indented, when nothing is selected, the line with the cursor
+    // is re-indented.
+    handleTab: function(direction) {
+      if (this.options.tabMode == "spaces")
+        select.insertTabAtCursor(this.win);
+      else
+        this.reindentSelection(direction);
+    },
+
+    // Custom home behaviour that doesn't land the cursor in front of
+    // leading whitespace unless pressed twice.
+    home: function() {
+      var cur = select.selectionTopNode(this.container, true), start = cur;
+      if (cur === false || !(!cur || cur.isPart || isBR(cur)) || !this.container.firstChild)
+        return false;
+
+      while (cur && !isBR(cur)) cur = cur.previousSibling;
+      var next = cur ? cur.nextSibling : this.container.firstChild;
+      if (next && next != start && next.isPart && hasClass(next, "whitespace"))
+        select.focusAfterNode(next, this.container);
+      else
+        select.focusAfterNode(cur, this.container);
+
+      select.scrollToCursor(this.container);
+      return true;
+    },
+
+    // Some browsers (Opera) don't manage to handle the end key
+    // properly in the face of vertical scrolling.
+    end: function() {
+      var cur = select.selectionTopNode(this.container, true);
+      if (cur === false) return false;
+      cur = endOfLine(cur, this.container);
+      if (!cur) return false;
+      select.focusAfterNode(cur.previousSibling, this.container);
+      select.scrollToCursor(this.container);
+      return true;
+    },
+
+    pageUp: function() {
+      var line = this.cursorPosition().line, scrollAmount = this.visibleLineCount();
+      if (line === false || scrollAmount === false) return false;
+      // Try to keep one line on the screen.
+      scrollAmount -= 2;
+      for (var i = 0; i < scrollAmount; i++) {
+        line = this.prevLine(line);
+        if (line === false) break;
+      }
+      if (i == 0) return false; // Already at first line
+      select.setCursorPos(this.container, {node: line, offset: 0});
+      select.scrollToCursor(this.container);
+      return true;
+    },
+
+    pageDown: function() {
+      var line = this.cursorPosition().line, scrollAmount = this.visibleLineCount();
+      if (line === false || scrollAmount === false) return false;
+      // Try to move to the last line of the current page.
+      scrollAmount -= 2;
+      for (var i = 0; i < scrollAmount; i++) {
+        var nextLine = this.nextLine(line);
+        if (nextLine === false) break;
+        line = nextLine;
+      }
+      if (i == 0) return false; // Already at last line
+      select.setCursorPos(this.container, {node: line, offset: 0});
+      select.scrollToCursor(this.container);
+      return true;
+    },
+
+    // Delay (or initiate) the next paren highlight event.
+    scheduleParenHighlight: function() {
+      if (this.parenEvent) this.parent.clearTimeout(this.parenEvent);
+      var self = this;
+      this.parenEvent = this.parent.setTimeout(function(){self.highlightParens();}, 300);
+    },
+
+    // Take the token before the cursor. If it contains a character in
+    // '()[]{}', search for the matching paren/brace/bracket, and
+    // highlight them in green for a moment, or red if no proper match
+    // was found.
+    highlightParens: function(jump, fromKey) {
+      var self = this;
+      // give the relevant nodes a colour.
+      function highlight(node, ok) {
+        if (!node) return;
+        if (self.options.markParen) {
+          self.options.markParen(node, ok);
+        }
+        else {
+          node.style.fontWeight = "bold";
+          node.style.color = ok ? "#8F8" : "#F88";
+        }
+      }
+      function unhighlight(node) {
+        if (!node) return;
+        if (self.options.unmarkParen) {
+          self.options.unmarkParen(node);
+        }
+        else {
+          node.style.fontWeight = "";
+          node.style.color = "";
+        }
+      }
+      if (!fromKey && self.highlighted) {
+        unhighlight(self.highlighted[0]);
+        unhighlight(self.highlighted[1]);
+      }
+
+      if (!window.select) return;
+      // Clear the event property.
+      if (this.parenEvent) this.parent.clearTimeout(this.parenEvent);
+      this.parenEvent = null;
+
+      // Extract a 'paren' from a piece of text.
+      function paren(node) {
+        if (node.currentText) {
+          var match = node.currentText.match(/^[\s\u00a0]*([\(\)\[\]{}])[\s\u00a0]*$/);
+          return match && match[1];
+        }
+      }
+      // Determine the direction a paren is facing.
+      function forward(ch) {
+        return /[\(\[\{]/.test(ch);
+      }
+
+      var ch, cursor = select.selectionTopNode(this.container, true);
+      if (!cursor || !this.highlightAtCursor()) return;
+      cursor = select.selectionTopNode(this.container, true);
+      if (!(cursor && ((ch = paren(cursor)) || (cursor = cursor.nextSibling) && (ch = paren(cursor)))))
+        return;
+      // We only look for tokens with the same className.
+      var className = cursor.className, dir = forward(ch), match = matching[ch];
+
+      // Since parts of the document might not have been properly
+      // highlighted, and it is hard to know in advance which part we
+      // have to scan, we just try, and when we find dirty nodes we
+      // abort, parse them, and re-try.
+      function tryFindMatch() {
+        var stack = [], ch, ok = true;
+        for (var runner = cursor; runner; runner = dir ? runner.nextSibling : runner.previousSibling) {
+          if (runner.className == className && isSpan(runner) && (ch = paren(runner))) {
+            if (forward(ch) == dir)
+              stack.push(ch);
+            else if (!stack.length)
+              ok = false;
+            else if (stack.pop() != matching[ch])
+              ok = false;
+            if (!stack.length) break;
+          }
+          else if (runner.dirty || !isSpan(runner) && !isBR(runner)) {
+            return {node: runner, status: "dirty"};
+          }
+        }
+        return {node: runner, status: runner && ok};
+      }
+
+      while (true) {
+        var found = tryFindMatch();
+        if (found.status == "dirty") {
+          this.highlight(found.node, endOfLine(found.node));
+          // Needed because in some corner cases a highlight does not
+          // reach a node.
+          found.node.dirty = false;
+          continue;
+        }
+        else {
+          highlight(cursor, found.status);
+          highlight(found.node, found.status);
+          if (fromKey)
+            self.parent.setTimeout(function() {unhighlight(cursor); unhighlight(found.node);}, 500);
+          else
+            self.highlighted = [cursor, found.node];
+          if (jump && found.node)
+            select.focusAfterNode(found.node.previousSibling, this.container);
+          break;
+        }
+      }
+    },
+
+    // Adjust the amount of whitespace at the start of the line that
+    // the cursor is on so that it is indented properly.
+    indentAtCursor: function(direction) {
+      if (!this.container.firstChild) return;
+      // The line has to have up-to-date lexical information, so we
+      // highlight it first.
+      if (!this.highlightAtCursor()) return;
+      var cursor = select.selectionTopNode(this.container, false);
+      // If we couldn't determine the place of the cursor,
+      // there's nothing to indent.
+      if (cursor === false)
+        return;
+      select.markSelection(this.win);
+      this.indentLineAfter(startOfLine(cursor), direction);
+      select.selectMarked();
+    },
+
+    // Indent all lines whose start falls inside of the current
+    // selection.
+    indentRegion: function(start, end, direction) {
+      var current = (start = startOfLine(start)), before = start && startOfLine(start.previousSibling);
+      if (!isBR(end)) end = endOfLine(end, this.container);
+      this.addDirtyNode(start);
+
+      do {
+        var next = endOfLine(current, this.container);
+        if (current) this.highlight(before, next, true);
+        this.indentLineAfter(current, direction);
+        before = current;
+        current = next;
+      } while (current != end);
+      select.setCursorPos(this.container, {node: start, offset: 0}, {node: end, offset: 0});
+    },
+
+    // Find the node that the cursor is in, mark it as dirty, and make
+    // sure a highlight pass is scheduled.
+    cursorActivity: function(safe) {
+      // pagehide event hack above
+      if (this.unloaded) {
+        this.win.document.designMode = "off";
+        this.win.document.designMode = "on";
+        this.unloaded = false;
+      }
+
+      if (internetExplorer) {
+        this.container.createTextRange().execCommand("unlink");
+        this.selectionSnapshot = select.getBookmark(this.container);
+      }
+
+      var activity = this.options.cursorActivity;
+      if (!safe || activity) {
+        var cursor = select.selectionTopNode(this.container, false);
+        if (cursor === false || !this.container.firstChild) return;
+        cursor = cursor || this.container.firstChild;
+        if (activity) activity(cursor);
+        if (!safe) {
+          this.scheduleHighlight();
+          this.addDirtyNode(cursor);
+        }
+      }
+    },
+
+    reparseBuffer: function() {
+      forEach(this.container.childNodes, function(node) {node.dirty = true;});
+      if (this.container.firstChild)
+        this.addDirtyNode(this.container.firstChild);
+    },
+
+    // Add a node to the set of dirty nodes, if it isn't already in
+    // there.
+    addDirtyNode: function(node) {
+      node = node || this.container.firstChild;
+      if (!node) return;
+
+      for (var i = 0; i < this.dirty.length; i++)
+        if (this.dirty[i] == node) return;
+
+      if (node.nodeType != 3)
+        node.dirty = true;
+      this.dirty.push(node);
+    },
+
+    allClean: function() {
+      return !this.dirty.length;
+    },
+
+    // Cause a highlight pass to happen in options.passDelay
+    // milliseconds. Clear the existing timeout, if one exists. This
+    // way, the passes do not happen while the user is typing, and
+    // should as unobtrusive as possible.
+    scheduleHighlight: function() {
+      // Timeouts are routed through the parent window, because on
+      // some browsers designMode windows do not fire timeouts.
+      var self = this;
+      this.parent.clearTimeout(this.highlightTimeout);
+      this.highlightTimeout = this.parent.setTimeout(function(){self.highlightDirty();}, this.options.passDelay);
+    },
+
+    // Fetch one dirty node, and remove it from the dirty set.
+    getDirtyNode: function() {
+      while (this.dirty.length > 0) {
+        var found = this.dirty.pop();
+        // IE8 sometimes throws an unexplainable 'invalid argument'
+        // exception for found.parentNode
+        try {
+          // If the node has been coloured in the meantime, or is no
+          // longer in the document, it should not be returned.
+          while (found && found.parentNode != this.container)
+            found = found.parentNode;
+          if (found && (found.dirty || found.nodeType == 3))
+            return found;
+        } catch (e) {}
+      }
+      return null;
+    },
+
+    // Pick dirty nodes, and highlight them, until options.passTime
+    // milliseconds have gone by. The highlight method will continue
+    // to next lines as long as it finds dirty nodes. It returns
+    // information about the place where it stopped. If there are
+    // dirty nodes left after this function has spent all its lines,
+    // it shedules another highlight to finish the job.
+    highlightDirty: function(force) {
+      // Prevent FF from raising an error when it is firing timeouts
+      // on a page that's no longer loaded.
+      if (!window.select) return false;
+
+      if (!this.options.readOnly) select.markSelection(this.win);
+      var start, endTime = force ? null : time() + this.options.passTime;
+      while ((time() < endTime || force) && (start = this.getDirtyNode())) {
+        var result = this.highlight(start, endTime);
+        if (result && result.node && result.dirty)
+          this.addDirtyNode(result.node);
+      }
+      if (!this.options.readOnly) select.selectMarked();
+      if (start) this.scheduleHighlight();
+      return this.dirty.length == 0;
+    },
+
+    // Creates a function that, when called through a timeout, will
+    // continuously re-parse the document.
+    documentScanner: function(passTime) {
+      var self = this, pos = null;
+      return function() {
+        // FF timeout weirdness workaround.
+        if (!window.select) return;
+        // If the current node is no longer in the document... oh
+        // well, we start over.
+        if (pos && pos.parentNode != self.container)
+          pos = null;
+        select.markSelection(self.win);
+        var result = self.highlight(pos, time() + passTime, true);
+        select.selectMarked();
+        var newPos = result ? (result.node && result.node.nextSibling) : null;
+        pos = (pos == newPos) ? null : newPos;
+        self.delayScanning();
+      };
+    },
+
+    // Starts the continuous scanning process for this document after
+    // a given interval.
+    delayScanning: function() {
+      if (this.scanner) {
+        this.parent.clearTimeout(this.documentScan);
+        this.documentScan = this.parent.setTimeout(this.scanner, this.options.continuousScanning);
+      }
+    },
+
+    isIMEOn: function() {
+      // chrome:  keyDown keyCode is 229 while IME on
+      // firefox: no keyUps or keyPresses fires after first keyDown while IME on
+      return this.lastKeyDownCode == 229 || this.keyUpOrPressAfterLastKeyDown === false;
+    },
+
+    // The function that does the actual highlighting/colouring (with
+    // help from the parser and the DOM normalizer). Its interface is
+    // rather overcomplicated, because it is used in different
+    // situations: ensuring that a certain line is highlighted, or
+    // highlighting up to X milliseconds starting from a certain
+    // point. The 'from' argument gives the node at which it should
+    // start. If this is null, it will start at the beginning of the
+    // document. When a timestamp is given with the 'target' argument,
+    // it will stop highlighting at that time. If this argument holds
+    // a DOM node, it will highlight until it reaches that node. If at
+    // any time it comes across two 'clean' lines (no dirty nodes), it
+    // will stop, except when 'cleanLines' is true. maxBacktrack is
+    // the maximum number of lines to backtrack to find an existing
+    // parser instance. This is used to give up in situations where a
+    // highlight would take too long and freeze the browser interface.
+    highlight: function(from, target, cleanLines, maxBacktrack){
+      var container = this.container, self = this, active = this.options.activeTokens;
+      var endTime = (typeof target == "number" ? target : null);
+
+      if (!container.firstChild || this.isIMEOn())
+        return false;
+      // Backtrack to the first node before from that has a partial
+      // parse stored.
+      while (from && (!from.parserFromHere || from.dirty)) {
+        if (maxBacktrack != null && isBR(from) && (--maxBacktrack) < 0)
+          return false;
+        from = from.previousSibling;
+      }
+      // If we are at the end of the document, do nothing.
+      if (from && !from.nextSibling)
+        return false;
+
+      // Check whether a part (<span> node) and the corresponding token
+      // match.
+      function correctPart(token, part){
+        return !part.reduced && part.currentText == token.value && part.className == token.style;
+      }
+      // Shorten the text associated with a part by chopping off
+      // characters from the front. Note that only the currentText
+      // property gets changed. For efficiency reasons, we leave the
+      // nodeValue alone -- we set the reduced flag to indicate that
+      // this part must be replaced.
+      function shortenPart(part, minus){
+        part.currentText = part.currentText.substring(minus);
+        part.reduced = true;
+      }
+      // Create a part corresponding to a given token.
+      function tokenPart(token){
+        var part = makePartSpan(token.value, self.doc);     
+        part.className = token.style;
+        return part;
+      }
+
+      function maybeTouch(node) {
+        if (node) {
+          var old = node.oldNextSibling;
+          if (lineDirty || old === undefined || node.nextSibling != old)
+            self.history.touch(node);
+          node.oldNextSibling = node.nextSibling;
+        }
+        else {
+          var old = self.container.oldFirstChild;
+          if (lineDirty || old === undefined || self.container.firstChild != old)
+            self.history.touch(null);
+          self.container.oldFirstChild = self.container.firstChild;
+        }
+      }
+
+      // Get the token stream. If from is null, we start with a new
+      // parser from the start of the frame, otherwise a partial parse
+      // is resumed.
+      var traversal = traverseDOM(from ? from.nextSibling : container.firstChild),
+          stream = stringStream(traversal),
+          parsed = from ? from.parserFromHere(stream) : Editor.Parser.make(stream);
+
+      function surroundedByBRs(node) {
+        return (node.previousSibling == null || isBR(node.previousSibling)) &&
+               (node.nextSibling == null || isBR(node.nextSibling));
+      }
+
+      // parts is an interface to make it possible to 'delay' fetching
+      // the next DOM node until we are completely done with the one
+      // before it. This is necessary because often the next node is
+      // not yet available when we want to proceed past the current
+      // one.
+      var parts = {
+        current: null,
+        // Fetch current node.
+        get: function(){
+          if (!this.current)
+            this.current = traversal.nodes.shift();
+          return this.current;
+        },
+        // Advance to the next part (do not fetch it yet).
+        next: function(){
+          this.current = null;
+        },
+        // Remove the current part from the DOM tree, and move to the
+        // next.
+        remove: function(){
+          container.removeChild(this.get());
+          this.current = null;
+        },
+        // Advance to the next part that is not empty, discarding empty
+        // parts.
+        getNonEmpty: function(){
+          var part = this.get();
+          // Allow empty nodes when they are alone on a line, needed
+          // for the FF cursor bug workaround (see select.js,
+          // insertNewlineAtCursor).
+          while (part && isSpan(part) && part.currentText == "") {
+            // Leave empty nodes that are alone on a line alone in
+            // Opera, since that browsers doesn't deal well with
+            // having 2 BRs in a row.
+            if (window.opera && surroundedByBRs(part)) {
+              this.next();
+              part = this.get();
+            }
+            else {
+              var old = part;
+              this.remove();
+              part = this.get();
+              // Adjust selection information, if any. See select.js for details.
+              select.snapshotMove(old.firstChild, part && (part.firstChild || part), 0);
+            }
+          }
+          
+          return part;
+        }
+      };
+
+      var lineDirty = false, prevLineDirty = true, lineNodes = 0;
+
+      // This forEach loops over the tokens from the parsed stream, and
+      // at the same time uses the parts object to proceed through the
+      // corresponding DOM nodes.
+      forEach(parsed, function(token){
+        var part = parts.getNonEmpty();
+
+        if (token.value == "\n"){
+          // The idea of the two streams actually staying synchronized
+          // is such a long shot that we explicitly check.
+          if (!isBR(part))
+            throw "Parser out of sync. Expected BR.";
+
+          if (part.dirty || !part.indentation) lineDirty = true;
+          maybeTouch(from);
+          from = part;
+
+          // Every <br> gets a copy of the parser state and a lexical
+          // context assigned to it. The first is used to be able to
+          // later resume parsing from this point, the second is used
+          // for indentation.
+          part.parserFromHere = parsed.copy();
+          part.indentation = token.indentation;
+          part.dirty = false;
+
+          // If the target argument wasn't an integer, go at least
+          // until that node.
+          if (endTime == null && part == target) throw StopIteration;
+
+          // A clean line with more than one node means we are done.
+          // Throwing a StopIteration is the way to break out of a
+          // MochiKit forEach loop.
+          if ((endTime != null && time() >= endTime) || (!lineDirty && !prevLineDirty && lineNodes > 1 && !cleanLines))
+            throw StopIteration;
+          prevLineDirty = lineDirty; lineDirty = false; lineNodes = 0;
+          parts.next();
+        }
+        else {
+          if (!isSpan(part))
+            throw "Parser out of sync. Expected SPAN.";
+          if (part.dirty)
+            lineDirty = true;
+          lineNodes++;
+
+          // If the part matches the token, we can leave it alone.
+          if (correctPart(token, part)){
+            part.dirty = false;
+            parts.next();
+          }
+          // Otherwise, we have to fix it.
+          else {
+            lineDirty = true;
+            // Insert the correct part.
+            var newPart = tokenPart(token);
+            container.insertBefore(newPart, part);
+            if (active) active(newPart, token, self);
+            var tokensize = token.value.length;
+            var offset = 0;
+            // Eat up parts until the text for this token has been
+            // removed, adjusting the stored selection info (see
+            // select.js) in the process.
+            while (tokensize > 0) {
+              part = parts.get();
+              var partsize = part.currentText.length;
+              select.snapshotReplaceNode(part.firstChild, newPart.firstChild, tokensize, offset);
+              if (partsize > tokensize){
+                shortenPart(part, tokensize);
+                tokensize = 0;
+              }
+              else {
+                tokensize -= partsize;
+                offset += partsize;
+                parts.remove();
+              }
+            }
+          }
+        }
+      });
+      maybeTouch(from);
+
+      // The function returns some status information that is used by
+      // hightlightDirty to determine whether and where it has to
+      // continue.
+      return {node: parts.getNonEmpty(),
+              dirty: lineDirty};
+    }
+  };
+
+  return Editor;
+})();
+
+addEventHandler(window, "load", function() {
+  var CodeMirror = window.frameElement.CodeMirror;
+  var e = CodeMirror.editor = new Editor(CodeMirror.options);
+  this.parent.setTimeout(method(CodeMirror, "init"), 0);
+});
diff --git a/src/redakcja/static/js/lib/codemirror-0.8/parsexml.js b/src/redakcja/static/js/lib/codemirror-0.8/parsexml.js
new file mode 100644 (file)
index 0000000..994efd3
--- /dev/null
@@ -0,0 +1,286 @@
+/* This file defines an XML parser, with a few kludges to make it
+ * useable for HTML. autoSelfClosers defines a set of tag names that
+ * are expected to not have a closing tag, and doNotIndent specifies
+ * the tags inside of which no indentation should happen (see Config
+ * object). These can be disabled by passing the editor an object like
+ * {useHTMLKludges: false} as parserConfig option.
+ */
+
+var XMLParser = Editor.Parser = (function() {
+  var Kludges = {
+    autoSelfClosers: {"br": true, "img": true, "hr": true, "link": true, "input": true,
+                      "meta": true, "col": true, "frame": true, "base": true, "area": true},
+    doNotIndent: {"pre": true, "!cdata": true}
+  };
+  var NoKludges = {autoSelfClosers: {}, doNotIndent: {"!cdata": true}};
+  var UseKludges = Kludges;
+  var alignCDATA = false;
+
+  // Simple stateful tokenizer for XML documents. Returns a
+  // MochiKit-style iterator, with a state property that contains a
+  // function encapsulating the current state. See tokenize.js.
+  var tokenizeXML = (function() {
+    function inText(source, setState) {
+      var ch = source.next();
+      if (ch == "<") {
+        if (source.equals("!")) {
+          source.next();
+          if (source.equals("[")) {
+            if (source.lookAhead("[CDATA[", true)) {
+              setState(inBlock("xml-cdata", "]]>"));
+              return null;
+            }
+            else {
+              return "xml-text";
+            }
+          }
+          else if (source.lookAhead("--", true)) {
+            setState(inBlock("xml-comment", "-->"));
+            return null;
+          }
+          else {
+            return "xml-text";
+          }
+        }
+        else if (source.equals("?")) {
+          source.next();
+          source.nextWhileMatches(/[\w\._\-]/);
+          setState(inBlock("xml-processing", "?>"));
+          return "xml-processing";
+        }
+        else {
+          if (source.equals("/")) source.next();
+          setState(inTag);
+          return "xml-punctuation";
+        }
+      }
+      else if (ch == "&") {
+        while (!source.endOfLine()) {
+          if (source.next() == ";")
+            break;
+        }
+        return "xml-entity";
+      }
+      else {
+        source.nextWhileMatches(/[^&<\n]/);
+        return "xml-text";
+      }
+    }
+
+    function inTag(source, setState) {
+      var ch = source.next();
+      if (ch == ">") {
+        setState(inText);
+        return "xml-punctuation";
+      }
+      else if (/[?\/]/.test(ch) && source.equals(">")) {
+        source.next();
+        setState(inText);
+        return "xml-punctuation";
+      }
+      else if (ch == "=") {
+        return "xml-punctuation";
+      }
+      else if (/[\'\"]/.test(ch)) {
+        setState(inAttribute(ch));
+        return null;
+      }
+      else {
+        source.nextWhileMatches(/[^\s\u00a0=<>\"\'\/?]/);
+        return "xml-name";
+      }
+    }
+
+    function inAttribute(quote) {
+      return function(source, setState) {
+        while (!source.endOfLine()) {
+          if (source.next() == quote) {
+            setState(inTag);
+            break;
+          }
+        }
+        return "xml-attribute";
+      };
+    }
+
+    function inBlock(style, terminator) {
+      return function(source, setState) {
+        while (!source.endOfLine()) {
+          if (source.lookAhead(terminator, true)) {
+            setState(inText);
+            break;
+          }
+          source.next();
+        }
+        return style;
+      };
+    }
+
+    return function(source, startState) {
+      return tokenizer(source, startState || inText);
+    };
+  })();
+
+  // The parser. The structure of this function largely follows that of
+  // parseJavaScript in parsejavascript.js (there is actually a bit more
+  // shared code than I'd like), but it is quite a bit simpler.
+  function parseXML(source) {
+    var tokens = tokenizeXML(source), token;
+    var cc = [base];
+    var tokenNr = 0, indented = 0;
+    var currentTag = null, context = null;
+    var consume;
+    
+    function push(fs) {
+      for (var i = fs.length - 1; i >= 0; i--)
+        cc.push(fs[i]);
+    }
+    function cont() {
+      push(arguments);
+      consume = true;
+    }
+    function pass() {
+      push(arguments);
+      consume = false;
+    }
+
+    function markErr() {
+      token.style += " xml-error";
+    }
+    function expect(text) {
+      return function(style, content) {
+        if (content == text) cont();
+        else {markErr(); cont(arguments.callee);}
+      };
+    }
+
+    function pushContext(tagname, startOfLine) {
+      var noIndent = UseKludges.doNotIndent.hasOwnProperty(tagname) || (context && context.noIndent);
+      context = {prev: context, name: tagname, indent: indented, startOfLine: startOfLine, noIndent: noIndent};
+    }
+    function popContext() {
+      context = context.prev;
+    }
+    function computeIndentation(baseContext) {
+      return function(nextChars, current) {
+        var context = baseContext;
+        if (context && context.noIndent)
+          return current;
+        if (alignCDATA && /<!\[CDATA\[/.test(nextChars))
+          return 0;
+        if (context && /^<\//.test(nextChars))
+          context = context.prev;
+        while (context && !context.startOfLine)
+          context = context.prev;
+        if (context)
+          return context.indent + indentUnit;
+        else
+          return 0;
+      };
+    }
+
+    function base() {
+      return pass(element, base);
+    }
+    var harmlessTokens = {"xml-text": true, "xml-entity": true, "xml-comment": true, "xml-processing": true};
+    function element(style, content) {
+      if (content == "<") cont(tagname, attributes, endtag(tokenNr == 1));
+      else if (content == "</") cont(closetagname, expect(">"));
+      else if (style == "xml-cdata") {
+        if (!context || context.name != "!cdata") pushContext("!cdata");
+        if (/\]\]>$/.test(content)) popContext();
+        cont();
+      }
+      else if (harmlessTokens.hasOwnProperty(style)) cont();
+      else {markErr(); cont();}
+    }
+    function tagname(style, content) {
+      if (style == "xml-name") {
+        currentTag = content.toLowerCase();
+        token.style = "xml-tagname";
+        cont();
+      }
+      else {
+        currentTag = null;
+        pass();
+      }
+    }
+    function closetagname(style, content) {
+      if (style == "xml-name") {
+        token.style = "xml-tagname";
+        if (context && content.toLowerCase() == context.name) popContext();
+        else markErr();
+      }
+      cont();
+    }
+    function endtag(startOfLine) {
+      return function(style, content) {
+        if (content == "/>" || (content == ">" && UseKludges.autoSelfClosers.hasOwnProperty(currentTag))) cont();
+        else if (content == ">") {pushContext(currentTag, startOfLine); cont();}
+        else {markErr(); cont(arguments.callee);}
+      };
+    }
+    function attributes(style) {
+      if (style == "xml-name") {token.style = "xml-attname"; cont(attribute, attributes);}
+      else pass();
+    }
+    function attribute(style, content) {
+      if (content == "=") cont(value);
+      else if (content == ">" || content == "/>") pass(endtag);
+      else pass();
+    }
+    function value(style) {
+      if (style == "xml-attribute") cont(value);
+      else pass();
+    }
+
+    return {
+      indentation: function() {return indented;},
+
+      next: function(){
+        token = tokens.next();
+        if (token.style == "whitespace" && tokenNr == 0)
+          indented = token.value.length;
+        else
+          tokenNr++;
+        if (token.content == "\n") {
+          indented = tokenNr = 0;
+          token.indentation = computeIndentation(context);
+        }
+
+        if (token.style == "whitespace" || token.type == "xml-comment")
+          return token;
+
+        while(true){
+          consume = false;
+          cc.pop()(token.style, token.content);
+          if (consume) return token;
+        }
+      },
+
+      copy: function(){
+        var _cc = cc.concat([]), _tokenState = tokens.state, _context = context;
+        var parser = this;
+        
+        return function(input){
+          cc = _cc.concat([]);
+          tokenNr = indented = 0;
+          context = _context;
+          tokens = tokenizeXML(input, _tokenState);
+          return parser;
+        };
+      }
+    };
+  }
+
+  return {
+    make: parseXML,
+    electricChars: "/",
+    configure: function(config) {
+      if (config.useHTMLKludges != null)
+        UseKludges = config.useHTMLKludges ? Kludges : NoKludges;
+      if (config.alignCDATA)
+        alignCDATA = config.alignCDATA;
+    }
+  };
+})();
diff --git a/src/redakcja/static/js/lib/codemirror-0.8/select.js b/src/redakcja/static/js/lib/codemirror-0.8/select.js
new file mode 100644 (file)
index 0000000..01fe996
--- /dev/null
@@ -0,0 +1,672 @@
+/* Functionality for finding, storing, and restoring selections
+ *
+ * This does not provide a generic API, just the minimal functionality
+ * required by the CodeMirror system.
+ */
+
+// Namespace object.
+var select = {};
+
+(function() {
+  select.ie_selection = document.selection && document.selection.createRangeCollection;
+
+  // Find the 'top-level' (defined as 'a direct child of the node
+  // passed as the top argument') node that the given node is
+  // contained in. Return null if the given node is not inside the top
+  // node.
+  function topLevelNodeAt(node, top) {
+    while (node && node.parentNode != top)
+      node = node.parentNode;
+    return node;
+  }
+
+  // Find the top-level node that contains the node before this one.
+  function topLevelNodeBefore(node, top) {
+    while (!node.previousSibling && node.parentNode != top)
+      node = node.parentNode;
+    return topLevelNodeAt(node.previousSibling, top);
+  }
+
+  var fourSpaces = "\u00a0\u00a0\u00a0\u00a0";
+
+  select.scrollToNode = function(node, cursor) {
+    if (!node) return;
+    var element = node,
+        doc = element.ownerDocument, body = doc.body,
+        win = (doc.defaultView || doc.parentWindow),
+        html = doc.documentElement,
+        atEnd = !element.nextSibling || !element.nextSibling.nextSibling
+                || !element.nextSibling.nextSibling.nextSibling;
+    // In Opera (and recent Webkit versions), BR elements *always*
+    // have a offsetTop property of zero.
+    var compensateHack = 0;
+    while (element && !element.offsetTop) {
+      compensateHack++;
+      element = element.previousSibling;
+    }
+    // atEnd is another kludge for these browsers -- if the cursor is
+    // at the end of the document, and the node doesn't have an
+    // offset, just scroll to the end.
+    if (compensateHack == 0) atEnd = false;
+
+    // WebKit has a bad habit of (sometimes) happily returning bogus
+    // offsets when the document has just been changed. This seems to
+    // always be 5/5, so we don't use those.
+    if (webkit && element && element.offsetTop == 5 && element.offsetLeft == 5)
+      return;
+
+    var y = compensateHack * (element ? element.offsetHeight : 0), x = 0,
+        width = (node ? node.offsetWidth : 0), pos = element;
+    while (pos && pos.offsetParent) {
+      y += pos.offsetTop;
+      // Don't count X offset for <br> nodes
+      if (!isBR(pos))
+        x += pos.offsetLeft;
+      pos = pos.offsetParent;
+    }
+
+    var scroll_x = body.scrollLeft || html.scrollLeft || 0,
+        scroll_y = body.scrollTop || html.scrollTop || 0,
+        scroll = false, screen_width = win.innerWidth || html.clientWidth || 0;
+
+    if (cursor || width < screen_width) {
+      if (cursor) {
+        var off = select.offsetInNode(win, node), size = nodeText(node).length;
+        if (size) x += width * (off / size);
+      }
+      var screen_x = x - scroll_x;
+      if (screen_x < 0 || screen_x > screen_width) {
+        scroll_x = x;
+        scroll = true;
+      }
+    }
+    var screen_y = y - scroll_y;
+    if (screen_y < 0 || atEnd || screen_y > (win.innerHeight || html.clientHeight || 0) - 50) {
+      scroll_y = atEnd ? 1e6 : y;
+      scroll = true;
+    }
+    if (scroll) win.scrollTo(scroll_x, scroll_y);
+  };
+
+  select.scrollToCursor = function(container) {
+    select.scrollToNode(select.selectionTopNode(container, true) || container.firstChild, true);
+  };
+
+  // Used to prevent restoring a selection when we do not need to.
+  var currentSelection = null;
+
+  select.snapshotChanged = function() {
+    if (currentSelection) currentSelection.changed = true;
+  };
+
+  // This is called by the code in editor.js whenever it is replacing
+  // a text node. The function sees whether the given oldNode is part
+  // of the current selection, and updates this selection if it is.
+  // Because nodes are often only partially replaced, the length of
+  // the part that gets replaced has to be taken into account -- the
+  // selection might stay in the oldNode if the newNode is smaller
+  // than the selection's offset. The offset argument is needed in
+  // case the selection does move to the new object, and the given
+  // length is not the whole length of the new node (part of it might
+  // have been used to replace another node).
+  select.snapshotReplaceNode = function(from, to, length, offset) {
+    if (!currentSelection) return;
+
+    function replace(point) {
+      if (from == point.node) {
+        currentSelection.changed = true;
+        if (length && point.offset > length) {
+          point.offset -= length;
+        }
+        else {
+          point.node = to;
+          point.offset += (offset || 0);
+        }
+      }
+    }
+    replace(currentSelection.start);
+    replace(currentSelection.end);
+  };
+
+  select.snapshotMove = function(from, to, distance, relative, ifAtStart) {
+    if (!currentSelection) return;
+
+    function move(point) {
+      if (from == point.node && (!ifAtStart || point.offset == 0)) {
+        currentSelection.changed = true;
+        point.node = to;
+        if (relative) point.offset = Math.max(0, point.offset + distance);
+        else point.offset = distance;
+      }
+    }
+    move(currentSelection.start);
+    move(currentSelection.end);
+  };
+
+  // Most functions are defined in two ways, one for the IE selection
+  // model, one for the W3C one.
+  if (select.ie_selection) {
+    function selectionNode(win, start) {
+      var range = win.document.selection.createRange();
+      range.collapse(start);
+
+      function nodeAfter(node) {
+        var found = null;
+        while (!found && node) {
+          found = node.nextSibling;
+          node = node.parentNode;
+        }
+        return nodeAtStartOf(found);
+      }
+
+      function nodeAtStartOf(node) {
+        while (node && node.firstChild) node = node.firstChild;
+        return {node: node, offset: 0};
+      }
+
+      var containing = range.parentElement();
+      if (!isAncestor(win.document.body, containing)) return null;
+      if (!containing.firstChild) return nodeAtStartOf(containing);
+
+      var working = range.duplicate();
+      working.moveToElementText(containing);
+      working.collapse(true);
+      for (var cur = containing.firstChild; cur; cur = cur.nextSibling) {
+        if (cur.nodeType == 3) {
+          var size = cur.nodeValue.length;
+          working.move("character", size);
+        }
+        else {
+          working.moveToElementText(cur);
+          working.collapse(false);
+        }
+
+        var dir = range.compareEndPoints("StartToStart", working);
+        if (dir == 0) return nodeAfter(cur);
+        if (dir == 1) continue;
+        if (cur.nodeType != 3) return nodeAtStartOf(cur);
+
+        working.setEndPoint("StartToEnd", range);
+        return {node: cur, offset: size - working.text.length};
+      }
+      return nodeAfter(containing);
+    }
+
+    select.markSelection = function(win) {
+      currentSelection = null;
+      var sel = win.document.selection;
+      if (!sel) return;
+      var start = selectionNode(win, true),
+          end = selectionNode(win, false);
+      if (!start || !end) return;
+      currentSelection = {start: start, end: end, window: win, changed: false};
+    };
+
+    select.selectMarked = function() {
+      if (!currentSelection || !currentSelection.changed) return;
+      var win = currentSelection.window, doc = win.document;
+
+      function makeRange(point) {
+        var range = doc.body.createTextRange(),
+            node = point.node;
+        if (!node) {
+          range.moveToElementText(currentSelection.window.document.body);
+          range.collapse(false);
+        }
+        else if (node.nodeType == 3) {
+          range.moveToElementText(node.parentNode);
+          var offset = point.offset;
+          while (node.previousSibling) {
+            node = node.previousSibling;
+            offset += (node.innerText || "").length;
+          }
+          range.move("character", offset);
+        }
+        else {
+          range.moveToElementText(node);
+          range.collapse(true);
+        }
+        return range;
+      }
+
+      var start = makeRange(currentSelection.start), end = makeRange(currentSelection.end);
+      start.setEndPoint("StartToEnd", end);
+      start.select();
+    };
+
+    select.offsetInNode = function(win, node) {
+      var sel = win.document.selection;
+      if (!sel) return 0;
+      var range = sel.createRange(), range2 = range.duplicate();
+      try {range2.moveToElementText(node);} catch(e){return 0;}
+      range.setEndPoint("StartToStart", range2);
+      return range.text.length;
+    };
+
+    // Get the top-level node that one end of the cursor is inside or
+    // after. Note that this returns false for 'no cursor', and null
+    // for 'start of document'.
+    select.selectionTopNode = function(container, start) {
+      var selection = container.ownerDocument.selection;
+      if (!selection) return false;
+
+      var range = selection.createRange(), range2 = range.duplicate();
+      range.collapse(start);
+      var around = range.parentElement();
+      if (around && isAncestor(container, around)) {
+        // Only use this node if the selection is not at its start.
+        range2.moveToElementText(around);
+        if (range.compareEndPoints("StartToStart", range2) == 1)
+          return topLevelNodeAt(around, container);
+      }
+
+      // Move the start of a range to the start of a node,
+      // compensating for the fact that you can't call
+      // moveToElementText with text nodes.
+      function moveToNodeStart(range, node) {
+        if (node.nodeType == 3) {
+          var count = 0, cur = node.previousSibling;
+          while (cur && cur.nodeType == 3) {
+            count += cur.nodeValue.length;
+            cur = cur.previousSibling;
+          }
+          if (cur) {
+            try{range.moveToElementText(cur);}
+            catch(e){return false;}
+            range.collapse(false);
+          }
+          else range.moveToElementText(node.parentNode);
+          if (count) range.move("character", count);
+        }
+        else {
+          try{range.moveToElementText(node);}
+          catch(e){return false;}
+        }
+        return true;
+      }
+
+      // Do a binary search through the container object, comparing
+      // the start of each node to the selection
+      var start = 0, end = container.childNodes.length - 1;
+      while (start < end) {
+        var middle = Math.ceil((end + start) / 2), node = container.childNodes[middle];
+        if (!node) return false; // Don't ask. IE6 manages this sometimes.
+        if (!moveToNodeStart(range2, node)) return false;
+        if (range.compareEndPoints("StartToStart", range2) == 1)
+          start = middle;
+        else
+          end = middle - 1;
+      }
+      return container.childNodes[start] || null;
+    };
+
+    // Place the cursor after this.start. This is only useful when
+    // manually moving the cursor instead of restoring it to its old
+    // position.
+    select.focusAfterNode = function(node, container) {
+      var range = container.ownerDocument.body.createTextRange();
+      range.moveToElementText(node || container);
+      range.collapse(!node);
+      range.select();
+    };
+
+    select.somethingSelected = function(win) {
+      var sel = win.document.selection;
+      return sel && (sel.createRange().text != "");
+    };
+
+    function insertAtCursor(window, html) {
+      var selection = window.document.selection;
+      if (selection) {
+        var range = selection.createRange();
+        range.pasteHTML(html);
+        range.collapse(false);
+        range.select();
+      }
+    }
+
+    // Used to normalize the effect of the enter key, since browsers
+    // do widely different things when pressing enter in designMode.
+    select.insertNewlineAtCursor = function(window) {
+      insertAtCursor(window, "<br>");
+    };
+
+    select.insertTabAtCursor = function(window) {
+      insertAtCursor(window, fourSpaces);
+    };
+
+    // Get the BR node at the start of the line on which the cursor
+    // currently is, and the offset into the line. Returns null as
+    // node if cursor is on first line.
+    select.cursorPos = function(container, start) {
+      var selection = container.ownerDocument.selection;
+      if (!selection) return null;
+
+      var topNode = select.selectionTopNode(container, start);
+      while (topNode && !isBR(topNode))
+        topNode = topNode.previousSibling;
+
+      var range = selection.createRange(), range2 = range.duplicate();
+      range.collapse(start);
+      if (topNode) {
+        range2.moveToElementText(topNode);
+        range2.collapse(false);
+      }
+      else {
+        // When nothing is selected, we can get all kinds of funky errors here.
+        try { range2.moveToElementText(container); }
+        catch (e) { return null; }
+        range2.collapse(true);
+      }
+      range.setEndPoint("StartToStart", range2);
+
+      return {node: topNode, offset: range.text.length};
+    };
+
+    select.setCursorPos = function(container, from, to) {
+      function rangeAt(pos) {
+        var range = container.ownerDocument.body.createTextRange();
+        if (!pos.node) {
+          range.moveToElementText(container);
+          range.collapse(true);
+        }
+        else {
+          range.moveToElementText(pos.node);
+          range.collapse(false);
+        }
+        range.move("character", pos.offset);
+        return range;
+      }
+
+      var range = rangeAt(from);
+      if (to && to != from)
+        range.setEndPoint("EndToEnd", rangeAt(to));
+      range.select();
+    }
+
+    // Some hacks for storing and re-storing the selection when the editor loses and regains focus.
+    select.getBookmark = function (container) {
+      var from = select.cursorPos(container, true), to = select.cursorPos(container, false);
+      if (from && to) return {from: from, to: to};
+    };
+
+    // Restore a stored selection.
+    select.setBookmark = function(container, mark) {
+      if (!mark) return;
+      select.setCursorPos(container, mark.from, mark.to);
+    };
+  }
+  // W3C model
+  else {
+    // Find the node right at the cursor, not one of its
+    // ancestors with a suitable offset. This goes down the DOM tree
+    // until a 'leaf' is reached (or is it *up* the DOM tree?).
+    function innerNode(node, offset) {
+      while (node.nodeType != 3 && !isBR(node)) {
+        var newNode = node.childNodes[offset] || node.nextSibling;
+        offset = 0;
+        while (!newNode && node.parentNode) {
+          node = node.parentNode;
+          newNode = node.nextSibling;
+        }
+        node = newNode;
+        if (!newNode) break;
+      }
+      return {node: node, offset: offset};
+    }
+
+    // Store start and end nodes, and offsets within these, and refer
+    // back to the selection object from those nodes, so that this
+    // object can be updated when the nodes are replaced before the
+    // selection is restored.
+    select.markSelection = function (win) {
+      var selection = win.getSelection();
+      if (!selection || selection.rangeCount == 0)
+        return (currentSelection = null);
+      var range = selection.getRangeAt(0);
+
+      currentSelection = {
+        start: innerNode(range.startContainer, range.startOffset),
+        end: innerNode(range.endContainer, range.endOffset),
+        window: win,
+        changed: false
+      };
+    };
+
+    select.selectMarked = function () {
+      var cs = currentSelection;
+      // on webkit-based browsers, it is apparently possible that the
+      // selection gets reset even when a node that is not one of the
+      // endpoints get messed with. the most common situation where
+      // this occurs is when a selection is deleted or overwitten. we
+      // check for that here.
+      function focusIssue() {
+        if (cs.start.node == cs.end.node && cs.start.offset == cs.end.offset) {
+          var selection = cs.window.getSelection();
+          if (!selection || selection.rangeCount == 0) return true;
+          var range = selection.getRangeAt(0), point = innerNode(range.startContainer, range.startOffset);
+          return cs.start.node != point.node || cs.start.offset != point.offset;
+        }
+      }
+      if (!cs || !(cs.changed || (webkit && focusIssue()))) return;
+      var win = cs.window, range = win.document.createRange();
+
+      function setPoint(point, which) {
+        if (point.node) {
+          // Some magic to generalize the setting of the start and end
+          // of a range.
+          if (point.offset == 0)
+            range["set" + which + "Before"](point.node);
+          else
+            range["set" + which](point.node, point.offset);
+        }
+        else {
+          range.setStartAfter(win.document.body.lastChild || win.document.body);
+        }
+      }
+
+      setPoint(cs.end, "End");
+      setPoint(cs.start, "Start");
+      selectRange(range, win);
+    };
+
+    // Helper for selecting a range object.
+    function selectRange(range, window) {
+      var selection = window.getSelection();
+      if (!selection) return;
+      selection.removeAllRanges();
+      selection.addRange(range);
+    }
+    function selectionRange(window) {
+      var selection = window.getSelection();
+      if (!selection || selection.rangeCount == 0)
+        return false;
+      else
+        return selection.getRangeAt(0);
+    }
+
+    // Finding the top-level node at the cursor in the W3C is, as you
+    // can see, quite an involved process.
+    select.selectionTopNode = function(container, start) {
+      var range = selectionRange(container.ownerDocument.defaultView);
+      if (!range) return false;
+
+      var node = start ? range.startContainer : range.endContainer;
+      var offset = start ? range.startOffset : range.endOffset;
+      // Work around (yet another) bug in Opera's selection model.
+      if (window.opera && !start && range.endContainer == container && range.endOffset == range.startOffset + 1 &&
+          container.childNodes[range.startOffset] && isBR(container.childNodes[range.startOffset]))
+        offset--;
+
+      // For text nodes, we look at the node itself if the cursor is
+      // inside, or at the node before it if the cursor is at the
+      // start.
+      if (node.nodeType == 3){
+        if (offset > 0)
+          return topLevelNodeAt(node, container);
+        else
+          return topLevelNodeBefore(node, container);
+      }
+      // Occasionally, browsers will return the HTML node as
+      // selection. If the offset is 0, we take the start of the frame
+      // ('after null'), otherwise, we take the last node.
+      else if (node.nodeName.toUpperCase() == "HTML") {
+        return (offset == 1 ? null : container.lastChild);
+      }
+      // If the given node is our 'container', we just look up the
+      // correct node by using the offset.
+      else if (node == container) {
+        return (offset == 0) ? null : node.childNodes[offset - 1];
+      }
+      // In any other case, we have a regular node. If the cursor is
+      // at the end of the node, we use the node itself, if it is at
+      // the start, we use the node before it, and in any other
+      // case, we look up the child before the cursor and use that.
+      else {
+        if (offset == node.childNodes.length)
+          return topLevelNodeAt(node, container);
+        else if (offset == 0)
+          return topLevelNodeBefore(node, container);
+        else
+          return topLevelNodeAt(node.childNodes[offset - 1], container);
+      }
+    };
+
+    select.focusAfterNode = function(node, container) {
+      var win = container.ownerDocument.defaultView,
+          range = win.document.createRange();
+      range.setStartBefore(container.firstChild || container);
+      // In Opera, setting the end of a range at the end of a line
+      // (before a BR) will cause the cursor to appear on the next
+      // line, so we set the end inside of the start node when
+      // possible.
+      if (node && !node.firstChild)
+        range.setEndAfter(node);
+      else if (node)
+        range.setEnd(node, node.childNodes.length);
+      else
+        range.setEndBefore(container.firstChild || container);
+      range.collapse(false);
+      selectRange(range, win);
+    };
+
+    select.somethingSelected = function(win) {
+      var range = selectionRange(win);
+      return range && !range.collapsed;
+    };
+
+    select.offsetInNode = function(win, node) {
+      var range = selectionRange(win);
+      if (!range) return 0;
+      range = range.cloneRange();
+      range.setStartBefore(node);
+      return range.toString().length;
+    };
+
+    function insertNodeAtCursor(window, node) {
+      var range = selectionRange(window);
+      if (!range) return;
+
+      range.deleteContents();
+      range.insertNode(node);
+
+      // work around weirdness where Opera will magically insert a new
+      // BR node when a BR node inside a span is moved around. makes
+      // sure the BR ends up outside of spans.
+      if (window.opera && isBR(node) && isSpan(node.parentNode)) {
+        var next = node.nextSibling, p = node.parentNode, outer = p.parentNode;
+        outer.insertBefore(node, p.nextSibling);
+        var textAfter = "";
+        for (; next && next.nodeType == 3; next = next.nextSibling) {
+          textAfter += next.nodeValue;
+          removeElement(next);
+        }
+        outer.insertBefore(makePartSpan(textAfter, window.document), node.nextSibling);
+      }
+      range = window.document.createRange();
+      range.selectNode(node);
+      range.collapse(false);
+      selectRange(range, window);
+    }
+
+    select.insertNewlineAtCursor = function(window) {
+      if (webkit)
+        document.execCommand('insertLineBreak');
+      else
+        insertNodeAtCursor(window, window.document.createElement("BR"));
+    };
+
+    select.insertTabAtCursor = function(window) {
+      insertNodeAtCursor(window, window.document.createTextNode(fourSpaces));
+    };
+
+    select.cursorPos = function(container, start) {
+      var range = selectionRange(window);
+      if (!range) return;
+
+      var topNode = select.selectionTopNode(container, start);
+      while (topNode && !isBR(topNode))
+        topNode = topNode.previousSibling;
+
+      range = range.cloneRange();
+      range.collapse(start);
+      if (topNode)
+        range.setStartAfter(topNode);
+      else
+        range.setStartBefore(container);
+
+      return {node: topNode, offset: range.toString().length};
+    };
+
+    select.setCursorPos = function(container, from, to) {
+      var win = container.ownerDocument.defaultView,
+          range = win.document.createRange();
+
+      function setPoint(node, offset, side) {
+        if (offset == 0 && node && !node.nextSibling) {
+          range["set" + side + "After"](node);
+          return true;
+        }
+
+        if (!node)
+          node = container.firstChild;
+        else
+          node = node.nextSibling;
+
+        if (!node) return;
+
+        if (offset == 0) {
+          range["set" + side + "Before"](node);
+          return true;
+        }
+
+        var backlog = []
+        function decompose(node) {
+          if (node.nodeType == 3)
+            backlog.push(node);
+          else
+            forEach(node.childNodes, decompose);
+        }
+        while (true) {
+          while (node && !backlog.length) {
+            decompose(node);
+            node = node.nextSibling;
+          }
+          var cur = backlog.shift();
+          if (!cur) return false;
+
+          var length = cur.nodeValue.length;
+          if (length >= offset) {
+            range["set" + side](cur, offset);
+            return true;
+          }
+          offset -= length;
+        }
+      }
+
+      to = to || from;
+      if (setPoint(to.node, to.offset, "End") && setPoint(from.node, from.offset, "Start"))
+        selectRange(range, win);
+    };
+  }
+})();
diff --git a/src/redakcja/static/js/lib/codemirror-0.8/stringstream.js b/src/redakcja/static/js/lib/codemirror-0.8/stringstream.js
new file mode 100644 (file)
index 0000000..4f5bc61
--- /dev/null
@@ -0,0 +1,140 @@
+/* String streams are the things fed to parsers (which can feed them
+ * to a tokenizer if they want). They provide peek and next methods
+ * for looking at the current character (next 'consumes' this
+ * character, peek does not), and a get method for retrieving all the
+ * text that was consumed since the last time get was called.
+ *
+ * An easy mistake to make is to let a StopIteration exception finish
+ * the token stream while there are still characters pending in the
+ * string stream (hitting the end of the buffer while parsing a
+ * token). To make it easier to detect such errors, the stringstreams
+ * throw an exception when this happens.
+ */
+
+// Make a stringstream stream out of an iterator that returns strings.
+// This is applied to the result of traverseDOM (see codemirror.js),
+// and the resulting stream is fed to the parser.
+var stringStream = function(source){
+  // String that's currently being iterated over.
+  var current = "";
+  // Position in that string.
+  var pos = 0;
+  // Accumulator for strings that have been iterated over but not
+  // get()-ed yet.
+  var accum = "";
+  // Make sure there are more characters ready, or throw
+  // StopIteration.
+  function ensureChars() {
+    while (pos == current.length) {
+      accum += current;
+      current = ""; // In case source.next() throws
+      pos = 0;
+      try {current = source.next();}
+      catch (e) {
+        if (e != StopIteration) throw e;
+        else return false;
+      }
+    }
+    return true;
+  }
+
+  return {
+    // Return the next character in the stream.
+    peek: function() {
+      if (!ensureChars()) return null;
+      return current.charAt(pos);
+    },
+    // Get the next character, throw StopIteration if at end, check
+    // for unused content.
+    next: function() {
+      if (!ensureChars()) {
+        if (accum.length > 0)
+          throw "End of stringstream reached without emptying buffer ('" + accum + "').";
+        else
+          throw StopIteration;
+      }
+      return current.charAt(pos++);
+    },
+    // Return the characters iterated over since the last call to
+    // .get().
+    get: function() {
+      var temp = accum;
+      accum = "";
+      if (pos > 0){
+        temp += current.slice(0, pos);
+        current = current.slice(pos);
+        pos = 0;
+      }
+      return temp;
+    },
+    // Push a string back into the stream.
+    push: function(str) {
+      current = current.slice(0, pos) + str + current.slice(pos);
+    },
+    lookAhead: function(str, consume, skipSpaces, caseInsensitive) {
+      function cased(str) {return caseInsensitive ? str.toLowerCase() : str;}
+      str = cased(str);
+      var found = false;
+
+      var _accum = accum, _pos = pos;
+      if (skipSpaces) this.nextWhileMatches(/[\s\u00a0]/);
+
+      while (true) {
+        var end = pos + str.length, left = current.length - pos;
+        if (end <= current.length) {
+          found = str == cased(current.slice(pos, end));
+          pos = end;
+          break;
+        }
+        else if (str.slice(0, left) == cased(current.slice(pos))) {
+          accum += current; current = "";
+          try {current = source.next();}
+          catch (e) {break;}
+          pos = 0;
+          str = str.slice(left);
+        }
+        else {
+          break;
+        }
+      }
+
+      if (!(found && consume)) {
+        current = accum.slice(_accum.length) + current;
+        pos = _pos;
+        accum = _accum;
+      }
+
+      return found;
+    },
+
+    // Utils built on top of the above
+    more: function() {
+      return this.peek() !== null;
+    },
+    applies: function(test) {
+      var next = this.peek();
+      return (next !== null && test(next));
+    },
+    nextWhile: function(test) {
+      var next;
+      while ((next = this.peek()) !== null && test(next))
+        this.next();
+    },
+    matches: function(re) {
+      var next = this.peek();
+      return (next !== null && re.test(next));
+    },
+    nextWhileMatches: function(re) {
+      var next;
+      while ((next = this.peek()) !== null && re.test(next))
+        this.next();
+    },
+    equals: function(ch) {
+      return ch === this.peek();
+    },
+    endOfLine: function() {
+      var next = this.peek();
+      return next == null || next == "\n";
+    }
+  };
+};
diff --git a/src/redakcja/static/js/lib/codemirror-0.8/tokenize.js b/src/redakcja/static/js/lib/codemirror-0.8/tokenize.js
new file mode 100644 (file)
index 0000000..071970c
--- /dev/null
@@ -0,0 +1,57 @@
+// A framework for simple tokenizers. Takes care of newlines and
+// white-space, and of getting the text from the source stream into
+// the token object. A state is a function of two arguments -- a
+// string stream and a setState function. The second can be used to
+// change the tokenizer's state, and can be ignored for stateless
+// tokenizers. This function should advance the stream over a token
+// and return a string or object containing information about the next
+// token, or null to pass and have the (new) state be called to finish
+// the token. When a string is given, it is wrapped in a {style, type}
+// object. In the resulting object, the characters consumed are stored
+// under the content property. Any whitespace following them is also
+// automatically consumed, and added to the value property. (Thus,
+// content is the actual meaningful part of the token, while value
+// contains all the text it spans.)
+
+function tokenizer(source, state) {
+  // Newlines are always a separate token.
+  function isWhiteSpace(ch) {
+    // The messy regexp is because IE's regexp matcher is of the
+    // opinion that non-breaking spaces are no whitespace.
+    return ch != "\n" && /^[\s\u00a0]*$/.test(ch);
+  }
+
+  var tokenizer = {
+    state: state,
+
+    take: function(type) {
+      if (typeof(type) == "string")
+        type = {style: type, type: type};
+
+      type.content = (type.content || "") + source.get();
+      if (!/\n$/.test(type.content))
+        source.nextWhile(isWhiteSpace);
+      type.value = type.content + source.get();
+      return type;
+    },
+
+    next: function () {
+      if (!source.more()) throw StopIteration;
+
+      var type;
+      if (source.equals("\n")) {
+        source.next();
+        return this.take("whitespace");
+      }
+      
+      if (source.applies(isWhiteSpace))
+        type = "whitespace";
+      else
+        while (!type)
+          type = this.state(source, function(s) {tokenizer.state = s;});
+
+      return this.take(type);
+    }
+  };
+  return tokenizer;
+}
diff --git a/src/redakcja/static/js/lib/codemirror-0.8/undo.js b/src/redakcja/static/js/lib/codemirror-0.8/undo.js
new file mode 100644 (file)
index 0000000..4ea2385
--- /dev/null
@@ -0,0 +1,410 @@
+/**
+ * Storage and control for undo information within a CodeMirror
+ * editor. 'Why on earth is such a complicated mess required for
+ * that?', I hear you ask. The goal, in implementing this, was to make
+ * the complexity of storing and reverting undo information depend
+ * only on the size of the edited or restored content, not on the size
+ * of the whole document. This makes it necessary to use a kind of
+ * 'diff' system, which, when applied to a DOM tree, causes some
+ * complexity and hackery.
+ *
+ * In short, the editor 'touches' BR elements as it parses them, and
+ * the UndoHistory stores these. When nothing is touched in commitDelay
+ * milliseconds, the changes are committed: It goes over all touched
+ * nodes, throws out the ones that did not change since last commit or
+ * are no longer in the document, and assembles the rest into zero or
+ * more 'chains' -- arrays of adjacent lines. Links back to these
+ * chains are added to the BR nodes, while the chain that previously
+ * spanned these nodes is added to the undo history. Undoing a change
+ * means taking such a chain off the undo history, restoring its
+ * content (text is saved per line) and linking it back into the
+ * document.
+ */
+
+// A history object needs to know about the DOM container holding the
+// document, the maximum amount of undo levels it should store, the
+// delay (of no input) after which it commits a set of changes, and,
+// unfortunately, the 'parent' window -- a window that is not in
+// designMode, and on which setTimeout works in every browser.
+function UndoHistory(container, maxDepth, commitDelay, editor) {
+  this.container = container;
+  this.maxDepth = maxDepth; this.commitDelay = commitDelay;
+  this.editor = editor; this.parent = editor.parent;
+  // This line object represents the initial, empty editor.
+  var initial = {text: "", from: null, to: null};
+  // As the borders between lines are represented by BR elements, the
+  // start of the first line and the end of the last one are
+  // represented by null. Since you can not store any properties
+  // (links to line objects) in null, these properties are used in
+  // those cases.
+  this.first = initial; this.last = initial;
+  // Similarly, a 'historyTouched' property is added to the BR in
+  // front of lines that have already been touched, and 'firstTouched'
+  // is used for the first line.
+  this.firstTouched = false;
+  // History is the set of committed changes, touched is the set of
+  // nodes touched since the last commit.
+  this.history = []; this.redoHistory = []; this.touched = [];
+}
+
+UndoHistory.prototype = {
+  // Schedule a commit (if no other touches come in for commitDelay
+  // milliseconds).
+  scheduleCommit: function() {
+    var self = this;
+    this.parent.clearTimeout(this.commitTimeout);
+    this.commitTimeout = this.parent.setTimeout(function(){self.tryCommit();}, this.commitDelay);
+  },
+
+  // Mark a node as touched. Null is a valid argument.
+  touch: function(node) {
+    this.setTouched(node);
+    this.scheduleCommit();
+  },
+
+  // Undo the last change.
+  undo: function() {
+    // Make sure pending changes have been committed.
+    this.commit();
+
+    if (this.history.length) {
+      // Take the top diff from the history, apply it, and store its
+      // shadow in the redo history.
+      var item = this.history.pop();
+      this.redoHistory.push(this.updateTo(item, "applyChain"));
+      this.notifyEnvironment();
+      return this.chainNode(item);
+    }
+  },
+
+  // Redo the last undone change.
+  redo: function() {
+    this.commit();
+    if (this.redoHistory.length) {
+      // The inverse of undo, basically.
+      var item = this.redoHistory.pop();
+      this.addUndoLevel(this.updateTo(item, "applyChain"));
+      this.notifyEnvironment();
+      return this.chainNode(item);
+    }
+  },
+
+  clear: function() {
+    this.history = [];
+    this.redoHistory = [];
+  },
+
+  // Ask for the size of the un/redo histories.
+  historySize: function() {
+    return {undo: this.history.length, redo: this.redoHistory.length};
+  },
+
+  // Push a changeset into the document.
+  push: function(from, to, lines) {
+    var chain = [];
+    for (var i = 0; i < lines.length; i++) {
+      var end = (i == lines.length - 1) ? to : this.container.ownerDocument.createElement("BR");
+      chain.push({from: from, to: end, text: cleanText(lines[i])});
+      from = end;
+    }
+    this.pushChains([chain], from == null && to == null);
+    this.notifyEnvironment();
+  },
+
+  pushChains: function(chains, doNotHighlight) {
+    this.commit(doNotHighlight);
+    this.addUndoLevel(this.updateTo(chains, "applyChain"));
+    this.redoHistory = [];
+  },
+
+  // Retrieve a DOM node from a chain (for scrolling to it after undo/redo).
+  chainNode: function(chains) {
+    for (var i = 0; i < chains.length; i++) {
+      var start = chains[i][0], node = start && (start.from || start.to);
+      if (node) return node;
+    }
+  },
+
+  // Clear the undo history, make the current document the start
+  // position.
+  reset: function() {
+    this.history = []; this.redoHistory = [];
+  },
+
+  textAfter: function(br) {
+    return this.after(br).text;
+  },
+
+  nodeAfter: function(br) {
+    return this.after(br).to;
+  },
+
+  nodeBefore: function(br) {
+    return this.before(br).from;
+  },
+
+  // Commit unless there are pending dirty nodes.
+  tryCommit: function() {
+    if (!window.UndoHistory) return; // Stop when frame has been unloaded
+    if (this.editor.highlightDirty()) this.commit(true);
+    else this.scheduleCommit();
+  },
+
+  // Check whether the touched nodes hold any changes, if so, commit
+  // them.
+  commit: function(doNotHighlight) {
+    this.parent.clearTimeout(this.commitTimeout);
+    // Make sure there are no pending dirty nodes.
+    if (!doNotHighlight) this.editor.highlightDirty(true);
+    // Build set of chains.
+    var chains = this.touchedChains(), self = this;
+
+    if (chains.length) {
+      this.addUndoLevel(this.updateTo(chains, "linkChain"));
+      this.redoHistory = [];
+      this.notifyEnvironment();
+    }
+  },
+
+  // [ end of public interface ]
+
+  // Update the document with a given set of chains, return its
+  // shadow. updateFunc should be "applyChain" or "linkChain". In the
+  // second case, the chains are taken to correspond the the current
+  // document, and only the state of the line data is updated. In the
+  // first case, the content of the chains is also pushed iinto the
+  // document.
+  updateTo: function(chains, updateFunc) {
+    var shadows = [], dirty = [];
+    for (var i = 0; i < chains.length; i++) {
+      shadows.push(this.shadowChain(chains[i]));
+      dirty.push(this[updateFunc](chains[i]));
+    }
+    if (updateFunc == "applyChain")
+      this.notifyDirty(dirty);
+    return shadows;
+  },
+
+  // Notify the editor that some nodes have changed.
+  notifyDirty: function(nodes) {
+    forEach(nodes, method(this.editor, "addDirtyNode"))
+    this.editor.scheduleHighlight();
+  },
+
+  notifyEnvironment: function() {
+    if (this.onChange) this.onChange();
+    // Used by the line-wrapping line-numbering code.
+    if (window.frameElement && window.frameElement.CodeMirror.updateNumbers)
+      window.frameElement.CodeMirror.updateNumbers();
+  },
+
+  // Link a chain into the DOM nodes (or the first/last links for null
+  // nodes).
+  linkChain: function(chain) {
+    for (var i = 0; i < chain.length; i++) {
+      var line = chain[i];
+      if (line.from) line.from.historyAfter = line;
+      else this.first = line;
+      if (line.to) line.to.historyBefore = line;
+      else this.last = line;
+    }
+  },
+
+  // Get the line object after/before a given node.
+  after: function(node) {
+    return node ? node.historyAfter : this.first;
+  },
+  before: function(node) {
+    return node ? node.historyBefore : this.last;
+  },
+
+  // Mark a node as touched if it has not already been marked.
+  setTouched: function(node) {
+    if (node) {
+      if (!node.historyTouched) {
+        this.touched.push(node);
+        node.historyTouched = true;
+      }
+    }
+    else {
+      this.firstTouched = true;
+    }
+  },
+
+  // Store a new set of undo info, throw away info if there is more of
+  // it than allowed.
+  addUndoLevel: function(diffs) {
+    this.history.push(diffs);
+    if (this.history.length > this.maxDepth)
+      this.history.shift();
+  },
+
+  // Build chains from a set of touched nodes.
+  touchedChains: function() {
+    var self = this;
+
+    // The temp system is a crummy hack to speed up determining
+    // whether a (currently touched) node has a line object associated
+    // with it. nullTemp is used to store the object for the first
+    // line, other nodes get it stored in their historyTemp property.
+    var nullTemp = null;
+    function temp(node) {return node ? node.historyTemp : nullTemp;}
+    function setTemp(node, line) {
+      if (node) node.historyTemp = line;
+      else nullTemp = line;
+    }
+
+    function buildLine(node) {
+      var text = [];
+      for (var cur = node ? node.nextSibling : self.container.firstChild;
+           cur && !isBR(cur); cur = cur.nextSibling)
+        if (cur.currentText) text.push(cur.currentText);
+      return {from: node, to: cur, text: cleanText(text.join(""))};
+    }
+
+    // Filter out unchanged lines and nodes that are no longer in the
+    // document. Build up line objects for remaining nodes.
+    var lines = [];
+    if (self.firstTouched) self.touched.push(null);
+    forEach(self.touched, function(node) {
+      if (node && node.parentNode != self.container) return;
+
+      if (node) node.historyTouched = false;
+      else self.firstTouched = false;
+
+      var line = buildLine(node), shadow = self.after(node);
+      if (!shadow || shadow.text != line.text || shadow.to != line.to) {
+        lines.push(line);
+        setTemp(node, line);
+      }
+    });
+
+    // Get the BR element after/before the given node.
+    function nextBR(node, dir) {
+      var link = dir + "Sibling", search = node[link];
+      while (search && !isBR(search))
+        search = search[link];
+      return search;
+    }
+
+    // Assemble line objects into chains by scanning the DOM tree
+    // around them.
+    var chains = []; self.touched = [];
+    forEach(lines, function(line) {
+      // Note that this makes the loop skip line objects that have
+      // been pulled into chains by lines before them.
+      if (!temp(line.from)) return;
+
+      var chain = [], curNode = line.from, safe = true;
+      // Put any line objects (referred to by temp info) before this
+      // one on the front of the array.
+      while (true) {
+        var curLine = temp(curNode);
+        if (!curLine) {
+          if (safe) break;
+          else curLine = buildLine(curNode);
+        }
+        chain.unshift(curLine);
+        setTemp(curNode, null);
+        if (!curNode) break;
+        safe = self.after(curNode);
+        curNode = nextBR(curNode, "previous");
+      }
+      curNode = line.to; safe = self.before(line.from);
+      // Add lines after this one at end of array.
+      while (true) {
+        if (!curNode) break;
+        var curLine = temp(curNode);
+        if (!curLine) {
+          if (safe) break;
+          else curLine = buildLine(curNode);
+        }
+        chain.push(curLine);
+        setTemp(curNode, null);
+        safe = self.before(curNode);
+        curNode = nextBR(curNode, "next");
+      }
+      chains.push(chain);
+    });
+
+    return chains;
+  },
+
+  // Find the 'shadow' of a given chain by following the links in the
+  // DOM nodes at its start and end.
+  shadowChain: function(chain) {
+    var shadows = [], next = this.after(chain[0].from), end = chain[chain.length - 1].to;
+    while (true) {
+      shadows.push(next);
+      var nextNode = next.to;
+      if (!nextNode || nextNode == end)
+        break;
+      else
+        next = nextNode.historyAfter || this.before(end);
+      // (The this.before(end) is a hack -- FF sometimes removes
+      // properties from BR nodes, in which case the best we can hope
+      // for is to not break.)
+    }
+    return shadows;
+  },
+
+  // Update the DOM tree to contain the lines specified in a given
+  // chain, link this chain into the DOM nodes.
+  applyChain: function(chain) {
+    // Some attempt is made to prevent the cursor from jumping
+    // randomly when an undo or redo happens. It still behaves a bit
+    // strange sometimes.
+    var cursor = select.cursorPos(this.container, false), self = this;
+
+    // Remove all nodes in the DOM tree between from and to (null for
+    // start/end of container).
+    function removeRange(from, to) {
+      var pos = from ? from.nextSibling : self.container.firstChild;
+      while (pos != to) {
+        var temp = pos.nextSibling;
+        removeElement(pos);
+        pos = temp;
+      }
+    }
+
+    var start = chain[0].from, end = chain[chain.length - 1].to;
+    // Clear the space where this change has to be made.
+    removeRange(start, end);
+
+    // Insert the content specified by the chain into the DOM tree.
+    for (var i = 0; i < chain.length; i++) {
+      var line = chain[i];
+      // The start and end of the space are already correct, but BR
+      // tags inside it have to be put back.
+      if (i > 0)
+        self.container.insertBefore(line.from, end);
+
+      // Add the text.
+      var node = makePartSpan(fixSpaces(line.text), this.container.ownerDocument);
+      self.container.insertBefore(node, end);
+      // See if the cursor was on this line. Put it back, adjusting
+      // for changed line length, if it was.
+      if (cursor && cursor.node == line.from) {
+        var cursordiff = 0;
+        var prev = this.after(line.from);
+        if (prev && i == chain.length - 1) {
+          // Only adjust if the cursor is after the unchanged part of
+          // the line.
+          for (var match = 0; match < cursor.offset &&
+               line.text.charAt(match) == prev.text.charAt(match); match++);
+          if (cursor.offset > match)
+            cursordiff = line.text.length - prev.text.length;
+        }
+        select.setCursorPos(this.container, {node: line.from, offset: Math.max(0, cursor.offset + cursordiff)});
+      }
+      // Cursor was in removed line, this is last new line.
+      else if (cursor && (i == chain.length - 1) && cursor.node && cursor.node.parentNode != this.container) {
+        select.setCursorPos(this.container, {node: line.from, offset: line.text.length});
+      }
+    }
+
+    // Anchor the chain in the DOM tree.
+    this.linkChain(chain);
+    return start;
+  }
+};
diff --git a/src/redakcja/static/js/lib/codemirror-0.8/util.js b/src/redakcja/static/js/lib/codemirror-0.8/util.js
new file mode 100644 (file)
index 0000000..c7021c2
--- /dev/null
@@ -0,0 +1,130 @@
+/* A few useful utility functions. */
+
+// Capture a method on an object.
+function method(obj, name) {
+  return function() {obj[name].apply(obj, arguments);};
+}
+
+// The value used to signal the end of a sequence in iterators.
+var StopIteration = {toString: function() {return "StopIteration"}};
+
+// Apply a function to each element in a sequence.
+function forEach(iter, f) {
+  if (iter.next) {
+    try {while (true) f(iter.next());}
+    catch (e) {if (e != StopIteration) throw e;}
+  }
+  else {
+    for (var i = 0; i < iter.length; i++)
+      f(iter[i]);
+  }
+}
+
+// Map a function over a sequence, producing an array of results.
+function map(iter, f) {
+  var accum = [];
+  forEach(iter, function(val) {accum.push(f(val));});
+  return accum;
+}
+
+// Create a predicate function that tests a string againsts a given
+// regular expression. No longer used but might be used by 3rd party
+// parsers.
+function matcher(regexp){
+  return function(value){return regexp.test(value);};
+}
+
+// Test whether a DOM node has a certain CSS class. Much faster than
+// the MochiKit equivalent, for some reason.
+function hasClass(element, className){
+  var classes = element.className;
+  return classes && new RegExp("(^| )" + className + "($| )").test(classes);
+}
+
+// Insert a DOM node after another node.
+function insertAfter(newNode, oldNode) {
+  var parent = oldNode.parentNode;
+  parent.insertBefore(newNode, oldNode.nextSibling);
+  return newNode;
+}
+
+function removeElement(node) {
+  if (node.parentNode)
+    node.parentNode.removeChild(node);
+}
+
+function clearElement(node) {
+  while (node.firstChild)
+    node.removeChild(node.firstChild);
+}
+
+// Check whether a node is contained in another one.
+function isAncestor(node, child) {
+  while (child = child.parentNode) {
+    if (node == child)
+      return true;
+  }
+  return false;
+}
+
+// The non-breaking space character.
+var nbsp = "\u00a0";
+var matching = {"{": "}", "[": "]", "(": ")",
+                "}": "{", "]": "[", ")": "("};
+
+// Standardize a few unportable event properties.
+function normalizeEvent(event) {
+  if (!event.stopPropagation) {
+    event.stopPropagation = function() {this.cancelBubble = true;};
+    event.preventDefault = function() {this.returnValue = false;};
+  }
+  if (!event.stop) {
+    event.stop = function() {
+      this.stopPropagation();
+      this.preventDefault();
+    };
+  }
+
+  if (event.type == "keypress") {
+    event.code = (event.charCode == null) ? event.keyCode : event.charCode;
+    event.character = String.fromCharCode(event.code);
+  }
+  return event;
+}
+
+// Portably register event handlers.
+function addEventHandler(node, type, handler, removeFunc) {
+  function wrapHandler(event) {
+    handler(normalizeEvent(event || window.event));
+  }
+  if (typeof node.addEventListener == "function") {
+    node.addEventListener(type, wrapHandler, false);
+    if (removeFunc) return function() {node.removeEventListener(type, wrapHandler, false);};
+  }
+  else {
+    node.attachEvent("on" + type, wrapHandler);
+    if (removeFunc) return function() {node.detachEvent("on" + type, wrapHandler);};
+  }
+}
+
+function nodeText(node) {
+  return node.textContent || node.innerText || node.nodeValue || "";
+}
+
+function nodeTop(node) {
+  var top = 0;
+  while (node.offsetParent) {
+    top += node.offsetTop;
+    node = node.offsetParent;
+  }
+  return top;
+}
+
+function isBR(node) {
+  var nn = node.nodeName;
+  return nn == "BR" || nn == "br";
+}
+function isSpan(node) {
+  var nn = node.nodeName;
+  return nn == "SPAN" || nn == "span";
+}
diff --git a/src/redakcja/static/js/lib/jquery-1.7.2.min.js b/src/redakcja/static/js/lib/jquery-1.7.2.min.js
new file mode 100644 (file)
index 0000000..16ad06c
--- /dev/null
@@ -0,0 +1,4 @@
+/*! jQuery v1.7.2 jquery.com | jquery.org/license */
+(function(a,b){function cy(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cu(a){if(!cj[a]){var b=c.body,d=f("<"+a+">").appendTo(b),e=d.css("display");d.remove();if(e==="none"||e===""){ck||(ck=c.createElement("iframe"),ck.frameBorder=ck.width=ck.height=0),b.appendChild(ck);if(!cl||!ck.createElement)cl=(ck.contentWindow||ck.contentDocument).document,cl.write((f.support.boxModel?"<!doctype html>":"")+"<html><body>"),cl.close();d=cl.createElement(a),cl.body.appendChild(d),e=f.css(d,"display"),b.removeChild(ck)}cj[a]=e}return cj[a]}function ct(a,b){var c={};f.each(cp.concat.apply([],cp.slice(0,b)),function(){c[this]=a});return c}function cs(){cq=b}function cr(){setTimeout(cs,0);return cq=f.now()}function ci(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ch(){try{return new a.XMLHttpRequest}catch(b){}}function cb(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g<i;g++){if(g===1)for(h in a.converters)typeof h=="string"&&(e[h.toLowerCase()]=a.converters[h]);l=k,k=d[g];if(k==="*")k=l;else if(l!=="*"&&l!==k){m=l+" "+k,n=e[m]||e["* "+k];if(!n){p=b;for(o in e){j=o.split(" ");if(j[0]===l||j[0]==="*"){p=e[j[1]+" "+k];if(p){o=e[o],o===!0?n=p:p===!0&&(n=o);break}}}}!n&&!p&&f.error("No conversion from "+m.replace(" "," to ")),n!==!0&&(c=n?n(c):p(o(c)))}}return c}function ca(a,c,d){var e=a.contents,f=a.dataTypes,g=a.responseFields,h,i,j,k;for(i in g)i in d&&(c[g[i]]=d[i]);while(f[0]==="*")f.shift(),h===b&&(h=a.mimeType||c.getResponseHeader("content-type"));if(h)for(i in e)if(e[i]&&e[i].test(h)){f.unshift(i);break}if(f[0]in d)j=f[0];else{for(i in d){if(!f[0]||a.converters[i+" "+f[0]]){j=i;break}k||(k=i)}j=j||k}if(j){j!==f[0]&&f.unshift(j);return d[j]}}function b_(a,b,c,d){if(f.isArray(b))f.each(b,function(b,e){c||bD.test(a)?d(a,e):b_(a+"["+(typeof e=="object"?b:"")+"]",e,c,d)});else if(!c&&f.type(b)==="object")for(var e in b)b_(a+"["+e+"]",b[e],c,d);else d(a,b)}function b$(a,c){var d,e,g=f.ajaxSettings.flatOptions||{};for(d in c)c[d]!==b&&((g[d]?a:e||(e={}))[d]=c[d]);e&&f.extend(!0,a,e)}function bZ(a,c,d,e,f,g){f=f||c.dataTypes[0],g=g||{},g[f]=!0;var h=a[f],i=0,j=h?h.length:0,k=a===bS,l;for(;i<j&&(k||!l);i++)l=h[i](c,d,e),typeof l=="string"&&(!k||g[l]?l=b:(c.dataTypes.unshift(l),l=bZ(a,c,d,e,l,g)));(k||!l)&&!g["*"]&&(l=bZ(a,c,d,e,"*",g));return l}function bY(a){return function(b,c){typeof b!="string"&&(c=b,b="*");if(f.isFunction(c)){var d=b.toLowerCase().split(bO),e=0,g=d.length,h,i,j;for(;e<g;e++)h=d[e],j=/^\+/.test(h),j&&(h=h.substr(1)||"*"),i=a[h]=a[h]||[],i[j?"unshift":"push"](c)}}}function bB(a,b,c){var d=b==="width"?a.offsetWidth:a.offsetHeight,e=b==="width"?1:0,g=4;if(d>0){if(c!=="border")for(;e<g;e+=2)c||(d-=parseFloat(f.css(a,"padding"+bx[e]))||0),c==="margin"?d+=parseFloat(f.css(a,c+bx[e]))||0:d-=parseFloat(f.css(a,"border"+bx[e]+"Width"))||0;return d+"px"}d=by(a,b);if(d<0||d==null)d=a.style[b];if(bt.test(d))return d;d=parseFloat(d)||0;if(c)for(;e<g;e+=2)d+=parseFloat(f.css(a,"padding"+bx[e]))||0,c!=="padding"&&(d+=parseFloat(f.css(a,"border"+bx[e]+"Width"))||0),c==="margin"&&(d+=parseFloat(f.css(a,c+bx[e]))||0);return d+"px"}function bo(a){var b=c.createElement("div");bh.appendChild(b),b.innerHTML=a.outerHTML;return b.firstChild}function bn(a){var b=(a.nodeName||"").toLowerCase();b==="input"?bm(a):b!=="script"&&typeof a.getElementsByTagName!="undefined"&&f.grep(a.getElementsByTagName("input"),bm)}function bm(a){if(a.type==="checkbox"||a.type==="radio")a.defaultChecked=a.checked}function bl(a){return typeof a.getElementsByTagName!="undefined"?a.getElementsByTagName("*"):typeof a.querySelectorAll!="undefined"?a.querySelectorAll("*"):[]}function bk(a,b){var c;b.nodeType===1&&(b.clearAttributes&&b.clearAttributes(),b.mergeAttributes&&b.mergeAttributes(a),c=b.nodeName.toLowerCase(),c==="object"?b.outerHTML=a.outerHTML:c!=="input"||a.type!=="checkbox"&&a.type!=="radio"?c==="option"?b.selected=a.defaultSelected:c==="input"||c==="textarea"?b.defaultValue=a.defaultValue:c==="script"&&b.text!==a.text&&(b.text=a.text):(a.checked&&(b.defaultChecked=b.checked=a.checked),b.value!==a.value&&(b.value=a.value)),b.removeAttribute(f.expando),b.removeAttribute("_submit_attached"),b.removeAttribute("_change_attached"))}function bj(a,b){if(b.nodeType===1&&!!f.hasData(a)){var c,d,e,g=f._data(a),h=f._data(b,g),i=g.events;if(i){delete h.handle,h.events={};for(c in i)for(d=0,e=i[c].length;d<e;d++)f.event.add(b,c,i[c][d])}h.data&&(h.data=f.extend({},h.data))}}function bi(a,b){return f.nodeName(a,"table")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function U(a){var b=V.split("|"),c=a.createDocumentFragment();if(c.createElement)while(b.length)c.createElement(b.pop());return c}function T(a,b,c){b=b||0;if(f.isFunction(b))return f.grep(a,function(a,d){var e=!!b.call(a,d,a);return e===c});if(b.nodeType)return f.grep(a,function(a,d){return a===b===c});if(typeof b=="string"){var d=f.grep(a,function(a){return a.nodeType===1});if(O.test(b))return f.filter(b,d,!c);b=f.filter(b,d)}return f.grep(a,function(a,d){return f.inArray(a,b)>=0===c})}function S(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function K(){return!0}function J(){return!1}function n(a,b,c){var d=b+"defer",e=b+"queue",g=b+"mark",h=f._data(a,d);h&&(c==="queue"||!f._data(a,e))&&(c==="mark"||!f._data(a,g))&&setTimeout(function(){!f._data(a,e)&&!f._data(a,g)&&(f.removeData(a,d,!0),h.fire())},0)}function m(a){for(var b in a){if(b==="data"&&f.isEmptyObject(a[b]))continue;if(b!=="toJSON")return!1}return!0}function l(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(k,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNumeric(d)?+d:j.test(d)?f.parseJSON(d):d}catch(g){}f.data(a,c,d)}else d=b}return d}function h(a){var b=g[a]={},c,d;a=a.split(/\s+/);for(c=0,d=a.length;c<d;c++)b[a[c]]=!0;return b}var c=a.document,d=a.navigator,e=a.location,f=function(){function J(){if(!e.isReady){try{c.documentElement.doScroll("left")}catch(a){setTimeout(J,1);return}e.ready()}}var e=function(a,b){return new e.fn.init(a,b,h)},f=a.jQuery,g=a.$,h,i=/^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,n=/^[\],:{}\s]*$/,o=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,p=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,q=/(?:^|:|,)(?:\s*\[)+/g,r=/(webkit)[ \/]([\w.]+)/,s=/(opera)(?:.*version)?[ \/]([\w.]+)/,t=/(msie) ([\w.]+)/,u=/(mozilla)(?:.*? rv:([\w.]+))?/,v=/-([a-z]|[0-9])/ig,w=/^-ms-/,x=function(a,b){return(b+"").toUpperCase()},y=d.userAgent,z,A,B,C=Object.prototype.toString,D=Object.prototype.hasOwnProperty,E=Array.prototype.push,F=Array.prototype.slice,G=String.prototype.trim,H=Array.prototype.indexOf,I={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=m.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.7.2",length:0,size:function(){return this.length},toArray:function(){return F.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?E.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),A.add(a);return this},eq:function(a){a=+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(F.apply(this,arguments),"slice",F.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:E,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j<k;j++)if((a=arguments[j])!=null)for(c in a){d=i[c],f=a[c];if(i===f)continue;l&&f&&(e.isPlainObject(f)||(g=e.isArray(f)))?(g?(g=!1,h=d&&e.isArray(d)?d:[]):h=d&&e.isPlainObject(d)?d:{},i[c]=e.extend(l,h,f)):f!==b&&(i[c]=f)}return i},e.extend({noConflict:function(b){a.$===e&&(a.$=g),b&&a.jQuery===e&&(a.jQuery=f);return e},isReady:!1,readyWait:1,holdReady:function(a){a?e.readyWait++:e.ready(!0)},ready:function(a){if(a===!0&&!--e.readyWait||a!==!0&&!e.isReady){if(!c.body)return setTimeout(e.ready,1);e.isReady=!0;if(a!==!0&&--e.readyWait>0)return;A.fireWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").off("ready")}},bindReady:function(){if(!A){A=e.Callbacks("once memory");if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",B,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",B),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&J()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a!=null&&a==a.window},isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)},type:function(a){return a==null?String(a):I[C.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;try{if(a.constructor&&!D.call(a,"constructor")&&!D.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||D.call(a,d)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw new Error(a)},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(n.test(b.replace(o,"@").replace(p,"]").replace(q,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(c){if(typeof c!="string"||!c)return null;var d,f;try{a.DOMParser?(f=new DOMParser,d=f.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(g){d=b}(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&e.error("Invalid XML: "+c);return d},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(w,"ms-").replace(v,x)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g<h;)if(c.apply(a[g++],d)===!1)break}else if(i){for(f in a)if(c.call(a[f],f,a[f])===!1)break}else for(;g<h;)if(c.call(a[g],g,a[g++])===!1)break;return a},trim:G?function(a){return a==null?"":G.call(a)}:function(a){return a==null?"":(a+"").replace(k,"").replace(l,"")},makeArray:function(a,b){var c=b||[];if(a!=null){var d=e.type(a);a.length==null||d==="string"||d==="function"||d==="regexp"||e.isWindow(a)?E.call(c,a):e.merge(c,a)}return c},inArray:function(a,b,c){var d;if(b){if(H)return H.call(b,a,c);d=b.length,c=c?c<0?Math.max(0,d+c):c:0;for(;c<d;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,c){var d=a.length,e=0;if(typeof c.length=="number")for(var f=c.length;e<f;e++)a[d++]=c[e];else while(c[e]!==b)a[d++]=c[e++];a.length=d;return a},grep:function(a,b,c){var d=[],e;c=!!c;for(var f=0,g=a.length;f<g;f++)e=!!b(a[f],f),c!==e&&d.push(a[f]);return d},map:function(a,c,d){var f,g,h=[],i=0,j=a.length,k=a instanceof e||j!==b&&typeof j=="number"&&(j>0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i<j;i++)f=c(a[i],i,d),f!=null&&(h[h.length]=f);else for(g in a)f=c(a[g],g,d),f!=null&&(h[h.length]=f);return h.concat.apply([],h)},guid:1,proxy:function(a,c){if(typeof c=="string"){var d=a[c];c=a,a=d}if(!e.isFunction(a))return b;var f=F.call(arguments,2),g=function(){return a.apply(c,f.concat(F.call(arguments)))};g.guid=a.guid=a.guid||g.guid||e.guid++;return g},access:function(a,c,d,f,g,h,i){var j,k=d==null,l=0,m=a.length;if(d&&typeof d=="object"){for(l in d)e.access(a,c,l,d[l],1,h,f);g=1}else if(f!==b){j=i===b&&e.isFunction(f),k&&(j?(j=c,c=function(a,b,c){return j.call(e(a),c)}):(c.call(a,f),c=null));if(c)for(;l<m;l++)c(a[l],d,j?f.call(a[l],l,c(a[l],d)):f,i);g=1}return g?a:k?c.call(a):m?c(a[0],d):h},now:function(){return(new Date).getTime()},uaMatch:function(a){a=a.toLowerCase();var b=r.exec(a)||s.exec(a)||t.exec(a)||a.indexOf("compatible")<0&&u.exec(a)||[];return{browser:b[1]||"",version:b[2]||"0"}},sub:function(){function a(b,c){return new a.fn.init(b,c)}e.extend(!0,a,this),a.superclass=this,a.fn=a.prototype=this(),a.fn.constructor=a,a.sub=this.sub,a.fn.init=function(d,f){f&&f instanceof e&&!(f instanceof a)&&(f=a(f));return e.fn.init.call(this,d,f,b)},a.fn.init.prototype=a.fn;var b=a(c);return a},browser:{}}),e.each("Boolean Number String Function Array Date RegExp Object".split(" "),function(a,b){I["[object "+b+"]"]=b.toLowerCase()}),z=e.uaMatch(y),z.browser&&(e.browser[z.browser]=!0,e.browser.version=z.version),e.browser.webkit&&(e.browser.safari=!0),j.test(" ")&&(k=/^[\s\xA0]+/,l=/[\s\xA0]+$/),h=e(c),c.addEventListener?B=function(){c.removeEventListener("DOMContentLoaded",B,!1),e.ready()}:c.attachEvent&&(B=function(){c.readyState==="complete"&&(c.detachEvent("onreadystatechange",B),e.ready())});return e}(),g={};f.Callbacks=function(a){a=a?g[a]||h(a):{};var c=[],d=[],e,i,j,k,l,m,n=function(b){var d,e,g,h,i;for(d=0,e=b.length;d<e;d++)g=b[d],h=f.type(g),h==="array"?n(g):h==="function"&&(!a.unique||!p.has(g))&&c.push(g)},o=function(b,f){f=f||[],e=!a.memory||[b,f],i=!0,j=!0,m=k||0,k=0,l=c.length;for(;c&&m<l;m++)if(c[m].apply(b,f)===!1&&a.stopOnFalse){e=!0;break}j=!1,c&&(a.once?e===!0?p.disable():c=[]:d&&d.length&&(e=d.shift(),p.fireWith(e[0],e[1])))},p={add:function(){if(c){var a=c.length;n(arguments),j?l=c.length:e&&e!==!0&&(k=a,o(e[0],e[1]))}return this},remove:function(){if(c){var b=arguments,d=0,e=b.length;for(;d<e;d++)for(var f=0;f<c.length;f++)if(b[d]===c[f]){j&&f<=l&&(l--,f<=m&&m--),c.splice(f--,1);if(a.unique)break}}return this},has:function(a){if(c){var b=0,d=c.length;for(;b<d;b++)if(a===c[b])return!0}return!1},empty:function(){c=[];return this},disable:function(){c=d=e=b;return this},disabled:function(){return!c},lock:function(){d=b,(!e||e===!0)&&p.disable();return this},locked:function(){return!d},fireWith:function(b,c){d&&(j?a.once||d.push([b,c]):(!a.once||!e)&&o(b,c));return this},fire:function(){p.fireWith(this,arguments);return this},fired:function(){return!!i}};return p};var i=[].slice;f.extend({Deferred:function(a){var b=f.Callbacks("once memory"),c=f.Callbacks("once memory"),d=f.Callbacks("memory"),e="pending",g={resolve:b,reject:c,notify:d},h={done:b.add,fail:c.add,progress:d.add,state:function(){return e},isResolved:b.fired,isRejected:c.fired,then:function(a,b,c){i.done(a).fail(b).progress(c);return this},always:function(){i.done.apply(i,arguments).fail.apply(i,arguments);return this},pipe:function(a,b,c){return f.Deferred(function(d){f.each({done:[a,"resolve"],fail:[b,"reject"],progress:[c,"notify"]},function(a,b){var c=b[0],e=b[1],g;f.isFunction(c)?i[a](function(){g=c.apply(this,arguments),g&&f.isFunction(g.promise)?g.promise().then(d.resolve,d.reject,d.notify):d[e+"With"](this===i?d:this,[g])}):i[a](d[e])})}).promise()},promise:function(a){if(a==null)a=h;else for(var b in h)a[b]=h[b];return a}},i=h.promise({}),j;for(j in g)i[j]=g[j].fire,i[j+"With"]=g[j].fireWith;i.done(function(){e="resolved"},c.disable,d.lock).fail(function(){e="rejected"},b.disable,d.lock),a&&a.call(i,i);return i},when:function(a){function m(a){return function(b){e[a]=arguments.length>1?i.call(arguments,0):b,j.notifyWith(k,e)}}function l(a){return function(c){b[a]=arguments.length>1?i.call(arguments,0):c,--g||j.resolveWith(j,b)}}var b=i.call(arguments,0),c=0,d=b.length,e=Array(d),g=d,h=d,j=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred(),k=j.promise();if(d>1){for(;c<d;c++)b[c]&&b[c].promise&&f.isFunction(b[c].promise)?b[c].promise().then(l(c),j.reject,m(c)):--g;g||j.resolveWith(j,b)}else j!==a&&j.resolveWith(j,d?[a]:[]);return k}}),f.support=function(){var b,d,e,g,h,i,j,k,l,m,n,o,p=c.createElement("div"),q=c.documentElement;p.setAttribute("className","t"),p.innerHTML="   <link/><table></table><a href='/a' style='top:1px;float:left;opacity:.55;'>a</a><input type='checkbox'/>",d=p.getElementsByTagName("*"),e=p.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=p.getElementsByTagName("input")[0],b={leadingWhitespace:p.firstChild.nodeType===3,tbody:!p.getElementsByTagName("tbody").length,htmlSerialize:!!p.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,getSetAttribute:p.className!=="t",enctype:!!c.createElement("form").enctype,html5Clone:c.createElement("nav").cloneNode(!0).outerHTML!=="<:nav></:nav>",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,pixelMargin:!0},f.boxModel=b.boxModel=c.compatMode==="CSS1Compat",i.checked=!0,b.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,b.optDisabled=!h.disabled;try{delete p.test}catch(r){b.deleteExpando=!1}!p.addEventListener&&p.attachEvent&&p.fireEvent&&(p.attachEvent("onclick",function(){b.noCloneEvent=!1}),p.cloneNode(!0).fireEvent("onclick")),i=c.createElement("input"),i.value="t",i.setAttribute("type","radio"),b.radioValue=i.value==="t",i.setAttribute("checked","checked"),i.setAttribute("name","t"),p.appendChild(i),j=c.createDocumentFragment(),j.appendChild(p.lastChild),b.checkClone=j.cloneNode(!0).cloneNode(!0).lastChild.checked,b.appendChecked=i.checked,j.removeChild(i),j.appendChild(p);if(p.attachEvent)for(n in{submit:1,change:1,focusin:1})m="on"+n,o=m in p,o||(p.setAttribute(m,"return;"),o=typeof p[m]=="function"),b[n+"Bubbles"]=o;j.removeChild(p),j=g=h=p=i=null,f(function(){var d,e,g,h,i,j,l,m,n,q,r,s,t,u=c.getElementsByTagName("body")[0];!u||(m=1,t="padding:0;margin:0;border:",r="position:absolute;top:0;left:0;width:1px;height:1px;",s=t+"0;visibility:hidden;",n="style='"+r+t+"5px solid #000;",q="<div "+n+"display:block;'><div style='"+t+"0;display:block;overflow:hidden;'></div></div>"+"<table "+n+"' cellpadding='0' cellspacing='0'>"+"<tr><td></td></tr></table>",d=c.createElement("div"),d.style.cssText=s+"width:0;height:0;position:static;top:0;margin-top:"+m+"px",u.insertBefore(d,u.firstChild),p=c.createElement("div"),d.appendChild(p),p.innerHTML="<table><tr><td style='"+t+"0;display:none'></td><td>t</td></tr></table>",k=p.getElementsByTagName("td"),o=k[0].offsetHeight===0,k[0].style.display="",k[1].style.display="none",b.reliableHiddenOffsets=o&&k[0].offsetHeight===0,a.getComputedStyle&&(p.innerHTML="",l=c.createElement("div"),l.style.width="0",l.style.marginRight="0",p.style.width="2px",p.appendChild(l),b.reliableMarginRight=(parseInt((a.getComputedStyle(l,null)||{marginRight:0}).marginRight,10)||0)===0),typeof p.style.zoom!="undefined"&&(p.innerHTML="",p.style.width=p.style.padding="1px",p.style.border=0,p.style.overflow="hidden",p.style.display="inline",p.style.zoom=1,b.inlineBlockNeedsLayout=p.offsetWidth===3,p.style.display="block",p.style.overflow="visible",p.innerHTML="<div style='width:5px;'></div>",b.shrinkWrapBlocks=p.offsetWidth!==3),p.style.cssText=r+s,p.innerHTML=q,e=p.firstChild,g=e.firstChild,i=e.nextSibling.firstChild.firstChild,j={doesNotAddBorder:g.offsetTop!==5,doesAddBorderForTableAndCells:i.offsetTop===5},g.style.position="fixed",g.style.top="20px",j.fixedPosition=g.offsetTop===20||g.offsetTop===15,g.style.position=g.style.top="",e.style.overflow="hidden",e.style.position="relative",j.subtractsBorderForOverflowNotVisible=g.offsetTop===-5,j.doesNotIncludeMarginInBodyOffset=u.offsetTop!==m,a.getComputedStyle&&(p.style.marginTop="1%",b.pixelMargin=(a.getComputedStyle(p,null)||{marginTop:0}).marginTop!=="1%"),typeof d.style.zoom!="undefined"&&(d.style.zoom=1),u.removeChild(d),l=p=d=null,f.extend(b,j))});return b}();var j=/^(?:\{.*\}|\[.*\])$/,k=/([A-Z])/g;f.extend({cache:{},uuid:0,expando:"jQuery"+(f.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?f.cache[a[f.expando]]:a[f.expando];return!!a&&!m(a)},data:function(a,c,d,e){if(!!f.acceptData(a)){var g,h,i,j=f.expando,k=typeof c=="string",l=a.nodeType,m=l?f.cache:a,n=l?a[j]:a[j]&&j,o=c==="events";if((!n||!m[n]||!o&&!e&&!m[n].data)&&k&&d===b)return;n||(l?a[j]=n=++f.uuid:n=j),m[n]||(m[n]={},l||(m[n].toJSON=f.noop));if(typeof c=="object"||typeof c=="function")e?m[n]=f.extend(m[n],c):m[n].data=f.extend(m[n].data,c);g=h=m[n],e||(h.data||(h.data={}),h=h.data),d!==b&&(h[f.camelCase(c)]=d);if(o&&!h[c])return g.events;k?(i=h[c],i==null&&(i=h[f.camelCase(c)])):i=h;return i}},removeData:function(a,b,c){if(!!f.acceptData(a)){var d,e,g,h=f.expando,i=a.nodeType,j=i?f.cache:a,k=i?a[h]:h;if(!j[k])return;if(b){d=c?j[k]:j[k].data;if(d){f.isArray(b)||(b in d?b=[b]:(b=f.camelCase(b),b in d?b=[b]:b=b.split(" ")));for(e=0,g=b.length;e<g;e++)delete d[b[e]];if(!(c?m:f.isEmptyObject)(d))return}}if(!c){delete j[k].data;if(!m(j[k]))return}f.support.deleteExpando||!j.setInterval?delete j[k]:j[k]=null,i&&(f.support.deleteExpando?delete a[h]:a.removeAttribute?a.removeAttribute(h):a[h]=null)}},_data:function(a,b,c){return f.data(a,b,c,!0)},acceptData:function(a){if(a.nodeName){var b=f.noData[a.nodeName.toLowerCase()];if(b)return b!==!0&&a.getAttribute("classid")===b}return!0}}),f.fn.extend({data:function(a,c){var d,e,g,h,i,j=this[0],k=0,m=null;if(a===b){if(this.length){m=f.data(j);if(j.nodeType===1&&!f._data(j,"parsedAttrs")){g=j.attributes;for(i=g.length;k<i;k++)h=g[k].name,h.indexOf("data-")===0&&(h=f.camelCase(h.substring(5)),l(j,h,m[h]));f._data(j,"parsedAttrs",!0)}}return m}if(typeof a=="object")return this.each(function(){f.data(this,a)});d=a.split(".",2),d[1]=d[1]?"."+d[1]:"",e=d[1]+"!";return f.access(this,function(c){if(c===b){m=this.triggerHandler("getData"+e,[d[0]]),m===b&&j&&(m=f.data(j,a),m=l(j,a,m));return m===b&&d[1]?this.data(d[0]):m}d[1]=c,this.each(function(){var b=f(this);b.triggerHandler("setData"+e,d),f.data(this,a,c),b.triggerHandler("changeData"+e,d)})},null,c,arguments.length>1,null,!1)},removeData:function(a){return this.each(function(){f.removeData(this,a)})}}),f.extend({_mark:function(a,b){a&&(b=(b||"fx")+"mark",f._data(a,b,(f._data(a,b)||0)+1))},_unmark:function(a,b,c){a!==!0&&(c=b,b=a,a=!1);if(b){c=c||"fx";var d=c+"mark",e=a?0:(f._data(b,d)||1)-1;e?f._data(b,d,e):(f.removeData(b,d,!0),n(b,c,"mark"))}},queue:function(a,b,c){var d;if(a){b=(b||"fx")+"queue",d=f._data(a,b),c&&(!d||f.isArray(c)?d=f._data(a,b,f.makeArray(c)):d.push(c));return d||[]}},dequeue:function(a,b){b=b||"fx";var c=f.queue(a,b),d=c.shift(),e={};d==="inprogress"&&(d=c.shift()),d&&(b==="fx"&&c.unshift("inprogress"),f._data(a,b+".run",e),d.call(a,function(){f.dequeue(a,b)},e)),c.length||(f.removeData(a,b+"queue "+b+".run",!0),n(a,b,"queue"))}}),f.fn.extend({queue:function(a,c){var d=2;typeof a!="string"&&(c=a,a="fx",d--);if(arguments.length<d)return f.queue(this[0],a);return c===b?this:this.each(function(){var b=f.queue(this,a,c);a==="fx"&&b[0]!=="inprogress"&&f.dequeue(this,a)})},dequeue:function(a){return this.each(function(){f.dequeue(this,a)})},delay:function(a,b){a=f.fx?f.fx.speeds[a]||a:a,b=b||"fx";return this.queue(b,function(b,c){var d=setTimeout(b,a);c.stop=function(){clearTimeout(d)}})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,c){function m(){--h||d.resolveWith(e,[e])}typeof a!="string"&&(c=a,a=b),a=a||"fx";var d=f.Deferred(),e=this,g=e.length,h=1,i=a+"defer",j=a+"queue",k=a+"mark",l;while(g--)if(l=f.data(e[g],i,b,!0)||(f.data(e[g],j,b,!0)||f.data(e[g],k,b,!0))&&f.data(e[g],i,f.Callbacks("once memory"),!0))h++,l.add(m);m();return d.promise(c)}});var o=/[\n\t\r]/g,p=/\s+/,q=/\r/g,r=/^(?:button|input)$/i,s=/^(?:button|input|object|select|textarea)$/i,t=/^a(?:rea)?$/i,u=/^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,v=f.support.getSetAttribute,w,x,y;f.fn.extend({attr:function(a,b){return f.access(this,f.attr,a,b,arguments.length>1)},removeAttr:function(a){return this.each(function(){f.removeAttr(this,a)})},prop:function(a,b){return f.access(this,f.prop,a,b,arguments.length>1)},removeProp:function(a){a=f.propFix[a]||a;return this.each(function(){try{this[a]=b,delete this[a]}catch(c){}})},addClass:function(a){var b,c,d,e,g,h,i;if(f.isFunction(a))return this.each(function(b){f(this).addClass(a.call(this,b,this.className))});if(a&&typeof a=="string"){b=a.split(p);for(c=0,d=this.length;c<d;c++){e=this[c];if(e.nodeType===1)if(!e.className&&b.length===1)e.className=a;else{g=" "+e.className+" ";for(h=0,i=b.length;h<i;h++)~g.indexOf(" "+b[h]+" ")||(g+=b[h]+" ");e.className=f.trim(g)}}}return this},removeClass:function(a){var c,d,e,g,h,i,j;if(f.isFunction(a))return this.each(function(b){f(this).removeClass(a.call(this,b,this.className))});if(a&&typeof a=="string"||a===b){c=(a||"").split(p);for(d=0,e=this.length;d<e;d++){g=this[d];if(g.nodeType===1&&g.className)if(a){h=(" "+g.className+" ").replace(o," ");for(i=0,j=c.length;i<j;i++)h=h.replace(" "+c[i]+" "," ");g.className=f.trim(h)}else g.className=""}}return this},toggleClass:function(a,b){var c=typeof a,d=typeof b=="boolean";if(f.isFunction(a))return this.each(function(c){f(this).toggleClass(a.call(this,c,this.className,b),b)});return this.each(function(){if(c==="string"){var e,g=0,h=f(this),i=b,j=a.split(p);while(e=j[g++])i=d?i:!h.hasClass(e),h[i?"addClass":"removeClass"](e)}else if(c==="undefined"||c==="boolean")this.className&&f._data(this,"__className__",this.className),this.className=this.className||a===!1?"":f._data(this,"__className__")||""})},hasClass:function(a){var b=" "+a+" ",c=0,d=this.length;for(;c<d;c++)if(this[c].nodeType===1&&(" "+this[c].className+" ").replace(o," ").indexOf(b)>-1)return!0;return!1},val:function(a){var c,d,e,g=this[0];{if(!!arguments.length){e=f.isFunction(a);return this.each(function(d){var g=f(this),h;if(this.nodeType===1){e?h=a.call(this,d,g.val()):h=a,h==null?h="":typeof h=="number"?h+="":f.isArray(h)&&(h=f.map(h,function(a){return a==null?"":a+""})),c=f.valHooks[this.type]||f.valHooks[this.nodeName.toLowerCase()];if(!c||!("set"in c)||c.set(this,h,"value")===b)this.value=h}})}if(g){c=f.valHooks[g.type]||f.valHooks[g.nodeName.toLowerCase()];if(c&&"get"in c&&(d=c.get(g,"value"))!==b)return d;d=g.value;return typeof d=="string"?d.replace(q,""):d==null?"":d}}}}),f.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c,d,e,g=a.selectedIndex,h=[],i=a.options,j=a.type==="select-one";if(g<0)return null;c=j?g:0,d=j?g+1:i.length;for(;c<d;c++){e=i[c];if(e.selected&&(f.support.optDisabled?!e.disabled:e.getAttribute("disabled")===null)&&(!e.parentNode.disabled||!f.nodeName(e.parentNode,"optgroup"))){b=f(e).val();if(j)return b;h.push(b)}}if(j&&!h.length&&i.length)return f(i[g]).val();return h},set:function(a,b){var c=f.makeArray(b);f(a).find("option").each(function(){this.selected=f.inArray(f(this).val(),c)>=0}),c.length||(a.selectedIndex=-1);return c}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attr:function(a,c,d,e){var g,h,i,j=a.nodeType;if(!!a&&j!==3&&j!==8&&j!==2){if(e&&c in f.attrFn)return f(a)[c](d);if(typeof a.getAttribute=="undefined")return f.prop(a,c,d);i=j!==1||!f.isXMLDoc(a),i&&(c=c.toLowerCase(),h=f.attrHooks[c]||(u.test(c)?x:w));if(d!==b){if(d===null){f.removeAttr(a,c);return}if(h&&"set"in h&&i&&(g=h.set(a,d,c))!==b)return g;a.setAttribute(c,""+d);return d}if(h&&"get"in h&&i&&(g=h.get(a,c))!==null)return g;g=a.getAttribute(c);return g===null?b:g}},removeAttr:function(a,b){var c,d,e,g,h,i=0;if(b&&a.nodeType===1){d=b.toLowerCase().split(p),g=d.length;for(;i<g;i++)e=d[i],e&&(c=f.propFix[e]||e,h=u.test(e),h||f.attr(a,e,""),a.removeAttribute(v?e:c),h&&c in a&&(a[c]=!1))}},attrHooks:{type:{set:function(a,b){if(r.test(a.nodeName)&&a.parentNode)f.error("type property can't be changed");else if(!f.support.radioValue&&b==="radio"&&f.nodeName(a,"input")){var c=a.value;a.setAttribute("type",b),c&&(a.value=c);return b}}},value:{get:function(a,b){if(w&&f.nodeName(a,"button"))return w.get(a,b);return b in a?a.value:null},set:function(a,b,c){if(w&&f.nodeName(a,"button"))return w.set(a,b,c);a.value=b}}},propFix:{tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},prop:function(a,c,d){var e,g,h,i=a.nodeType;if(!!a&&i!==3&&i!==8&&i!==2){h=i!==1||!f.isXMLDoc(a),h&&(c=f.propFix[c]||c,g=f.propHooks[c]);return d!==b?g&&"set"in g&&(e=g.set(a,d,c))!==b?e:a[c]=d:g&&"get"in g&&(e=g.get(a,c))!==null?e:a[c]}},propHooks:{tabIndex:{get:function(a){var c=a.getAttributeNode("tabindex");return c&&c.specified?parseInt(c.value,10):s.test(a.nodeName)||t.test(a.nodeName)&&a.href?0:b}}}}),f.attrHooks.tabindex=f.propHooks.tabIndex,x={get:function(a,c){var d,e=f.prop(a,c);return e===!0||typeof e!="boolean"&&(d=a.getAttributeNode(c))&&d.nodeValue!==!1?c.toLowerCase():b},set:function(a,b,c){var d;b===!1?f.removeAttr(a,c):(d=f.propFix[c]||c,d in a&&(a[d]=!0),a.setAttribute(c,c.toLowerCase()));return c}},v||(y={name:!0,id:!0,coords:!0},w=f.valHooks.button={get:function(a,c){var d;d=a.getAttributeNode(c);return d&&(y[c]?d.nodeValue!=="":d.specified)?d.nodeValue:b},set:function(a,b,d){var e=a.getAttributeNode(d);e||(e=c.createAttribute(d),a.setAttributeNode(e));return e.nodeValue=b+""}},f.attrHooks.tabindex.set=w.set,f.each(["width","height"],function(a,b){f.attrHooks[b]=f.extend(f.attrHooks[b],{set:function(a,c){if(c===""){a.setAttribute(b,"auto");return c}}})}),f.attrHooks.contenteditable={get:w.get,set:function(a,b,c){b===""&&(b="false"),w.set(a,b,c)}}),f.support.hrefNormalized||f.each(["href","src","width","height"],function(a,c){f.attrHooks[c]=f.extend(f.attrHooks[c],{get:function(a){var d=a.getAttribute(c,2);return d===null?b:d}})}),f.support.style||(f.attrHooks.style={get:function(a){return a.style.cssText.toLowerCase()||b},set:function(a,b){return a.style.cssText=""+b}}),f.support.optSelected||(f.propHooks.selected=f.extend(f.propHooks.selected,{get:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex);return null}})),f.support.enctype||(f.propFix.enctype="encoding"),f.support.checkOn||f.each(["radio","checkbox"],function(){f.valHooks[this]={get:function(a){return a.getAttribute("value")===null?"on":a.value}}}),f.each(["radio","checkbox"],function(){f.valHooks[this]=f.extend(f.valHooks[this],{set:function(a,b){if(f.isArray(b))return a.checked=f.inArray(f(a).val(),b)>=0}})});var z=/^(?:textarea|input|select)$/i,A=/^([^\.]*)?(?:\.(.+))?$/,B=/(?:^|\s)hover(\.\S+)?\b/,C=/^key/,D=/^(?:mouse|contextmenu)|click/,E=/^(?:focusinfocus|focusoutblur)$/,F=/^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/,G=function(
+a){var b=F.exec(a);b&&(b[1]=(b[1]||"").toLowerCase(),b[3]=b[3]&&new RegExp("(?:^|\\s)"+b[3]+"(?:\\s|$)"));return b},H=function(a,b){var c=a.attributes||{};return(!b[1]||a.nodeName.toLowerCase()===b[1])&&(!b[2]||(c.id||{}).value===b[2])&&(!b[3]||b[3].test((c["class"]||{}).value))},I=function(a){return f.event.special.hover?a:a.replace(B,"mouseenter$1 mouseleave$1")};f.event={add:function(a,c,d,e,g){var h,i,j,k,l,m,n,o,p,q,r,s;if(!(a.nodeType===3||a.nodeType===8||!c||!d||!(h=f._data(a)))){d.handler&&(p=d,d=p.handler,g=p.selector),d.guid||(d.guid=f.guid++),j=h.events,j||(h.events=j={}),i=h.handle,i||(h.handle=i=function(a){return typeof f!="undefined"&&(!a||f.event.triggered!==a.type)?f.event.dispatch.apply(i.elem,arguments):b},i.elem=a),c=f.trim(I(c)).split(" ");for(k=0;k<c.length;k++){l=A.exec(c[k])||[],m=l[1],n=(l[2]||"").split(".").sort(),s=f.event.special[m]||{},m=(g?s.delegateType:s.bindType)||m,s=f.event.special[m]||{},o=f.extend({type:m,origType:l[1],data:e,handler:d,guid:d.guid,selector:g,quick:g&&G(g),namespace:n.join(".")},p),r=j[m];if(!r){r=j[m]=[],r.delegateCount=0;if(!s.setup||s.setup.call(a,e,n,i)===!1)a.addEventListener?a.addEventListener(m,i,!1):a.attachEvent&&a.attachEvent("on"+m,i)}s.add&&(s.add.call(a,o),o.handler.guid||(o.handler.guid=d.guid)),g?r.splice(r.delegateCount++,0,o):r.push(o),f.event.global[m]=!0}a=null}},global:{},remove:function(a,b,c,d,e){var g=f.hasData(a)&&f._data(a),h,i,j,k,l,m,n,o,p,q,r,s;if(!!g&&!!(o=g.events)){b=f.trim(I(b||"")).split(" ");for(h=0;h<b.length;h++){i=A.exec(b[h])||[],j=k=i[1],l=i[2];if(!j){for(j in o)f.event.remove(a,j+b[h],c,d,!0);continue}p=f.event.special[j]||{},j=(d?p.delegateType:p.bindType)||j,r=o[j]||[],m=r.length,l=l?new RegExp("(^|\\.)"+l.split(".").sort().join("\\.(?:.*\\.)?")+"(\\.|$)"):null;for(n=0;n<r.length;n++)s=r[n],(e||k===s.origType)&&(!c||c.guid===s.guid)&&(!l||l.test(s.namespace))&&(!d||d===s.selector||d==="**"&&s.selector)&&(r.splice(n--,1),s.selector&&r.delegateCount--,p.remove&&p.remove.call(a,s));r.length===0&&m!==r.length&&((!p.teardown||p.teardown.call(a,l)===!1)&&f.removeEvent(a,j,g.handle),delete o[j])}f.isEmptyObject(o)&&(q=g.handle,q&&(q.elem=null),f.removeData(a,["events","handle"],!0))}},customEvent:{getData:!0,setData:!0,changeData:!0},trigger:function(c,d,e,g){if(!e||e.nodeType!==3&&e.nodeType!==8){var h=c.type||c,i=[],j,k,l,m,n,o,p,q,r,s;if(E.test(h+f.event.triggered))return;h.indexOf("!")>=0&&(h=h.slice(0,-1),k=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i.shift(),i.sort());if((!e||f.event.customEvent[h])&&!f.event.global[h])return;c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.isTrigger=!0,c.exclusive=k,c.namespace=i.join("."),c.namespace_re=c.namespace?new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)"):null,o=h.indexOf(":")<0?"on"+h:"";if(!e){j=f.cache;for(l in j)j[l].events&&j[l].events[h]&&f.event.trigger(c,d,j[l].handle.elem,!0);return}c.result=b,c.target||(c.target=e),d=d!=null?f.makeArray(d):[],d.unshift(c),p=f.event.special[h]||{};if(p.trigger&&p.trigger.apply(e,d)===!1)return;r=[[e,p.bindType||h]];if(!g&&!p.noBubble&&!f.isWindow(e)){s=p.delegateType||h,m=E.test(s+h)?e:e.parentNode,n=null;for(;m;m=m.parentNode)r.push([m,s]),n=m;n&&n===e.ownerDocument&&r.push([n.defaultView||n.parentWindow||a,s])}for(l=0;l<r.length&&!c.isPropagationStopped();l++)m=r[l][0],c.type=r[l][1],q=(f._data(m,"events")||{})[c.type]&&f._data(m,"handle"),q&&q.apply(m,d),q=o&&m[o],q&&f.acceptData(m)&&q.apply(m,d)===!1&&c.preventDefault();c.type=h,!g&&!c.isDefaultPrevented()&&(!p._default||p._default.apply(e.ownerDocument,d)===!1)&&(h!=="click"||!f.nodeName(e,"a"))&&f.acceptData(e)&&o&&e[h]&&(h!=="focus"&&h!=="blur"||c.target.offsetWidth!==0)&&!f.isWindow(e)&&(n=e[o],n&&(e[o]=null),f.event.triggered=h,e[h](),f.event.triggered=b,n&&(e[o]=n));return c.result}},dispatch:function(c){c=f.event.fix(c||a.event);var d=(f._data(this,"events")||{})[c.type]||[],e=d.delegateCount,g=[].slice.call(arguments,0),h=!c.exclusive&&!c.namespace,i=f.event.special[c.type]||{},j=[],k,l,m,n,o,p,q,r,s,t,u;g[0]=c,c.delegateTarget=this;if(!i.preDispatch||i.preDispatch.call(this,c)!==!1){if(e&&(!c.button||c.type!=="click")){n=f(this),n.context=this.ownerDocument||this;for(m=c.target;m!=this;m=m.parentNode||this)if(m.disabled!==!0){p={},r=[],n[0]=m;for(k=0;k<e;k++)s=d[k],t=s.selector,p[t]===b&&(p[t]=s.quick?H(m,s.quick):n.is(t)),p[t]&&r.push(s);r.length&&j.push({elem:m,matches:r})}}d.length>e&&j.push({elem:this,matches:d.slice(e)});for(k=0;k<j.length&&!c.isPropagationStopped();k++){q=j[k],c.currentTarget=q.elem;for(l=0;l<q.matches.length&&!c.isImmediatePropagationStopped();l++){s=q.matches[l];if(h||!c.namespace&&!s.namespace||c.namespace_re&&c.namespace_re.test(s.namespace))c.data=s.data,c.handleObj=s,o=((f.event.special[s.origType]||{}).handle||s.handler).apply(q.elem,g),o!==b&&(c.result=o,o===!1&&(c.preventDefault(),c.stopPropagation()))}}i.postDispatch&&i.postDispatch.call(this,c);return c.result}},props:"attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(a,b){a.which==null&&(a.which=b.charCode!=null?b.charCode:b.keyCode);return a}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(a,d){var e,f,g,h=d.button,i=d.fromElement;a.pageX==null&&d.clientX!=null&&(e=a.target.ownerDocument||c,f=e.documentElement,g=e.body,a.pageX=d.clientX+(f&&f.scrollLeft||g&&g.scrollLeft||0)-(f&&f.clientLeft||g&&g.clientLeft||0),a.pageY=d.clientY+(f&&f.scrollTop||g&&g.scrollTop||0)-(f&&f.clientTop||g&&g.clientTop||0)),!a.relatedTarget&&i&&(a.relatedTarget=i===a.target?d.toElement:i),!a.which&&h!==b&&(a.which=h&1?1:h&2?3:h&4?2:0);return a}},fix:function(a){if(a[f.expando])return a;var d,e,g=a,h=f.event.fixHooks[a.type]||{},i=h.props?this.props.concat(h.props):this.props;a=f.Event(g);for(d=i.length;d;)e=i[--d],a[e]=g[e];a.target||(a.target=g.srcElement||c),a.target.nodeType===3&&(a.target=a.target.parentNode),a.metaKey===b&&(a.metaKey=a.ctrlKey);return h.filter?h.filter(a,g):a},special:{ready:{setup:f.bindReady},load:{noBubble:!0},focus:{delegateType:"focusin"},blur:{delegateType:"focusout"},beforeunload:{setup:function(a,b,c){f.isWindow(this)&&(this.onbeforeunload=c)},teardown:function(a,b){this.onbeforeunload===b&&(this.onbeforeunload=null)}}},simulate:function(a,b,c,d){var e=f.extend(new f.Event,c,{type:a,isSimulated:!0,originalEvent:{}});d?f.event.trigger(e,null,b):f.event.dispatch.call(b,e),e.isDefaultPrevented()&&c.preventDefault()}},f.event.handle=f.event.dispatch,f.removeEvent=c.removeEventListener?function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c,!1)}:function(a,b,c){a.detachEvent&&a.detachEvent("on"+b,c)},f.Event=function(a,b){if(!(this instanceof f.Event))return new f.Event(a,b);a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||a.returnValue===!1||a.getPreventDefault&&a.getPreventDefault()?K:J):this.type=a,b&&f.extend(this,b),this.timeStamp=a&&a.timeStamp||f.now(),this[f.expando]=!0},f.Event.prototype={preventDefault:function(){this.isDefaultPrevented=K;var a=this.originalEvent;!a||(a.preventDefault?a.preventDefault():a.returnValue=!1)},stopPropagation:function(){this.isPropagationStopped=K;var a=this.originalEvent;!a||(a.stopPropagation&&a.stopPropagation(),a.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=K,this.stopPropagation()},isDefaultPrevented:J,isPropagationStopped:J,isImmediatePropagationStopped:J},f.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){f.event.special[a]={delegateType:b,bindType:b,handle:function(a){var c=this,d=a.relatedTarget,e=a.handleObj,g=e.selector,h;if(!d||d!==c&&!f.contains(c,d))a.type=e.origType,h=e.handler.apply(this,arguments),a.type=b;return h}}}),f.support.submitBubbles||(f.event.special.submit={setup:function(){if(f.nodeName(this,"form"))return!1;f.event.add(this,"click._submit keypress._submit",function(a){var c=a.target,d=f.nodeName(c,"input")||f.nodeName(c,"button")?c.form:b;d&&!d._submit_attached&&(f.event.add(d,"submit._submit",function(a){a._submit_bubble=!0}),d._submit_attached=!0)})},postDispatch:function(a){a._submit_bubble&&(delete a._submit_bubble,this.parentNode&&!a.isTrigger&&f.event.simulate("submit",this.parentNode,a,!0))},teardown:function(){if(f.nodeName(this,"form"))return!1;f.event.remove(this,"._submit")}}),f.support.changeBubbles||(f.event.special.change={setup:function(){if(z.test(this.nodeName)){if(this.type==="checkbox"||this.type==="radio")f.event.add(this,"propertychange._change",function(a){a.originalEvent.propertyName==="checked"&&(this._just_changed=!0)}),f.event.add(this,"click._change",function(a){this._just_changed&&!a.isTrigger&&(this._just_changed=!1,f.event.simulate("change",this,a,!0))});return!1}f.event.add(this,"beforeactivate._change",function(a){var b=a.target;z.test(b.nodeName)&&!b._change_attached&&(f.event.add(b,"change._change",function(a){this.parentNode&&!a.isSimulated&&!a.isTrigger&&f.event.simulate("change",this.parentNode,a,!0)}),b._change_attached=!0)})},handle:function(a){var b=a.target;if(this!==b||a.isSimulated||a.isTrigger||b.type!=="radio"&&b.type!=="checkbox")return a.handleObj.handler.apply(this,arguments)},teardown:function(){f.event.remove(this,"._change");return z.test(this.nodeName)}}),f.support.focusinBubbles||f.each({focus:"focusin",blur:"focusout"},function(a,b){var d=0,e=function(a){f.event.simulate(b,a.target,f.event.fix(a),!0)};f.event.special[b]={setup:function(){d++===0&&c.addEventListener(a,e,!0)},teardown:function(){--d===0&&c.removeEventListener(a,e,!0)}}}),f.fn.extend({on:function(a,c,d,e,g){var h,i;if(typeof a=="object"){typeof c!="string"&&(d=d||c,c=b);for(i in a)this.on(i,c,d,a[i],g);return this}d==null&&e==null?(e=c,d=c=b):e==null&&(typeof c=="string"?(e=d,d=b):(e=d,d=c,c=b));if(e===!1)e=J;else if(!e)return this;g===1&&(h=e,e=function(a){f().off(a);return h.apply(this,arguments)},e.guid=h.guid||(h.guid=f.guid++));return this.each(function(){f.event.add(this,a,e,d,c)})},one:function(a,b,c,d){return this.on(a,b,c,d,1)},off:function(a,c,d){if(a&&a.preventDefault&&a.handleObj){var e=a.handleObj;f(a.delegateTarget).off(e.namespace?e.origType+"."+e.namespace:e.origType,e.selector,e.handler);return this}if(typeof a=="object"){for(var g in a)this.off(g,c,a[g]);return this}if(c===!1||typeof c=="function")d=c,c=b;d===!1&&(d=J);return this.each(function(){f.event.remove(this,a,d,c)})},bind:function(a,b,c){return this.on(a,null,b,c)},unbind:function(a,b){return this.off(a,null,b)},live:function(a,b,c){f(this.context).on(a,this.selector,b,c);return this},die:function(a,b){f(this.context).off(a,this.selector||"**",b);return this},delegate:function(a,b,c,d){return this.on(b,a,c,d)},undelegate:function(a,b,c){return arguments.length==1?this.off(a,"**"):this.off(b,a,c)},trigger:function(a,b){return this.each(function(){f.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0])return f.event.trigger(a,b,this[0],!0)},toggle:function(a){var b=arguments,c=a.guid||f.guid++,d=0,e=function(c){var e=(f._data(this,"lastToggle"+a.guid)||0)%d;f._data(this,"lastToggle"+a.guid,e+1),c.preventDefault();return b[e].apply(this,arguments)||!1};e.guid=c;while(d<b.length)b[d++].guid=c;return this.click(e)},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}}),f.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 contextmenu".split(" "),function(a,b){f.fn[b]=function(a,c){c==null&&(c=a,a=null);return arguments.length>0?this.on(b,null,a,c):this.trigger(b)},f.attrFn&&(f.attrFn[b]=!0),C.test(b)&&(f.event.fixHooks[b]=f.event.keyHooks),D.test(b)&&(f.event.fixHooks[b]=f.event.mouseHooks)}),function(){function x(a,b,c,e,f,g){for(var h=0,i=e.length;h<i;h++){var j=e[h];if(j){var k=!1;j=j[a];while(j){if(j[d]===c){k=e[j.sizset];break}if(j.nodeType===1){g||(j[d]=c,j.sizset=h);if(typeof b!="string"){if(j===b){k=!0;break}}else if(m.filter(b,[j]).length>0){k=j;break}}j=j[a]}e[h]=k}}}function w(a,b,c,e,f,g){for(var h=0,i=e.length;h<i;h++){var j=e[h];if(j){var k=!1;j=j[a];while(j){if(j[d]===c){k=e[j.sizset];break}j.nodeType===1&&!g&&(j[d]=c,j.sizset=h);if(j.nodeName.toLowerCase()===b){k=j;break}j=j[a]}e[h]=k}}}var a=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,d="sizcache"+(Math.random()+"").replace(".",""),e=0,g=Object.prototype.toString,h=!1,i=!0,j=/\\/g,k=/\r\n/g,l=/\W/;[0,0].sort(function(){i=!1;return 0});var m=function(b,d,e,f){e=e||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!="string")return e;var i,j,k,l,n,q,r,t,u=!0,v=m.isXML(d),w=[],x=b;do{a.exec(""),i=a.exec(x);if(i){x=i[3],w.push(i[1]);if(i[2]){l=i[3];break}}}while(i);if(w.length>1&&p.exec(b))if(w.length===2&&o.relative[w[0]])j=y(w[0]+w[1],d,f);else{j=o.relative[w[0]]?[d]:m(w.shift(),d);while(w.length)b=w.shift(),o.relative[b]&&(b+=w.shift()),j=y(b,j,f)}else{!f&&w.length>1&&d.nodeType===9&&!v&&o.match.ID.test(w[0])&&!o.match.ID.test(w[w.length-1])&&(n=m.find(w.shift(),d,v),d=n.expr?m.filter(n.expr,n.set)[0]:n.set[0]);if(d){n=f?{expr:w.pop(),set:s(f)}:m.find(w.pop(),w.length===1&&(w[0]==="~"||w[0]==="+")&&d.parentNode?d.parentNode:d,v),j=n.expr?m.filter(n.expr,n.set):n.set,w.length>0?k=s(j):u=!1;while(w.length)q=w.pop(),r=q,o.relative[q]?r=w.pop():q="",r==null&&(r=d),o.relative[q](k,r,v)}else k=w=[]}k||(k=j),k||m.error(q||b);if(g.call(k)==="[object Array]")if(!u)e.push.apply(e,k);else if(d&&d.nodeType===1)for(t=0;k[t]!=null;t++)k[t]&&(k[t]===!0||k[t].nodeType===1&&m.contains(d,k[t]))&&e.push(j[t]);else for(t=0;k[t]!=null;t++)k[t]&&k[t].nodeType===1&&e.push(j[t]);else s(k,e);l&&(m(l,h,e,f),m.uniqueSort(e));return e};m.uniqueSort=function(a){if(u){h=i,a.sort(u);if(h)for(var b=1;b<a.length;b++)a[b]===a[b-1]&&a.splice(b--,1)}return a},m.matches=function(a,b){return m(a,null,null,b)},m.matchesSelector=function(a,b){return m(b,null,null,[a]).length>0},m.find=function(a,b,c){var d,e,f,g,h,i;if(!a)return[];for(e=0,f=o.order.length;e<f;e++){h=o.order[e];if(g=o.leftMatch[h].exec(a)){i=g[1],g.splice(1,1);if(i.substr(i.length-1)!=="\\"){g[1]=(g[1]||"").replace(j,""),d=o.find[h](g,b,c);if(d!=null){a=a.replace(o.match[h],"");break}}}}d||(d=typeof b.getElementsByTagName!="undefined"?b.getElementsByTagName("*"):[]);return{set:d,expr:a}},m.filter=function(a,c,d,e){var f,g,h,i,j,k,l,n,p,q=a,r=[],s=c,t=c&&c[0]&&m.isXML(c[0]);while(a&&c.length){for(h in o.filter)if((f=o.leftMatch[h].exec(a))!=null&&f[2]){k=o.filter[h],l=f[1],g=!1,f.splice(1,1);if(l.substr(l.length-1)==="\\")continue;s===r&&(r=[]);if(o.preFilter[h]){f=o.preFilter[h](f,s,d,r,e,t);if(!f)g=i=!0;else if(f===!0)continue}if(f)for(n=0;(j=s[n])!=null;n++)j&&(i=k(j,f,n,s),p=e^i,d&&i!=null?p?g=!0:s[n]=!1:p&&(r.push(j),g=!0));if(i!==b){d||(s=r),a=a.replace(o.match[h],"");if(!g)return[];break}}if(a===q)if(g==null)m.error(a);else break;q=a}return s},m.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)};var n=m.getText=function(a){var b,c,d=a.nodeType,e="";if(d){if(d===1||d===9||d===11){if(typeof a.textContent=="string")return a.textContent;if(typeof a.innerText=="string")return a.innerText.replace(k,"");for(a=a.firstChild;a;a=a.nextSibling)e+=n(a)}else if(d===3||d===4)return a.nodeValue}else for(b=0;c=a[b];b++)c.nodeType!==8&&(e+=n(c));return e},o=m.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|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(a){return a.getAttribute("href")},type:function(a){return a.getAttribute("type")}},relative:{"+":function(a,b){var c=typeof b=="string",d=c&&!l.test(b),e=c&&!d;d&&(b=b.toLowerCase());for(var f=0,g=a.length,h;f<g;f++)if(h=a[f]){while((h=h.previousSibling)&&h.nodeType!==1);a[f]=e||h&&h.nodeName.toLowerCase()===b?h||!1:h===b}e&&m.filter(b,a,!0)},">":function(a,b){var c,d=typeof b=="string",e=0,f=a.length;if(d&&!l.test(b)){b=b.toLowerCase();for(;e<f;e++){c=a[e];if(c){var g=c.parentNode;a[e]=g.nodeName.toLowerCase()===b?g:!1}}}else{for(;e<f;e++)c=a[e],c&&(a[e]=d?c.parentNode:c.parentNode===b);d&&m.filter(b,a,!0)}},"":function(a,b,c){var d,f=e++,g=x;typeof b=="string"&&!l.test(b)&&(b=b.toLowerCase(),d=b,g=w),g("parentNode",b,f,a,d,c)},"~":function(a,b,c){var d,f=e++,g=x;typeof b=="string"&&!l.test(b)&&(b=b.toLowerCase(),d=b,g=w),g("previousSibling",b,f,a,d,c)}},find:{ID:function(a,b,c){if(typeof b.getElementById!="undefined"&&!c){var d=b.getElementById(a[1]);return d&&d.parentNode?[d]:[]}},NAME:function(a,b){if(typeof b.getElementsByName!="undefined"){var c=[],d=b.getElementsByName(a[1]);for(var e=0,f=d.length;e<f;e++)d[e].getAttribute("name")===a[1]&&c.push(d[e]);return c.length===0?null:c}},TAG:function(a,b){if(typeof b.getElementsByTagName!="undefined")return b.getElementsByTagName(a[1])}},preFilter:{CLASS:function(a,b,c,d,e,f){a=" "+a[1].replace(j,"")+" ";if(f)return a;for(var g=0,h;(h=b[g])!=null;g++)h&&(e^(h.className&&(" "+h.className+" ").replace(/[\t\n\r]/g," ").indexOf(a)>=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(j,"")},TAG:function(a,b){return a[1].replace(j,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||m.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&m.error(a[0]);a[0]=e++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(j,"");!f&&o.attrMap[g]&&(a[1]=o.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(j,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=m(b[3],null,null,c);else{var g=m.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(o.match.POS.test(b[0])||o.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!m(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return a.nodeName.toLowerCase()==="input"&&"text"===c&&(b===c||b===null)},radio:function(a){return a.nodeName.toLowerCase()==="input"&&"radio"===a.type},checkbox:function(a){return a.nodeName.toLowerCase()==="input"&&"checkbox"===a.type},file:function(a){return a.nodeName.toLowerCase()==="input"&&"file"===a.type},password:function(a){return a.nodeName.toLowerCase()==="input"&&"password"===a.type},submit:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"submit"===a.type},image:function(a){return a.nodeName.toLowerCase()==="input"&&"image"===a.type},reset:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"reset"===a.type},button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&"button"===a.type||b==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)},focus:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return b<c[3]-0},gt:function(a,b,c){return b>c[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=o.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||n([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h<i;h++)if(g[h]===a)return!1;return!0}m.error(e)},CHILD:function(a,b){var c,e,f,g,h,i,j,k=b[1],l=a;switch(k){case"only":case"first":while(l=l.previousSibling)if(l.nodeType===1)return!1;if(k==="first")return!0;l=a;case"last":while(l=l.nextSibling)if(l.nodeType===1)return!1;return!0;case"nth":c=b[2],e=b[3];if(c===1&&e===0)return!0;f=b[0],g=a.parentNode;if(g&&(g[d]!==f||!a.nodeIndex)){i=0;for(l=g.firstChild;l;l=l.nextSibling)l.nodeType===1&&(l.nodeIndex=++i);g[d]=f}j=a.nodeIndex-e;return c===0?j===0:j%c===0&&j/c>=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||!!a.nodeName&&a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=m.attr?m.attr(a,c):o.attrHandle[c]?o.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":!f&&m.attr?d!=null:f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=o.setFilters[e];if(f)return f(a,c,b,d)}}},p=o.match.POS,q=function(a,b){return"\\"+(b-0+1)};for(var r in o.match)o.match[r]=new RegExp(o.match[r].source+/(?![^\[]*\])(?![^\(]*\))/.source),o.leftMatch[r]=new RegExp(/(^(?:.|\r|\n)*?)/.source+o.match[r].source.replace(/\\(\d+)/g,q));o.match.globalPOS=p;var s=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(t){s=function(a,b){var c=0,d=b||[];if(g.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length=="number")for(var e=a.length;c<e;c++)d.push(a[c]);else for(;a[c];c++)d.push(a[c]);return d}}var u,v;c.documentElement.compareDocumentPosition?u=function(a,b){if(a===b){h=!0;return 0}if(!a.compareDocumentPosition||!b.compareDocumentPosition)return a.compareDocumentPosition?-1:1;return a.compareDocumentPosition(b)&4?-1:1}:(u=function(a,b){if(a===b){h=!0;return 0}if(a.sourceIndex&&b.sourceIndex)return a.sourceIndex-b.sourceIndex;var c,d,e=[],f=[],g=a.parentNode,i=b.parentNode,j=g;if(g===i)return v(a,b);if(!g)return-1;if(!i)return 1;while(j)e.unshift(j),j=j.parentNode;j=i;while(j)f.unshift(j),j=j.parentNode;c=e.length,d=f.length;for(var k=0;k<c&&k<d;k++)if(e[k]!==f[k])return v(e[k],f[k]);return k===c?v(a,f[k],-1):v(e[k],b,1)},v=function(a,b,c){if(a===b)return c;var d=a.nextSibling;while(d){if(d===b)return-1;d=d.nextSibling}return 1}),function(){var a=c.createElement("div"),d="script"+(new Date).getTime(),e=c.documentElement;a.innerHTML="<a name='"+d+"'/>",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(o.find.ID=function(a,c,d){if(typeof c.getElementById!="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},o.filter.ID=function(a,b){var c=typeof a.getAttributeNode!="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(o.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="<a href='#'></a>",a.firstChild&&typeof a.firstChild.getAttribute!="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(o.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=m,b=c.createElement("div"),d="__sizzle__";b.innerHTML="<p class='TEST'></p>";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){m=function(b,e,f,g){e=e||c;if(!g&&!m.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return s(e.getElementsByTagName(b),f);if(h[2]&&o.find.CLASS&&e.getElementsByClassName)return s(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return s([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return s([],f);if(i.id===h[3])return s([i],f)}try{return s(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var k=e,l=e.getAttribute("id"),n=l||d,p=e.parentNode,q=/^\s*[+~]/.test(b);l?n=n.replace(/'/g,"\\$&"):e.setAttribute("id",n),q&&p&&(e=e.parentNode);try{if(!q||p)return s(e.querySelectorAll("[id='"+n+"'] "+b),f)}catch(r){}finally{l||k.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)m[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}m.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!m.isXML(a))try{if(e||!o.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return m(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="<div class='test e'></div><div class='test'></div>";if(!!a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;o.order.splice(1,0,"CLASS"),o.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?m.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?m.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:m.contains=function(){return!1},m.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var y=function(a,b,c){var d,e=[],f="",g=b.nodeType?[b]:b;while(d=o.match.PSEUDO.exec(a))f+=d[0],a=a.replace(o.match.PSEUDO,"");a=o.relative[a]?a+"*":a;for(var h=0,i=g.length;h<i;h++)m(a,g[h],e,c);return m.filter(f,e)};m.attr=f.attr,m.selectors.attrMap={},f.find=m,f.expr=m.selectors,f.expr[":"]=f.expr.filters,f.unique=m.uniqueSort,f.text=m.getText,f.isXMLDoc=m.isXML,f.contains=m.contains}();var L=/Until$/,M=/^(?:parents|prevUntil|prevAll)/,N=/,/,O=/^.[^:#\[\.,]*$/,P=Array.prototype.slice,Q=f.expr.match.globalPOS,R={children:!0,contents:!0,next:!0,prev:!0};f.fn.extend({find:function(a){var b=this,c,d;if(typeof a!="string")return f(a).filter(function(){for(c=0,d=b.length;c<d;c++)if(f.contains(b[c],this))return!0});var e=this.pushStack("","find",a),g,h,i;for(c=0,d=this.length;c<d;c++){g=e.length,f.find(a,this[c],e);if(c>0)for(h=g;h<e.length;h++)for(i=0;i<g;i++)if(e[i]===e[h]){e.splice(h--,1);break}}return e},has:function(a){var b=f(a);return this.filter(function(){for(var a=0,c=b.length;a<c;a++)if(f.contains(this,b[a]))return!0})},not:function(a){return this.pushStack(T(this,a,!1),"not",a)},filter:function(a){return this.pushStack(T(this,a,!0),"filter",a)},is:function(a){return!!a&&(typeof a=="string"?Q.test(a)?f(a,this.context).index(this[0])>=0:f.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c=[],d,e,g=this[0];if(f.isArray(a)){var h=1;while(g&&g.ownerDocument&&g!==b){for(d=0;d<a.length;d++)f(g).is(a[d])&&c.push({selector:a[d],elem:g,level:h});g=g.parentNode,h++}return c}var i=Q.test(a)||typeof a!="string"?f(a,b||this.context):0;for(d=0,e=this.length;d<e;d++){g=this[d];while(g){if(i?i.index(g)>-1:f.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b||g.nodeType===11)break}}c=c.length>1?f.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a)return this[0]&&this[0].parentNode?this.prevAll().length:-1;if(typeof a=="string")return f.inArray(this[0],f(a));return f.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a=="string"?f(a,b):f.makeArray(a&&a.nodeType?[a]:a),d=f.merge(this.get(),c);return this.pushStack(S(c[0])||S(d[0])?d:f.unique(d))},andSelf:function(){return this.add(this.prevObject)}}),f.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return f.dir(a,"parentNode")},parentsUntil:function(a,b,c){return f.dir(a,"parentNode",c)},next:function(a){return f.nth(a,2,"nextSibling")},prev:function(a){return f.nth(a,2,"previousSibling")},nextAll:function(a){return f.dir(a,"nextSibling")},prevAll:function(a){return f.dir(a,"previousSibling")},nextUntil:function(a,b,c){return f.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return f.dir(a,"previousSibling",c)},siblings:function(a){return f.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return f.sibling(a.firstChild)},contents:function(a){return f.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:f.makeArray(a.childNodes)}},function(a,b){f.fn[a]=function(c,d){var e=f.map(this,b,c);L.test(a)||(d=c),d&&typeof d=="string"&&(e=f.filter(d,e)),e=this.length>1&&!R[a]?f.unique(e):e,(this.length>1||N.test(d))&&M.test(a)&&(e=e.reverse());return this.pushStack(e,a,P.call(arguments).join(","))}}),f.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?f.find.matchesSelector(b[0],a)?[b[0]]:[]:f.find.matches(a,b)},dir:function(a,c,d){var e=[],g=a[c];while(g&&g.nodeType!==9&&(d===b||g.nodeType!==1||!f(g).is(d)))g.nodeType===1&&e.push(g),g=g[c];return e},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var V="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",W=/ jQuery\d+="(?:\d+|null)"/g,X=/^\s+/,Y=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,Z=/<([\w:]+)/,$=/<tbody/i,_=/<|&#?\w+;/,ba=/<(?:script|style)/i,bb=/<(?:script|object|embed|option|style)/i,bc=new RegExp("<(?:"+V+")[\\s/>]","i"),bd=/checked\s*(?:[^=]|=\s*.checked.)/i,be=/\/(java|ecma)script/i,bf=/^\s*<!(?:\[CDATA\[|\-\-)/,bg={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,"",""]},bh=U(c);bg.optgroup=bg.option,bg.tbody=bg.tfoot=bg.colgroup=bg.caption=bg.thead,bg.th=bg.td,f.support.htmlSerialize||(bg._default=[1,"div<div>","</div>"]),f.fn.extend({text:function(a){return f.access(this,function(a){return a===b?f.text(this):this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a))},null,a,arguments.length)},wrapAll:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapAll(a.call(this,b))});if(this[0]){var b=f(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapInner(a.call(this,b))});return this.each(function(){var b=f(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=f.isFunction(a);return this.each(function(c){f(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){f.nodeName(this,"body")||f(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=f
+.clean(arguments);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,f.clean(arguments));return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++)if(!a||f.filter(a,[d]).length)!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagName("*")),f.cleanData([d])),d.parentNode&&d.parentNode.removeChild(d);return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&f.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return f.clone(this,a,b)})},html:function(a){return f.access(this,function(a){var c=this[0]||{},d=0,e=this.length;if(a===b)return c.nodeType===1?c.innerHTML.replace(W,""):null;if(typeof a=="string"&&!ba.test(a)&&(f.support.leadingWhitespace||!X.test(a))&&!bg[(Z.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Y,"<$1></$2>");try{for(;d<e;d++)c=this[d]||{},c.nodeType===1&&(f.cleanData(c.getElementsByTagName("*")),c.innerHTML=a);c=0}catch(g){}}c&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(a){if(this[0]&&this[0].parentNode){if(f.isFunction(a))return this.each(function(b){var c=f(this),d=c.html();c.replaceWith(a.call(this,b,d))});typeof a!="string"&&(a=f(a).detach());return this.each(function(){var b=this.nextSibling,c=this.parentNode;f(this).remove(),b?f(b).before(a):f(c).append(a)})}return this.length?this.pushStack(f(f.isFunction(a)?a():a),"replaceWith",a):this},detach:function(a){return this.remove(a,!0)},domManip:function(a,c,d){var e,g,h,i,j=a[0],k=[];if(!f.support.checkClone&&arguments.length===3&&typeof j=="string"&&bd.test(j))return this.each(function(){f(this).domManip(a,c,d,!0)});if(f.isFunction(j))return this.each(function(e){var g=f(this);a[0]=j.call(this,e,c?g.html():b),g.domManip(a,c,d)});if(this[0]){i=j&&j.parentNode,f.support.parentNode&&i&&i.nodeType===11&&i.childNodes.length===this.length?e={fragment:i}:e=f.buildFragment(a,this,k),h=e.fragment,h.childNodes.length===1?g=h=h.firstChild:g=h.firstChild;if(g){c=c&&f.nodeName(g,"tr");for(var l=0,m=this.length,n=m-1;l<m;l++)d.call(c?bi(this[l],g):this[l],e.cacheable||m>1&&l<n?f.clone(h,!0,!0):h)}k.length&&f.each(k,function(a,b){b.src?f.ajax({type:"GET",global:!1,url:b.src,async:!1,dataType:"script"}):f.globalEval((b.text||b.textContent||b.innerHTML||"").replace(bf,"/*$0*/")),b.parentNode&&b.parentNode.removeChild(b)})}return this}}),f.buildFragment=function(a,b,d){var e,g,h,i,j=a[0];b&&b[0]&&(i=b[0].ownerDocument||b[0]),i.createDocumentFragment||(i=c),a.length===1&&typeof j=="string"&&j.length<512&&i===c&&j.charAt(0)==="<"&&!bb.test(j)&&(f.support.checkClone||!bd.test(j))&&(f.support.html5Clone||!bc.test(j))&&(g=!0,h=f.fragments[j],h&&h!==1&&(e=h)),e||(e=i.createDocumentFragment(),f.clean(a,i,e,d)),g&&(f.fragments[j]=h?e:1);return{fragment:e,cacheable:g}},f.fragments={},f.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){f.fn[a]=function(c){var d=[],e=f(c),g=this.length===1&&this[0].parentNode;if(g&&g.nodeType===11&&g.childNodes.length===1&&e.length===1){e[b](this[0]);return this}for(var h=0,i=e.length;h<i;h++){var j=(h>0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j)}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d,e,g,h=f.support.html5Clone||f.isXMLDoc(a)||!bc.test("<"+a.nodeName+">")?a.cloneNode(!0):bo(a);if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bk(a,h),d=bl(a),e=bl(h);for(g=0;d[g];++g)e[g]&&bk(d[g],e[g])}if(b){bj(a,h);if(c){d=bl(a),e=bl(h);for(g=0;d[g];++g)bj(d[g],e[g])}}d=e=null;return h},clean:function(a,b,d,e){var g,h,i,j=[];b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);for(var k=0,l;(l=a[k])!=null;k++){typeof l=="number"&&(l+="");if(!l)continue;if(typeof l=="string")if(!_.test(l))l=b.createTextNode(l);else{l=l.replace(Y,"<$1></$2>");var m=(Z.exec(l)||["",""])[1].toLowerCase(),n=bg[m]||bg._default,o=n[0],p=b.createElement("div"),q=bh.childNodes,r;b===c?bh.appendChild(p):U(b).appendChild(p),p.innerHTML=n[1]+l+n[2];while(o--)p=p.lastChild;if(!f.support.tbody){var s=$.test(l),t=m==="table"&&!s?p.firstChild&&p.firstChild.childNodes:n[1]==="<table>"&&!s?p.childNodes:[];for(i=t.length-1;i>=0;--i)f.nodeName(t[i],"tbody")&&!t[i].childNodes.length&&t[i].parentNode.removeChild(t[i])}!f.support.leadingWhitespace&&X.test(l)&&p.insertBefore(b.createTextNode(X.exec(l)[0]),p.firstChild),l=p.childNodes,p&&(p.parentNode.removeChild(p),q.length>0&&(r=q[q.length-1],r&&r.parentNode&&r.parentNode.removeChild(r)))}var u;if(!f.support.appendChecked)if(l[0]&&typeof (u=l.length)=="number")for(i=0;i<u;i++)bn(l[i]);else bn(l);l.nodeType?j.push(l):j=f.merge(j,l)}if(d){g=function(a){return!a.type||be.test(a.type)};for(k=0;j[k];k++){h=j[k];if(e&&f.nodeName(h,"script")&&(!h.type||be.test(h.type)))e.push(h.parentNode?h.parentNode.removeChild(h):h);else{if(h.nodeType===1){var v=f.grep(h.getElementsByTagName("script"),g);j.splice.apply(j,[k+1,0].concat(v))}d.appendChild(h)}}}return j},cleanData:function(a){var b,c,d=f.cache,e=f.event.special,g=f.support.deleteExpando;for(var h=0,i;(i=a[h])!=null;h++){if(i.nodeName&&f.noData[i.nodeName.toLowerCase()])continue;c=i[f.expando];if(c){b=d[c];if(b&&b.events){for(var j in b.events)e[j]?f.event.remove(i,j):f.removeEvent(i,j,b.handle);b.handle&&(b.handle.elem=null)}g?delete i[f.expando]:i.removeAttribute&&i.removeAttribute(f.expando),delete d[c]}}}});var bp=/alpha\([^)]*\)/i,bq=/opacity=([^)]*)/,br=/([A-Z]|^ms)/g,bs=/^[\-+]?(?:\d*\.)?\d+$/i,bt=/^-?(?:\d*\.)?\d+(?!px)[^\d\s]+$/i,bu=/^([\-+])=([\-+.\de]+)/,bv=/^margin/,bw={position:"absolute",visibility:"hidden",display:"block"},bx=["Top","Right","Bottom","Left"],by,bz,bA;f.fn.css=function(a,c){return f.access(this,function(a,c,d){return d!==b?f.style(a,c,d):f.css(a,c)},a,c,arguments.length>1)},f.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=by(a,"opacity");return c===""?"1":c}return a.style.opacity}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":f.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,d,e){if(!!a&&a.nodeType!==3&&a.nodeType!==8&&!!a.style){var g,h,i=f.camelCase(c),j=a.style,k=f.cssHooks[i];c=f.cssProps[i]||i;if(d===b){if(k&&"get"in k&&(g=k.get(a,!1,e))!==b)return g;return j[c]}h=typeof d,h==="string"&&(g=bu.exec(d))&&(d=+(g[1]+1)*+g[2]+parseFloat(f.css(a,c)),h="number");if(d==null||h==="number"&&isNaN(d))return;h==="number"&&!f.cssNumber[i]&&(d+="px");if(!k||!("set"in k)||(d=k.set(a,d))!==b)try{j[c]=d}catch(l){}}},css:function(a,c,d){var e,g;c=f.camelCase(c),g=f.cssHooks[c],c=f.cssProps[c]||c,c==="cssFloat"&&(c="float");if(g&&"get"in g&&(e=g.get(a,!0,d))!==b)return e;if(by)return by(a,c)},swap:function(a,b,c){var d={},e,f;for(f in b)d[f]=a.style[f],a.style[f]=b[f];e=c.call(a);for(f in b)a.style[f]=d[f];return e}}),f.curCSS=f.css,c.defaultView&&c.defaultView.getComputedStyle&&(bz=function(a,b){var c,d,e,g,h=a.style;b=b.replace(br,"-$1").toLowerCase(),(d=a.ownerDocument.defaultView)&&(e=d.getComputedStyle(a,null))&&(c=e.getPropertyValue(b),c===""&&!f.contains(a.ownerDocument.documentElement,a)&&(c=f.style(a,b))),!f.support.pixelMargin&&e&&bv.test(b)&&bt.test(c)&&(g=h.width,h.width=c,c=e.width,h.width=g);return c}),c.documentElement.currentStyle&&(bA=function(a,b){var c,d,e,f=a.currentStyle&&a.currentStyle[b],g=a.style;f==null&&g&&(e=g[b])&&(f=e),bt.test(f)&&(c=g.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),g.left=b==="fontSize"?"1em":f,f=g.pixelLeft+"px",g.left=c,d&&(a.runtimeStyle.left=d));return f===""?"auto":f}),by=bz||bA,f.each(["height","width"],function(a,b){f.cssHooks[b]={get:function(a,c,d){if(c)return a.offsetWidth!==0?bB(a,b,d):f.swap(a,bw,function(){return bB(a,b,d)})},set:function(a,b){return bs.test(b)?b+"px":b}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return bq.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=f.isNumeric(b)?"alpha(opacity="+b*100+")":"",g=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&f.trim(g.replace(bp,""))===""){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bp.test(g)?g.replace(bp,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){return f.swap(a,{display:"inline-block"},function(){return b?by(a,"margin-right"):a.style.marginRight})}})}),f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style&&a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)}),f.each({margin:"",padding:"",border:"Width"},function(a,b){f.cssHooks[a+b]={expand:function(c){var d,e=typeof c=="string"?c.split(" "):[c],f={};for(d=0;d<4;d++)f[a+bx[d]+b]=e[d]||e[d-2]||e[0];return f}}});var bC=/%20/g,bD=/\[\]$/,bE=/\r?\n/g,bF=/#.*$/,bG=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bH=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bI=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,bJ=/^(?:GET|HEAD)$/,bK=/^\/\//,bL=/\?/,bM=/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,bN=/^(?:select|textarea)/i,bO=/\s+/,bP=/([?&])_=[^&]*/,bQ=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bR=f.fn.load,bS={},bT={},bU,bV,bW=["*/"]+["*"];try{bU=e.href}catch(bX){bU=c.createElement("a"),bU.href="",bU=bU.href}bV=bQ.exec(bU.toLowerCase())||[],f.fn.extend({load:function(a,c,d){if(typeof a!="string"&&bR)return bR.apply(this,arguments);if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var g=a.slice(e,a.length);a=a.slice(0,e)}var h="GET";c&&(f.isFunction(c)?(d=c,c=b):typeof c=="object"&&(c=f.param(c,f.ajaxSettings.traditional),h="POST"));var i=this;f.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?f("<div>").append(c.replace(bM,"")).find(g):c)),d&&i.each(d,[c,b,a])}});return this},serialize:function(){return f.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?f.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bN.test(this.nodeName)||bH.test(this.type))}).map(function(a,b){var c=f(this).val();return c==null?null:f.isArray(c)?f.map(c,function(a,c){return{name:b.name,value:a.replace(bE,"\r\n")}}):{name:b.name,value:c.replace(bE,"\r\n")}}).get()}}),f.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){f.fn[b]=function(a){return this.on(b,a)}}),f.each(["get","post"],function(a,c){f[c]=function(a,d,e,g){f.isFunction(d)&&(g=g||e,e=d,d=b);return f.ajax({type:c,url:a,data:d,success:e,dataType:g})}}),f.extend({getScript:function(a,c){return f.get(a,b,c,"script")},getJSON:function(a,b,c){return f.get(a,b,c,"json")},ajaxSetup:function(a,b){b?b$(a,f.ajaxSettings):(b=a,a=f.ajaxSettings),b$(a,b);return a},ajaxSettings:{url:bU,isLocal:bI.test(bV[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded; charset=UTF-8",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":bW},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":f.parseJSON,"text xml":f.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:bY(bS),ajaxTransport:bY(bT),ajax:function(a,c){function w(a,c,l,m){if(s!==2){s=2,q&&clearTimeout(q),p=b,n=m||"",v.readyState=a>0?4:0;var o,r,u,w=c,x=l?ca(d,v,l):b,y,z;if(a>=200&&a<300||a===304){if(d.ifModified){if(y=v.getResponseHeader("Last-Modified"))f.lastModified[k]=y;if(z=v.getResponseHeader("Etag"))f.etag[k]=z}if(a===304)w="notmodified",o=!0;else try{r=cb(d,x),w="success",o=!0}catch(A){w="parsererror",u=A}}else{u=w;if(!w||a)w="error",a<0&&(a=0)}v.status=a,v.statusText=""+(c||w),o?h.resolveWith(e,[r,w,v]):h.rejectWith(e,[v,w,u]),v.statusCode(j),j=b,t&&g.trigger("ajax"+(o?"Success":"Error"),[v,d,o?r:u]),i.fireWith(e,[v,w]),t&&(g.trigger("ajaxComplete",[v,d]),--f.active||f.event.trigger("ajaxStop"))}}typeof a=="object"&&(c=a,a=b),c=c||{};var d=f.ajaxSetup({},c),e=d.context||d,g=e!==d&&(e.nodeType||e instanceof f)?f(e):f.event,h=f.Deferred(),i=f.Callbacks("once memory"),j=d.statusCode||{},k,l={},m={},n,o,p,q,r,s=0,t,u,v={readyState:0,setRequestHeader:function(a,b){if(!s){var c=a.toLowerCase();a=m[c]=m[c]||a,l[a]=b}return this},getAllResponseHeaders:function(){return s===2?n:null},getResponseHeader:function(a){var c;if(s===2){if(!o){o={};while(c=bG.exec(n))o[c[1].toLowerCase()]=c[2]}c=o[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){s||(d.mimeType=a);return this},abort:function(a){a=a||"abort",p&&p.abort(a),w(0,a);return this}};h.promise(v),v.success=v.done,v.error=v.fail,v.complete=i.add,v.statusCode=function(a){if(a){var b;if(s<2)for(b in a)j[b]=[j[b],a[b]];else b=a[v.status],v.then(b,b)}return this},d.url=((a||d.url)+"").replace(bF,"").replace(bK,bV[1]+"//"),d.dataTypes=f.trim(d.dataType||"*").toLowerCase().split(bO),d.crossDomain==null&&(r=bQ.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]==bV[1]&&r[2]==bV[2]&&(r[3]||(r[1]==="http:"?80:443))==(bV[3]||(bV[1]==="http:"?80:443)))),d.data&&d.processData&&typeof d.data!="string"&&(d.data=f.param(d.data,d.traditional)),bZ(bS,d,c,v);if(s===2)return!1;t=d.global,d.type=d.type.toUpperCase(),d.hasContent=!bJ.test(d.type),t&&f.active++===0&&f.event.trigger("ajaxStart");if(!d.hasContent){d.data&&(d.url+=(bL.test(d.url)?"&":"?")+d.data,delete d.data),k=d.url;if(d.cache===!1){var x=f.now(),y=d.url.replace(bP,"$1_="+x);d.url=y+(y===d.url?(bL.test(d.url)?"&":"?")+"_="+x:"")}}(d.data&&d.hasContent&&d.contentType!==!1||c.contentType)&&v.setRequestHeader("Content-Type",d.contentType),d.ifModified&&(k=k||d.url,f.lastModified[k]&&v.setRequestHeader("If-Modified-Since",f.lastModified[k]),f.etag[k]&&v.setRequestHeader("If-None-Match",f.etag[k])),v.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(d.dataTypes[0]!=="*"?", "+bW+"; q=0.01":""):d.accepts["*"]);for(u in d.headers)v.setRequestHeader(u,d.headers[u]);if(d.beforeSend&&(d.beforeSend.call(e,v,d)===!1||s===2)){v.abort();return!1}for(u in{success:1,error:1,complete:1})v[u](d[u]);p=bZ(bT,d,c,v);if(!p)w(-1,"No Transport");else{v.readyState=1,t&&g.trigger("ajaxSend",[v,d]),d.async&&d.timeout>0&&(q=setTimeout(function(){v.abort("timeout")},d.timeout));try{s=1,p.send(l,w)}catch(z){if(s<2)w(-1,z);else throw z}}return v},param:function(a,c){var d=[],e=function(a,b){b=f.isFunction(b)?b():b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=f.ajaxSettings.traditional);if(f.isArray(a)||a.jquery&&!f.isPlainObject(a))f.each(a,function(){e(this.name,this.value)});else for(var g in a)b_(g,a[g],c,e);return d.join("&").replace(bC,"+")}}),f.extend({active:0,lastModified:{},etag:{}});var cc=f.now(),cd=/(\=)\?(&|$)|\?\?/i;f.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return f.expando+"_"+cc++}}),f.ajaxPrefilter("json jsonp",function(b,c,d){var e=typeof b.data=="string"&&/^application\/x\-www\-form\-urlencoded/.test(b.contentType);if(b.dataTypes[0]==="jsonp"||b.jsonp!==!1&&(cd.test(b.url)||e&&cd.test(b.data))){var g,h=b.jsonpCallback=f.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2";b.jsonp!==!1&&(j=j.replace(cd,l),b.url===j&&(e&&(k=k.replace(cd,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},d.always(function(){a[h]=i,g&&f.isFunction(i)&&a[h](g[0])}),b.converters["script json"]=function(){g||f.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),f.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){f.globalEval(a);return a}}}),f.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),f.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(c||!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var ce=a.ActiveXObject?function(){for(var a in cg)cg[a](0,1)}:!1,cf=0,cg;f.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&ch()||ci()}:ch,function(a){f.extend(f.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(f.ajaxSettings.xhr()),f.support.ajax&&f.ajaxTransport(function(c){if(!c.crossDomain||f.support.cors){var d;return{send:function(e,g){var h=c.xhr(),i,j;c.username?h.open(c.type,c.url,c.async,c.username,c.password):h.open(c.type,c.url,c.async);if(c.xhrFields)for(j in c.xhrFields)h[j]=c.xhrFields[j];c.mimeType&&h.overrideMimeType&&h.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(j in e)h.setRequestHeader(j,e[j])}catch(k){}h.send(c.hasContent&&c.data||null),d=function(a,e){var j,k,l,m,n;try{if(d&&(e||h.readyState===4)){d=b,i&&(h.onreadystatechange=f.noop,ce&&delete cg[i]);if(e)h.readyState!==4&&h.abort();else{j=h.status,l=h.getAllResponseHeaders(),m={},n=h.responseXML,n&&n.documentElement&&(m.xml=n);try{m.text=h.responseText}catch(a){}try{k=h.statusText}catch(o){k=""}!j&&c.isLocal&&!c.crossDomain?j=m.text?200:404:j===1223&&(j=204)}}}catch(p){e||g(-1,p)}m&&g(j,k,m,l)},!c.async||h.readyState===4?d():(i=++cf,ce&&(cg||(cg={},f(a).unload(ce)),cg[i]=d),h.onreadystatechange=d)},abort:function(){d&&d(0,1)}}}});var cj={},ck,cl,cm=/^(?:toggle|show|hide)$/,cn=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,co,cp=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]],cq;f.fn.extend({show:function(a,b,c){var d,e;if(a||a===0)return this.animate(ct("show",3),a,b,c);for(var g=0,h=this.length;g<h;g++)d=this[g],d.style&&(e=d.style.display,!f._data(d,"olddisplay")&&e==="none"&&(e=d.style.display=""),(e===""&&f.css(d,"display")==="none"||!f.contains(d.ownerDocument.documentElement,d))&&f._data(d,"olddisplay",cu(d.nodeName)));for(g=0;g<h;g++){d=this[g];if(d.style){e=d.style.display;if(e===""||e==="none")d.style.display=f._data(d,"olddisplay")||""}}return this},hide:function(a,b,c){if(a||a===0)return this.animate(ct("hide",3),a,b,c);var d,e,g=0,h=this.length;for(;g<h;g++)d=this[g],d.style&&(e=f.css(d,"display"),e!=="none"&&!f._data(d,"olddisplay")&&f._data(d,"olddisplay",e));for(g=0;g<h;g++)this[g].style&&(this[g].style.display="none");return this},_toggle:f.fn.toggle,toggle:function(a,b,c){var d=typeof a=="boolean";f.isFunction(a)&&f.isFunction(b)?this._toggle.apply(this,arguments):a==null||d?this.each(function(){var b=d?a:f(this).is(":hidden");f(this)[b?"show":"hide"]()}):this.animate(ct("toggle",3),a,b,c);return this},fadeTo:function(a,b,c,d){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){function g(){e.queue===!1&&f._mark(this);var b=f.extend({},e),c=this.nodeType===1,d=c&&f(this).is(":hidden"),g,h,i,j,k,l,m,n,o,p,q;b.animatedProperties={};for(i in a){g=f.camelCase(i),i!==g&&(a[g]=a[i],delete a[i]);if((k=f.cssHooks[g])&&"expand"in k){l=k.expand(a[g]),delete a[g];for(i in l)i in a||(a[i]=l[i])}}for(g in a){h=a[g],f.isArray(h)?(b.animatedProperties[g]=h[1],h=a[g]=h[0]):b.animatedProperties[g]=b.specialEasing&&b.specialEasing[g]||b.easing||"swing";if(h==="hide"&&d||h==="show"&&!d)return b.complete.call(this);c&&(g==="height"||g==="width")&&(b.overflow=[this.style.overflow,this.style.overflowX,this.style.overflowY],f.css(this,"display")==="inline"&&f.css(this,"float")==="none"&&(!f.support.inlineBlockNeedsLayout||cu(this.nodeName)==="inline"?this.style.display="inline-block":this.style.zoom=1))}b.overflow!=null&&(this.style.overflow="hidden");for(i in a)j=new f.fx(this,b,i),h=a[i],cm.test(h)?(q=f._data(this,"toggle"+i)||(h==="toggle"?d?"show":"hide":0),q?(f._data(this,"toggle"+i,q==="show"?"hide":"show"),j[q]()):j[h]()):(m=cn.exec(h),n=j.cur(),m?(o=parseFloat(m[2]),p=m[3]||(f.cssNumber[i]?"":"px"),p!=="px"&&(f.style(this,i,(o||1)+p),n=(o||1)/j.cur()*n,f.style(this,i,n+p)),m[1]&&(o=(m[1]==="-="?-1:1)*o+n),j.custom(n,o,p)):j.custom(n,h,""));return!0}var e=f.speed(b,c,d);if(f.isEmptyObject(a))return this.each(e.complete,[!1]);a=f.extend({},a);return e.queue===!1?this.each(g):this.queue(e.queue,g)},stop:function(a,c,d){typeof a!="string"&&(d=c,c=a,a=b),c&&a!==!1&&this.queue(a||"fx",[]);return this.each(function(){function h(a,b,c){var e=b[c];f.removeData(a,c,!0),e.stop(d)}var b,c=!1,e=f.timers,g=f._data(this);d||f._unmark(!0,this);if(a==null)for(b in g)g[b]&&g[b].stop&&b.indexOf(".run")===b.length-4&&h(this,g,b);else g[b=a+".run"]&&g[b].stop&&h(this,g,b);for(b=e.length;b--;)e[b].elem===this&&(a==null||e[b].queue===a)&&(d?e[b](!0):e[b].saveState(),c=!0,e.splice(b,1));(!d||!c)&&f.dequeue(this,a)})}}),f.each({slideDown:ct("show",1),slideUp:ct("hide",1),slideToggle:ct("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){f.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),f.extend({speed:function(a,b,c){var d=a&&typeof a=="object"?f.extend({},a):{complete:c||!c&&b||f.isFunction(a)&&a,duration:a,easing:c&&b||b&&!f.isFunction(b)&&b};d.duration=f.fx.off?0:typeof d.duration=="number"?d.duration:d.duration in f.fx.speeds?f.fx.speeds[d.duration]:f.fx.speeds._default;if(d.queue==null||d.queue===!0)d.queue="fx";d.old=d.complete,d.complete=function(a){f.isFunction(d.old)&&d.old.call(this),d.queue?f.dequeue(this,d.queue):a!==!1&&f._unmark(this)};return d},easing:{linear:function(a){return a},swing:function(a){return-Math.cos(a*Math.PI)/2+.5}},timers:[],fx:function(a,b,c){this.options=b,this.elem=a,this.prop=c,b.orig=b.orig||{}}}),f.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this),(f.fx.step[this.prop]||f.fx.step._default)(this)},cur:function(){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];var a,b=f.css(this.elem,this.prop);return isNaN(a=parseFloat(b))?!b||b==="auto"?0:b:a},custom:function(a,c,d){function h(a){return e.step(a)}var e=this,g=f.fx;this.startTime=cq||cr(),this.end=c,this.now=this.start=a,this.pos=this.state=0,this.unit=d||this.unit||(f.cssNumber[this.prop]?"":"px"),h.queue=this.options.queue,h.elem=this.elem,h.saveState=function(){f._data(e.elem,"fxshow"+e.prop)===b&&(e.options.hide?f._data(e.elem,"fxshow"+e.prop,e.start):e.options.show&&f._data(e.elem,"fxshow"+e.prop,e.end))},h()&&f.timers.push(h)&&!co&&(co=setInterval(g.tick,g.interval))},show:function(){var a=f._data(this.elem,"fxshow"+this.prop);this.options.orig[this.prop]=a||f.style(this.elem,this.prop),this.options.show=!0,a!==b?this.custom(this.cur(),a):this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur()),f(this.elem).show()},hide:function(){this.options.orig[this.prop]=f._data(this.elem,"fxshow"+this.prop)||f.style(this.elem,this.prop),this.options.hide=!0,this.custom(this.cur(),0)},step:function(a){var b,c,d,e=cq||cr(),g=!0,h=this.elem,i=this.options;if(a||e>=i.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),i.animatedProperties[this.prop]=!0;for(b in i.animatedProperties)i.animatedProperties[b]!==!0&&(g=!1);if(g){i.overflow!=null&&!f.support.shrinkWrapBlocks&&f.each(["","X","Y"],function(a,b){h.style["overflow"+b]=i.overflow[a]}),i.hide&&f(h).hide();if(i.hide||i.show)for(b in i.animatedProperties)f.style(h,b,i.orig[b]),f.removeData(h,"fxshow"+b,!0),f.removeData(h,"toggle"+b,!0);d=i.complete,d&&(i.complete=!1,d.call(h))}return!1}i.duration==Infinity?this.now=e:(c=e-this.startTime,this.state=c/i.duration,this.pos=f.easing[i.animatedProperties[this.prop]](this.state,c,0,1,i.duration),this.now=this.start+(this.end-this.start)*this.pos),this.update();return!0}},f.extend(f.fx,{tick:function(){var a,b=f.timers,c=0;for(;c<b.length;c++)a=b[c],!a()&&b[c]===a&&b.splice(c--,1);b.length||f.fx.stop()},interval:13,stop:function(){clearInterval(co),co=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){f.style(a.elem,"opacity",a.now)},_default:function(a){a.elem.style&&a.elem.style[a.prop]!=null?a.elem.style[a.prop]=a.now+a.unit:a.elem[a.prop]=a.now}}}),f.each(cp.concat.apply([],cp),function(a,b){b.indexOf("margin")&&(f.fx.step[b]=function(a){f.style(a.elem,b,Math.max(0,a.now)+a.unit)})}),f.expr&&f.expr.filters&&(f.expr.filters.animated=function(a){return f.grep(f.timers,function(b){return a===b.elem}).length});var cv,cw=/^t(?:able|d|h)$/i,cx=/^(?:body|html)$/i;"getBoundingClientRect"in c.documentElement?cv=function(a,b,c,d){try{d=a.getBoundingClientRect()}catch(e){}if(!d||!f.contains(c,a))return d?{top:d.top,left:d.left}:{top:0,left:0};var g=b.body,h=cy(b),i=c.clientTop||g.clientTop||0,j=c.clientLeft||g.clientLeft||0,k=h.pageYOffset||f.support.boxModel&&c.scrollTop||g.scrollTop,l=h.pageXOffset||f.support.boxModel&&c.scrollLeft||g.scrollLeft,m=d.top+k-i,n=d.left+l-j;return{top:m,left:n}}:cv=function(a,b,c){var d,e=a.offsetParent,g=a,h=b.body,i=b.defaultView,j=i?i.getComputedStyle(a,null):a.currentStyle,k=a.offsetTop,l=a.offsetLeft;while((a=a.parentNode)&&a!==h&&a!==c){if(f.support.fixedPosition&&j.position==="fixed")break;d=i?i.getComputedStyle(a,null):a.currentStyle,k-=a.scrollTop,l-=a.scrollLeft,a===e&&(k+=a.offsetTop,l+=a.offsetLeft,f.support.doesNotAddBorder&&(!f.support.doesAddBorderForTableAndCells||!cw.test(a.nodeName))&&(k+=parseFloat(d.borderTopWidth)||0,l+=parseFloat(d.borderLeftWidth)||0),g=e,e=a.offsetParent),f.support.subtractsBorderForOverflowNotVisible&&d.overflow!=="visible"&&(k+=parseFloat(d.borderTopWidth)||0,l+=parseFloat(d.borderLeftWidth)||0),j=d}if(j.position==="relative"||j.position==="static")k+=h.offsetTop,l+=h.offsetLeft;f.support.fixedPosition&&j.position==="fixed"&&(k+=Math.max(c.scrollTop,h.scrollTop),l+=Math.max(c.scrollLeft,h.scrollLeft));return{top:k,left:l}},f.fn.offset=function(a){if(arguments.length)return a===b?this:this.each(function(b){f.offset.setOffset(this,a,b)});var c=this[0],d=c&&c.ownerDocument;if(!d)return null;if(c===d.body)return f.offset.bodyOffset(c);return cv(c,d,d.documentElement)},f.offset={bodyOffset:function(a){var b=a.offsetTop,c=a.offsetLeft;f.support.doesNotIncludeMarginInBodyOffset&&(b+=parseFloat(f.css(a,"marginTop"))||0,c+=parseFloat(f.css(a,"marginLeft"))||0);return{top:b,left:c}},setOffset:function(a,b,c){var d=f.css(a,"position");d==="static"&&(a.style.position="relative");var e=f(a),g=e.offset(),h=f.css(a,"top"),i=f.css(a,"left"),j=(d==="absolute"||d==="fixed")&&f.inArray("auto",[h,i])>-1,k={},l={},m,n;j?(l=e.position(),m=l.top,n=l.left):(m=parseFloat(h)||0,n=parseFloat(i)||0),f.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):e.css(k)}},f.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),d=cx.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(f.css(a,"marginTop"))||0,c.left-=parseFloat(f.css(a,"marginLeft"))||0,d.top+=parseFloat(f.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(f.css(b[0],"borderLeftWidth"))||0;return{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&!cx.test(a.nodeName)&&f.css(a,"position")==="static")a=a.offsetParent;return a})}}),f.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,c){var d=/Y/.test(c);f.fn[a]=function(e){return f.access(this,function(a,e,g){var h=cy(a);if(g===b)return h?c in h?h[c]:f.support.boxModel&&h.document.documentElement[e]||h.document.body[e]:a[e];h?h.scrollTo(d?f(h).scrollLeft():g,d?g:f(h).scrollTop()):a[e]=g},a,e,arguments.length,null)}}),f.each({Height:"height",Width:"width"},function(a,c){var d="client"+a,e="scroll"+a,g="offset"+a;f.fn["inner"+a]=function(){var a=this[0];return a?a.style?parseFloat(f.css(a,c,"padding")):this[c]():null},f.fn["outer"+a]=function(a){var b=this[0];return b?b.style?parseFloat(f.css(b,c,a?"margin":"border")):this[c]():null},f.fn[c]=function(a){return f.access(this,function(a,c,h){var i,j,k,l;if(f.isWindow(a)){i=a.document,j=i.documentElement[d];return f.support.boxModel&&j||i.body&&i.body[d]||j}if(a.nodeType===9){i=a.documentElement;if(i[d]>=i[e])return i[d];return Math.max(a.body[e],i[e],a.body[g],i[g])}if(h===b){k=f.css(a,c),l=parseFloat(k);return f.isNumeric(l)?l:k}f(a).css(c,h)},c,a,arguments.length,null)}}),a.jQuery=a.$=f,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return f})})(window);
\ No newline at end of file
diff --git a/src/redakcja/static/js/lib/jquery/jquery.autocomplete.js b/src/redakcja/static/js/lib/jquery/jquery.autocomplete.js
new file mode 100644 (file)
index 0000000..83d8f9d
--- /dev/null
@@ -0,0 +1,816 @@
+/*
+ * jQuery Autocomplete plugin 1.1
+ *
+ * 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 15 2009-08-22 10:30:27Z joern.zaefferer $
+ * 
+ * changed by Radek Czajka (FNP), 20100907: ignore alt keydown
+ */
+
+;(function($) {
+
+$.fn.extend({
+       autocomplete: function(urlOrData, options) {
+               var isUrl = typeof urlOrData == "string";
+               options = $.extend({}, $.Autocompleter.defaults, {
+                       url: isUrl ? urlOrData : null,
+                       data: isUrl ? null : urlOrData,
+                       delay: isUrl ? $.Autocompleter.defaults.delay : 10,
+                       max: options && !options.scroll ? 10 : 150
+               }, options);
+
+               // if highlight is set to false, replace it with a do-nothing function
+               options.highlight = options.highlight || function(value) { return value; };
+
+               // if the formatMatch option is not specified, then use formatItem for backwards compatibility
+               options.formatMatch = options.formatMatch || options.formatItem;
+
+               return this.each(function() {
+                       new $.Autocompleter(this, options);
+               });
+       },
+       result: function(handler) {
+               return this.bind("result", handler);
+       },
+       search: function(handler) {
+               return this.trigger("search", [handler]);
+       },
+       flushCache: function() {
+               return this.trigger("flushCache");
+       },
+       setOptions: function(options){
+               return this.trigger("setOptions", [options]);
+       },
+       unautocomplete: function() {
+               return this.trigger("unautocomplete");
+       }
+});
+
+$.Autocompleter = function(input, options) {
+
+       var KEY = {
+               UP: 38,
+               DOWN: 40,
+               DEL: 46,
+               TAB: 9,
+               RETURN: 13,
+               ESC: 27,
+               COMMA: 188,
+               PAGEUP: 33,
+               PAGEDOWN: 34,
+               BACKSPACE: 8
+       };
+
+       // Create $ object for input element
+       var $input = $(input).attr("autocomplete", "off").addClass(options.inputClass);
+
+       var timeout;
+       var previousValue = "";
+       var cache = $.Autocompleter.Cache(options);
+       var hasFocus = 0;
+       var lastKeyPressCode;
+       var config = {
+               mouseDownOnSelect: false
+       };
+       var select = $.Autocompleter.Select(options, input, selectCurrent, config);
+
+       var blockSubmit;
+
+       // prevent form submit in opera when selecting with return key
+       $.browser.opera && $(input.form).bind("submit.autocomplete", function() {
+               if (blockSubmit) {
+                       blockSubmit = false;
+                       return false;
+               }
+       });
+
+       // 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) {
+
+            // changed by fnp
+            case 18:
+            case 17:
+                break;
+            // changed by fnp
+
+                       case KEY.UP:
+                               event.preventDefault();
+                               if ( select.visible() ) {
+                                       select.prev();
+                               } else {
+                                       onChange(0, true);
+                               }
+                               break;
+
+                       case KEY.DOWN:
+                               event.preventDefault();
+                               if ( select.visible() ) {
+                                       select.next();
+                               } else {
+                                       onChange(0, true);
+                               }
+                               break;
+
+                       case KEY.PAGEUP:
+                               event.preventDefault();
+                               if ( select.visible() ) {
+                                       select.pageUp();
+                               } else {
+                                       onChange(0, true);
+                               }
+                               break;
+
+                       case KEY.PAGEDOWN:
+                               event.preventDefault();
+                               if ( select.visible() ) {
+                                       select.pageDown();
+                               } else {
+                                       onChange(0, true);
+                               }
+                               break;
+
+                       // matches also semicolon
+                       case options.multiple && $.trim(options.multipleSeparator) == "," && KEY.COMMA:
+                       case KEY.TAB:
+                       case KEY.RETURN:
+                               if( selectCurrent() ) {
+                                       // stop default to prevent a form submit, Opera needs special handling
+                                       event.preventDefault();
+                                       blockSubmit = true;
+                                       return false;
+                               }
+                               break;
+
+                       case KEY.ESC:
+                               select.hide();
+                               break;
+
+                       default:
+                               clearTimeout(timeout);
+                               timeout = setTimeout(onChange, options.delay);
+                               break;
+               }
+       }).focus(function(){
+               // track whether the field has focus, we shouldn't process any
+               // results if the field no longer has focus
+               hasFocus++;
+       }).blur(function() {
+               hasFocus = 0;
+               if (!config.mouseDownOnSelect) {
+                       hideResults();
+               }
+       }).click(function() {
+               // show select when clicking in a focused field
+               if ( hasFocus++ > 1 && !select.visible() ) {
+                       onChange(0, true);
+               }
+       }).bind("search", function() {
+               // TODO why not just specifying both arguments?
+               var fn = (arguments.length > 1) ? arguments[1] : null;
+               function findValueCallback(q, data) {
+                       var result;
+                       if( data && data.length ) {
+                               for (var i=0; i < data.length; i++) {
+                                       if( data[i].result.toLowerCase() == q.toLowerCase() ) {
+                                               result = data[i];
+                                               break;
+                                       }
+                               }
+                       }
+                       if( typeof fn == "function" ) fn(result);
+                       else $input.trigger("result", result && [result.data, result.value]);
+               }
+               $.each(trimWords($input.val()), function(i, value) {
+                       request(value, findValueCallback, findValueCallback);
+               });
+       }).bind("flushCache", function() {
+               cache.flush();
+       }).bind("setOptions", function() {
+               $.extend(options, arguments[1]);
+               // if we've updated the data, repopulate
+               if ( "data" in arguments[1] )
+                       cache.populate();
+       }).bind("unautocomplete", function() {
+               select.unbind();
+               $input.unbind();
+               $(input.form).unbind(".autocomplete");
+       });
+
+
+       function selectCurrent() {
+               var selected = select.selected();
+               if( !selected )
+                       return false;
+
+               var v = selected.result;
+               previousValue = v;
+
+               if ( options.multiple ) {
+                       var words = trimWords($input.val());
+                       if ( words.length > 1 ) {
+                               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;
+               }
+
+               $input.val(v);
+               hideResultsNow();
+               $input.trigger("result", [selected.data, selected.value]);
+               return true;
+       }
+
+       function onChange(crap, skipPrevCheck) {
+               if( lastKeyPressCode == KEY.DEL ) {
+                       select.hide();
+                       return;
+               }
+
+               var currentValue = $input.val();
+
+               if ( !skipPrevCheck && currentValue == previousValue )
+                       return;
+
+               previousValue = currentValue;
+
+               currentValue = lastWord(currentValue);
+               if ( currentValue.length >= options.minChars) {
+                       $input.addClass(options.loadingClass);
+                       if (!options.matchCase)
+                               currentValue = currentValue.toLowerCase();
+                       request(currentValue, receiveData, hideResultsNow);
+               } else {
+                       stopLoading();
+                       select.hide();
+               }
+       };
+
+       function trimWords(value) {
+               if (!value)
+                       return [""];
+               if (!options.multiple)
+                       return [$.trim(value)];
+               return $.map(value.split(options.multipleSeparator), function(word) {
+                       return $.trim(value).length ? $.trim(word) : null;
+               });
+       }
+
+       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];
+       }
+
+       // fills in the input box w/the first match (assumed to be the best match)
+       // q: the term entered
+       // sValue: the first matching result
+       function autoFill(q, sValue){
+               // autofill in the complete box w/the first match as long as the user hasn't entered in more data
+               // if the last user key pressed was backspace, don't autofill
+               if( options.autoFill && (lastWord($input.val()).toLowerCase() == q.toLowerCase()) && lastKeyPressCode != KEY.BACKSPACE ) {
+                       // 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)
+                       $(input).selection(previousValue.length, previousValue.length + sValue.length);
+               }
+       };
+
+       function hideResults() {
+               clearTimeout(timeout);
+               timeout = setTimeout(hideResultsNow, 200);
+       };
+
+       function hideResultsNow() {
+               var wasVisible = select.visible();
+               select.hide();
+               clearTimeout(timeout);
+               stopLoading();
+               if (options.mustMatch) {
+                       // call search and run callback
+                       $input.search(
+                               function (result){
+                                       // if no value found, clear the input box
+                                       if( !result ) {
+                                               if (options.multiple) {
+                                                       var words = trimWords($input.val()).slice(0, -1);
+                                                       $input.val( words.join(options.multipleSeparator) + (words.length ? options.multipleSeparator : "") );
+                                               }
+                                               else {
+                                                       $input.val( "" );
+                                                       $input.trigger("result", null);
+                                               }
+                                       }
+                               }
+                       );
+               }
+       };
+
+       function receiveData(q, data) {
+               if ( data && data.length && hasFocus ) {
+                       stopLoading();
+                       select.display(data, q);
+                       autoFill(q, data[0].value);
+                       select.show();
+               } else {
+                       hideResultsNow();
+               }
+       };
+
+       function request(term, success, failure) {
+               if (!options.matchCase)
+                       term = term.toLowerCase();
+               var data = cache.load(term);
+               // recieve the cached data
+               if (data && data.length) {
+                       success(term, data);
+               // if an AJAX url has been supplied, try loading the data now
+               } else if( (typeof options.url == "string") && (options.url.length > 0) ){
+
+                       var extraParams = {
+                               timestamp: +new Date()
+                       };
+                       $.each(options.extraParams, function(key, param) {
+                               extraParams[key] = typeof param == "function" ? param() : param;
+                       });
+
+                       $.ajax({
+                               // try to leverage ajaxQueue plugin to abort previous requests
+                               mode: "abort",
+                               // limit abortion to this input
+                               port: "autocomplete" + input.name,
+                               dataType: options.dataType,
+                               url: options.url,
+                               data: $.extend({
+                                       q: lastWord(term),
+                                       limit: options.max
+                               }, extraParams),
+                               success: function(data) {
+                                       var parsed = options.parse && options.parse(data) || parse(data);
+                                       cache.add(term, parsed);
+                                       success(term, parsed);
+                               }
+                       });
+               } else {
+                       // if we have a failure, we need to empty the list -- this prevents the the [TAB] key from selecting the last successful match
+                       select.emptyList();
+                       failure(term);
+               }
+       };
+
+       function parse(data) {
+               var parsed = [];
+               var rows = data.split("\n");
+               for (var i=0; i < rows.length; i++) {
+                       var row = $.trim(rows[i]);
+                       if (row) {
+                               row = row.split("|");
+                               parsed[parsed.length] = {
+                                       data: row,
+                                       value: row[0],
+                                       result: options.formatResult && options.formatResult(row, row[0]) || row[0]
+                               };
+                       }
+               }
+               return parsed;
+       };
+
+       function stopLoading() {
+               $input.removeClass(options.loadingClass);
+       };
+
+};
+
+$.Autocompleter.defaults = {
+       inputClass: "ac_input",
+       resultsClass: "ac_results",
+       loadingClass: "ac_loading",
+       minChars: 1,
+       delay: 400,
+       matchCase: false,
+       matchSubset: true,
+       matchContains: false,
+       cacheLength: 10,
+       max: 100,
+       mustMatch: false,
+       extraParams: {},
+       selectFirst: true,
+       formatItem: function(row) { return row[0]; },
+       formatMatch: null,
+       autoFill: false,
+       width: 0,
+       multiple: false,
+       multipleSeparator: ", ",
+       highlight: function(value, term) {
+               return value.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + term.replace(/([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi, "\\$1") + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "<strong>$1</strong>");
+       },
+    scroll: true,
+    scrollHeight: 180
+};
+
+$.Autocompleter.Cache = function(options) {
+
+       var data = {};
+       var length = 0;
+
+       function matchSubset(s, sub) {
+               if (!options.matchCase)
+                       s = s.toLowerCase();
+               var i = s.indexOf(sub);
+               if (options.matchContains == "word"){
+                       i = s.toLowerCase().search("\\b" + sub.toLowerCase());
+               }
+               if (i == -1) return false;
+               return i == 0 || options.matchContains;
+       };
+
+       function add(q, value) {
+               if (length > options.cacheLength){
+                       flush();
+               }
+               if (!data[q]){
+                       length++;
+               }
+               data[q] = value;
+       }
+
+       function populate(){
+               if( !options.data ) return false;
+               // track the matches
+               var stMatchSets = {},
+                       nullData = 0;
+
+               // no url was specified, we need to adjust the cache length to make sure it fits the local data store
+               if( !options.url ) options.cacheLength = 1;
+
+               // track all options for minChars = 0
+               stMatchSets[""] = [];
+
+               // loop through the array and create a lookup structure
+               for ( var i = 0, ol = options.data.length; i < ol; i++ ) {
+                       var rawValue = options.data[i];
+                       // if rawValue is a string, make an array otherwise just reference the array
+                       rawValue = (typeof rawValue == "string") ? [rawValue] : rawValue;
+
+                       var value = options.formatMatch(rawValue, i+1, options.data.length);
+                       if ( value === false )
+                               continue;
+
+                       var firstChar = value.charAt(0).toLowerCase();
+                       // if no lookup array for this character exists, look it up now
+                       if( !stMatchSets[firstChar] )
+                               stMatchSets[firstChar] = [];
+
+                       // if the match is a string
+                       var row = {
+                               value: value,
+                               data: rawValue,
+                               result: options.formatResult && options.formatResult(rawValue) || value
+                       };
+
+                       // push the current match into the set list
+                       stMatchSets[firstChar].push(row);
+
+                       // keep track of minChars zero items
+                       if ( nullData++ < options.max ) {
+                               stMatchSets[""].push(row);
+                       }
+               };
+
+               // add the data items to the cache
+               $.each(stMatchSets, function(i, value) {
+                       // increase the cache size
+                       options.cacheLength++;
+                       // add to the cache
+                       add(i, value);
+               });
+       }
+
+       // populate any existing data
+       setTimeout(populate, 25);
+
+       function flush(){
+               data = {};
+               length = 0;
+       }
+
+       return {
+               flush: flush,
+               add: add,
+               populate: populate,
+               load: function(q) {
+                       if (!options.cacheLength || !length)
+                               return null;
+                       /*
+                        * if dealing w/local data and matchContains than we must make sure
+                        * to loop through all the data collections looking for matches
+                        */
+                       if( !options.url && options.matchContains ){
+                               // track all matches
+                               var csub = [];
+                               // loop through all the data grids for matches
+                               for( var k in data ){
+                                       // don't search through the stMatchSets[""] (minChars: 0) cache
+                                       // this prevents duplicates
+                                       if( k.length > 0 ){
+                                               var c = data[k];
+                                               $.each(c, function(i, x) {
+                                                       // if we've got a match, add it to the array
+                                                       if (matchSubset(x.value, q)) {
+                                                               csub.push(x);
+                                                       }
+                                               });
+                                       }
+                               }
+                               return csub;
+                       } else
+                       // if the exact item exists, use it
+                       if (data[q]){
+                               return data[q];
+                       } else
+                       if (options.matchSubset) {
+                               for (var i = q.length - 1; i >= options.minChars; i--) {
+                                       var c = data[q.substr(0, i)];
+                                       if (c) {
+                                               var csub = [];
+                                               $.each(c, function(i, x) {
+                                                       if (matchSubset(x.value, q)) {
+                                                               csub[csub.length] = x;
+                                                       }
+                                               });
+                                               return csub;
+                                       }
+                               }
+                       }
+                       return null;
+               }
+       };
+};
+
+$.Autocompleter.Select = function (options, input, select, config) {
+       var CLASSES = {
+               ACTIVE: "ac_over"
+       };
+
+       var listItems,
+               active = -1,
+               data,
+               term = "",
+               needsInit = true,
+               element,
+               list;
+
+       // Create results
+       function init() {
+               if (!needsInit)
+                       return;
+               element = $("<div/>")
+               .hide()
+               .addClass(options.resultsClass)
+               .css("position", "absolute")
+               .appendTo(document.body);
+
+               list = $("<ul/>").appendTo(element).mouseover( function(event) {
+                       if(target(event).nodeName && target(event).nodeName.toUpperCase() == 'LI') {
+                   active = $("li", list).removeClass(CLASSES.ACTIVE).index(target(event));
+                           $(target(event)).addClass(CLASSES.ACTIVE);
+               }
+               }).click(function(event) {
+                       $(target(event)).addClass(CLASSES.ACTIVE);
+                       select();
+                       // TODO provide option to avoid setting focus again after selection? useful for cleanup-on-focus
+                       input.focus();
+                       return false;
+               }).mousedown(function() {
+                       config.mouseDownOnSelect = true;
+               }).mouseup(function() {
+                       config.mouseDownOnSelect = false;
+               });
+
+               if( options.width > 0 )
+                       element.css("width", options.width);
+
+               needsInit = false;
+       }
+
+       function target(event) {
+               var element = event.target;
+               while(element && element.tagName != "LI")
+                       element = element.parentNode;
+               // more fun with IE, sometimes event.target is empty, just ignore it then
+               if(!element)
+                       return [];
+               return element;
+       }
+
+       function moveSelect(step) {
+               listItems.slice(active, active + 1).removeClass(CLASSES.ACTIVE);
+               movePosition(step);
+        var activeItem = listItems.slice(active, active + 1).addClass(CLASSES.ACTIVE);
+        if(options.scroll) {
+            var offset = 0;
+            listItems.slice(0, active).each(function() {
+                               offset += this.offsetHeight;
+                       });
+            if((offset + activeItem[0].offsetHeight - list.scrollTop()) > list[0].clientHeight) {
+                list.scrollTop(offset + activeItem[0].offsetHeight - list.innerHeight());
+            } else if(offset < list.scrollTop()) {
+                list.scrollTop(offset);
+            }
+        }
+       };
+
+       function movePosition(step) {
+               active += step;
+               if (active < 0) {
+                       active = listItems.size() - 1;
+               } else if (active >= listItems.size()) {
+                       active = 0;
+               }
+       }
+
+       function limitNumberOfItems(available) {
+               return options.max && options.max < available
+                       ? options.max
+                       : available;
+       }
+
+       function fillList() {
+               list.empty();
+               var max = limitNumberOfItems(data.length);
+               for (var i=0; i < max; i++) {
+                       if (!data[i])
+                               continue;
+                       var formatted = options.formatItem(data[i].data, i+1, max, data[i].value, term);
+                       if ( formatted === false )
+                               continue;
+                       var li = $("<li/>").html( options.highlight(formatted, term) ).addClass(i%2 == 0 ? "ac_even" : "ac_odd").appendTo(list)[0];
+                       $.data(li, "ac_data", data[i]);
+               }
+               listItems = list.find("li");
+               if ( options.selectFirst ) {
+                       listItems.slice(0, 1).addClass(CLASSES.ACTIVE);
+                       active = 0;
+               }
+               // apply bgiframe if available
+               if ( $.fn.bgiframe )
+                       list.bgiframe();
+       }
+
+       return {
+               display: function(d, q) {
+                       init();
+                       data = d;
+                       term = q;
+                       fillList();
+               },
+               next: function() {
+                       moveSelect(1);
+               },
+               prev: function() {
+                       moveSelect(-1);
+               },
+               pageUp: function() {
+                       if (active != 0 && active - 8 < 0) {
+                               moveSelect( -active );
+                       } else {
+                               moveSelect(-8);
+                       }
+               },
+               pageDown: function() {
+                       if (active != listItems.size() - 1 && active + 8 > listItems.size()) {
+                               moveSelect( listItems.size() - 1 - active );
+                       } else {
+                               moveSelect(8);
+                       }
+               },
+               hide: function() {
+                       element && element.hide();
+                       listItems && listItems.removeClass(CLASSES.ACTIVE);
+                       active = -1;
+               },
+               visible : function() {
+                       return element && element.is(":visible");
+               },
+               current: function() {
+                       return this.visible() && (listItems.filter("." + CLASSES.ACTIVE)[0] || options.selectFirst && listItems[0]);
+               },
+               show: function() {
+                       var offset = $(input).offset();
+                       element.css({
+                               width: typeof options.width == "string" || options.width > 0 ? options.width : $(input).width(),
+                               top: offset.top + input.offsetHeight,
+                               left: offset.left
+                       }).show();
+            if(options.scroll) {
+                list.scrollTop(0);
+                list.css({
+                                       maxHeight: options.scrollHeight,
+                                       overflow: 'auto'
+                               });
+
+                if($.browser.msie && typeof document.body.style.maxHeight === "undefined") {
+                                       var listHeight = 0;
+                                       listItems.each(function() {
+                                               listHeight += this.offsetHeight;
+                                       });
+                                       var scrollbarsVisible = listHeight > options.scrollHeight;
+                    list.css('height', scrollbarsVisible ? options.scrollHeight : listHeight );
+                                       if (!scrollbarsVisible) {
+                                               // IE doesn't recalculate width when scrollbar disappears
+                                               listItems.width( list.width() - parseInt(listItems.css("padding-left")) - parseInt(listItems.css("padding-right")) );
+                                       }
+                }
+
+            }
+               },
+               selected: function() {
+                       var selected = listItems && listItems.filter("." + CLASSES.ACTIVE).removeClass(CLASSES.ACTIVE);
+                       return selected && selected.length && $.data(selected[0], "ac_data");
+               },
+               emptyList: function (){
+                       list && list.empty();
+               },
+               unbind: function() {
+                       element && element.remove();
+               }
+       };
+};
+
+$.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
+               }
+       }
+};
+
+})(jQuery);
\ No newline at end of file
diff --git a/src/redakcja/static/js/lib/jquery/jquery.blockui.js b/src/redakcja/static/js/lib/jquery/jquery.blockui.js
new file mode 100644 (file)
index 0000000..5491667
--- /dev/null
@@ -0,0 +1,477 @@
+/*!
+ * jQuery blockUI plugin
+ * Version 2.31 (06-JAN-2010)
+ * @requires jQuery v1.2.3 or later
+ *
+ * Examples at: http://malsup.com/jquery/block/
+ * Copyright (c) 2007-2008 M. Alsup
+ * Dual licensed under the MIT and GPL licenses:
+ * http://www.opensource.org/licenses/mit-license.php
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * Thanks to Amir-Hossein Sobhi for some excellent contributions!
+ */
+
+;(function($) {
+
+if (/1\.(0|1|2)\.(0|1|2)/.test($.fn.jquery) || /^1.1/.test($.fn.jquery)) {
+       alert('blockUI requires jQuery v1.2.3 or later!  You are using v' + $.fn.jquery);
+       return;
+}
+
+$.fn._fadeIn = $.fn.fadeIn;
+
+var noOp = function() {};
+
+// this bit is to ensure we don't call setExpression when we shouldn't (with extra muscle to handle
+// retarded userAgent strings on Vista)
+var mode = document.documentMode || 0;
+var setExpr = $.browser.msie && (($.browser.version < 8 && !mode) || mode < 8);
+var ie6 = $.browser.msie && /MSIE 6.0/.test(navigator.userAgent) && !mode;
+
+// global $ methods for blocking/unblocking the entire page
+$.blockUI   = function(opts) { install(window, opts); };
+$.unblockUI = function(opts) { remove(window, opts); };
+
+// convenience method for quick growl-like notifications  (http://www.google.com/search?q=growl)
+$.growlUI = function(title, message, timeout, onClose) {
+       var $m = $('<div class="growlUI"></div>');
+       if (title) $m.append('<h1>'+title+'</h1>');
+       if (message) $m.append('<h2>'+message+'</h2>');
+       if (timeout == undefined) timeout = 3000;
+       $.blockUI({
+               message: $m, fadeIn: 700, fadeOut: 1000, centerY: false,
+               timeout: timeout, showOverlay: false,
+               onUnblock: onClose,
+               css: $.blockUI.defaults.growlCSS
+       });
+};
+
+// plugin method for blocking element content
+$.fn.block = function(opts) {
+       return this.unblock({ fadeOut: 0 }).each(function() {
+               if ($.css(this,'position') == 'static')
+                       this.style.position = 'relative';
+               if ($.browser.msie)
+                       this.style.zoom = 1; // force 'hasLayout'
+               install(this, opts);
+       });
+};
+
+// plugin method for unblocking element content
+$.fn.unblock = function(opts) {
+       return this.each(function() {
+               remove(this, opts);
+       });
+};
+
+$.blockUI.version = 2.31; // 2nd generation blocking at no extra cost!
+
+// override these in your code to change the default behavior and style
+$.blockUI.defaults = {
+       // message displayed when blocking (use null for no message)
+       message:  '<h1>Please wait...</h1>',
+
+       title: null,      // title string; only used when theme == true
+       draggable: true,  // only used when theme == true (requires jquery-ui.js to be loaded)
+
+       theme: false, // set to true to use with jQuery UI themes
+
+       // styles for the message when blocking; if you wish to disable
+       // these and use an external stylesheet then do this in your code:
+       // $.blockUI.defaults.css = {};
+       css: {
+               padding:        0,
+               margin:         0,
+               width:          '30%',
+               top:            '40%',
+               left:           '35%',
+               textAlign:      'center',
+               color:          '#000',
+               border:         '3px solid #aaa',
+               backgroundColor:'#fff',
+               cursor:         'wait'
+       },
+
+       // minimal style set used when themes are used
+       themedCSS: {
+               width:  '30%',
+               top:    '40%',
+               left:   '35%'
+       },
+
+       // styles for the overlay
+       overlayCSS:  {
+               backgroundColor: '#000',
+               opacity:                 0.6,
+               cursor:                  'wait'
+       },
+
+       // styles applied when using $.growlUI
+       growlCSS: {
+               width:          '350px',
+               top:            '10px',
+               left:           '',
+               right:          '10px',
+               border:         'none',
+               padding:        '5px',
+               opacity:        0.6,
+               cursor:         'default',
+               color:          '#fff',
+               backgroundColor: '#000',
+               '-webkit-border-radius': '10px',
+               '-moz-border-radius':    '10px'
+       },
+
+       // IE issues: 'about:blank' fails on HTTPS and javascript:false is s-l-o-w
+       // (hat tip to Jorge H. N. de Vasconcelos)
+       iframeSrc: /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank',
+
+       // force usage of iframe in non-IE browsers (handy for blocking applets)
+       forceIframe: false,
+
+       // z-index for the blocking overlay
+       baseZ: 1000,
+
+       // set these to true to have the message automatically centered
+       centerX: true, // <-- only effects element blocking (page block controlled via css above)
+       centerY: true,
+
+       // allow body element to be stetched in ie6; this makes blocking look better
+       // on "short" pages.  disable if you wish to prevent changes to the body height
+       allowBodyStretch: true,
+
+       // enable if you want key and mouse events to be disabled for content that is blocked
+       bindEvents: true,
+
+       // be default blockUI will supress tab navigation from leaving blocking content
+       // (if bindEvents is true)
+       constrainTabKey: true,
+
+       // fadeIn time in millis; set to 0 to disable fadeIn on block
+       fadeIn:  200,
+
+       // fadeOut time in millis; set to 0 to disable fadeOut on unblock
+       fadeOut:  400,
+
+       // time in millis to wait before auto-unblocking; set to 0 to disable auto-unblock
+       timeout: 0,
+
+       // disable if you don't want to show the overlay
+       showOverlay: true,
+
+       // if true, focus will be placed in the first available input field when
+       // page blocking
+       focusInput: true,
+
+       // suppresses the use of overlay styles on FF/Linux (due to performance issues with opacity)
+       applyPlatformOpacityRules: true,
+
+       // callback method invoked when fadeIn has completed and blocking message is visible
+       onBlock: null,
+
+       // callback method invoked when unblocking has completed; the callback is
+       // passed the element that has been unblocked (which is the window object for page
+       // blocks) and the options that were passed to the unblock call:
+       //       onUnblock(element, options)
+       onUnblock: null,
+
+       // don't ask; if you really must know: http://groups.google.com/group/jquery-en/browse_thread/thread/36640a8730503595/2f6a79a77a78e493#2f6a79a77a78e493
+       quirksmodeOffsetHack: 4
+};
+
+// private data and functions follow...
+
+var pageBlock = null;
+var pageBlockEls = [];
+
+function install(el, opts) {
+       var full = (el == window);
+       var msg = opts && opts.message !== undefined ? opts.message : undefined;
+       opts = $.extend({}, $.blockUI.defaults, opts || {});
+       opts.overlayCSS = $.extend({}, $.blockUI.defaults.overlayCSS, opts.overlayCSS || {});
+       var css = $.extend({}, $.blockUI.defaults.css, opts.css || {});
+       var themedCSS = $.extend({}, $.blockUI.defaults.themedCSS, opts.themedCSS || {});
+       msg = msg === undefined ? opts.message : msg;
+
+       // remove the current block (if there is one)
+       if (full && pageBlock)
+               remove(window, {fadeOut:0});
+
+       // if an existing element is being used as the blocking content then we capture
+       // its current place in the DOM (and current display style) so we can restore
+       // it when we unblock
+       if (msg && typeof msg != 'string' && (msg.parentNode || msg.jquery)) {
+               var node = msg.jquery ? msg[0] : msg;
+               var data = {};
+               $(el).data('blockUI.history', data);
+               data.el = node;
+               data.parent = node.parentNode;
+               data.display = node.style.display;
+               data.position = node.style.position;
+               if (data.parent)
+                       data.parent.removeChild(node);
+       }
+
+       var z = opts.baseZ;
+
+       // blockUI uses 3 layers for blocking, for simplicity they are all used on every platform;
+       // layer1 is the iframe layer which is used to supress bleed through of underlying content
+       // layer2 is the overlay layer which has opacity and a wait cursor (by default)
+       // layer3 is the message content that is displayed while blocking
+
+       var lyr1 = ($.browser.msie || opts.forceIframe)
+               ? $('<iframe class="blockUI" style="z-index:'+ (z++) +';display:none;border:none;margin:0;padding:0;position:absolute;width:100%;height:100%;top:0;left:0" src="'+opts.iframeSrc+'"></iframe>')
+               : $('<div class="blockUI" style="display:none"></div>');
+       var lyr2 = $('<div class="blockUI blockOverlay" style="z-index:'+ (z++) +';display:none;border:none;margin:0;padding:0;width:100%;height:100%;top:0;left:0"></div>');
+
+       var lyr3;
+       if (opts.theme && full) {
+               var s = '<div class="blockUI blockMsg blockPage ui-dialog ui-widget ui-corner-all" style="z-index:'+z+';display:none;position:fixed">' +
+                                       '<div class="ui-widget-header ui-dialog-titlebar blockTitle">'+(opts.title || '&nbsp;')+'</div>' +
+                                       '<div class="ui-widget-content ui-dialog-content"></div>' +
+                               '</div>';
+               lyr3 = $(s);
+       }
+       else {
+               lyr3 = full ? $('<div class="blockUI blockMsg blockPage" style="z-index:'+z+';display:none;position:fixed"></div>')
+                                       : $('<div class="blockUI blockMsg blockElement" style="z-index:'+z+';display:none;position:absolute"></div>');
+       }
+
+       // if we have a message, style it
+       if (msg) {
+               if (opts.theme) {
+                       lyr3.css(themedCSS);
+                       lyr3.addClass('ui-widget-content');
+               }
+               else
+                       lyr3.css(css);
+       }
+
+       // style the overlay
+       if (!opts.applyPlatformOpacityRules || !($.browser.mozilla && /Linux/.test(navigator.platform)))
+               lyr2.css(opts.overlayCSS);
+       lyr2.css('position', full ? 'fixed' : 'absolute');
+
+       // make iframe layer transparent in IE
+       if ($.browser.msie || opts.forceIframe)
+               lyr1.css('opacity',0.0);
+
+       //$([lyr1[0],lyr2[0],lyr3[0]]).appendTo(full ? 'body' : el);
+       var layers = [lyr1,lyr2,lyr3], $par = full ? $('body') : $(el);
+       $.each(layers, function() {
+               this.appendTo($par);
+       });
+
+       if (opts.theme && opts.draggable && $.fn.draggable) {
+               lyr3.draggable({
+                       handle: '.ui-dialog-titlebar',
+                       cancel: 'li'
+               });
+       }
+
+       // ie7 must use absolute positioning in quirks mode and to account for activex issues (when scrolling)
+       var expr = setExpr && (!$.boxModel || $('object,embed', full ? null : el).length > 0);
+       if (ie6 || expr) {
+               // give body 100% height
+               if (full && opts.allowBodyStretch && $.boxModel)
+                       $('html,body').css('height','100%');
+
+               // fix ie6 issue when blocked element has a border width
+               if ((ie6 || !$.boxModel) && !full) {
+                       var t = sz(el,'borderTopWidth'), l = sz(el,'borderLeftWidth');
+                       var fixT = t ? '(0 - '+t+')' : 0;
+                       var fixL = l ? '(0 - '+l+')' : 0;
+               }
+
+               // simulate fixed position
+               $.each([lyr1,lyr2,lyr3], function(i,o) {
+                       var s = o[0].style;
+                       s.position = 'absolute';
+                       if (i < 2) {
+                               full ? s.setExpression('height','Math.max(document.body.scrollHeight, document.body.offsetHeight) - (jQuery.boxModel?0:'+opts.quirksmodeOffsetHack+') + "px"')
+                                        : s.setExpression('height','this.parentNode.offsetHeight + "px"');
+                               full ? s.setExpression('width','jQuery.boxModel && document.documentElement.clientWidth || document.body.clientWidth + "px"')
+                                        : s.setExpression('width','this.parentNode.offsetWidth + "px"');
+                               if (fixL) s.setExpression('left', fixL);
+                               if (fixT) s.setExpression('top', fixT);
+                       }
+                       else if (opts.centerY) {
+                               if (full) s.setExpression('top','(document.documentElement.clientHeight || document.body.clientHeight) / 2 - (this.offsetHeight / 2) + (blah = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop) + "px"');
+                               s.marginTop = 0;
+                       }
+                       else if (!opts.centerY && full) {
+                               var top = (opts.css && opts.css.top) ? parseInt(opts.css.top) : 0;
+                               var expression = '((document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop) + '+top+') + "px"';
+                               s.setExpression('top',expression);
+                       }
+               });
+       }
+
+       // show the message
+       if (msg) {
+               if (opts.theme)
+                       lyr3.find('.ui-widget-content').append(msg);
+               else
+                       lyr3.append(msg);
+               if (msg.jquery || msg.nodeType)
+                       $(msg).show();
+       }
+
+       if (($.browser.msie || opts.forceIframe) && opts.showOverlay)
+               lyr1.show(); // opacity is zero
+       if (opts.fadeIn) {
+               var cb = opts.onBlock ? opts.onBlock : noOp;
+               var cb1 = (opts.showOverlay && !msg) ? cb : noOp;
+               var cb2 = msg ? cb : noOp;
+               if (opts.showOverlay)
+                       lyr2._fadeIn(opts.fadeIn, cb1);
+               if (msg)
+                       lyr3._fadeIn(opts.fadeIn, cb2);
+       }
+       else {
+               if (opts.showOverlay)
+                       lyr2.show();
+               if (msg)
+                       lyr3.show();
+               if (opts.onBlock)
+                       opts.onBlock();
+       }
+
+       // bind key and mouse events
+       bind(1, el, opts);
+
+       if (full) {
+               pageBlock = lyr3[0];
+               pageBlockEls = $(':input:enabled:visible',pageBlock);
+               if (opts.focusInput)
+                       setTimeout(focus, 20);
+       }
+       else
+               center(lyr3[0], opts.centerX, opts.centerY);
+
+       if (opts.timeout) {
+               // auto-unblock
+               var to = setTimeout(function() {
+                       full ? $.unblockUI(opts) : $(el).unblock(opts);
+               }, opts.timeout);
+               $(el).data('blockUI.timeout', to);
+       }
+};
+
+// remove the block
+function remove(el, opts) {
+       var full = (el == window);
+       var $el = $(el);
+       var data = $el.data('blockUI.history');
+       var to = $el.data('blockUI.timeout');
+       if (to) {
+               clearTimeout(to);
+               $el.removeData('blockUI.timeout');
+       }
+       opts = $.extend({}, $.blockUI.defaults, opts || {});
+       bind(0, el, opts); // unbind events
+
+       var els;
+       if (full) // crazy selector to handle odd field errors in ie6/7
+               els = $('body').children().filter('.blockUI').add('body > .blockUI');
+       else
+               els = $('.blockUI', el);
+
+       if (full)
+               pageBlock = pageBlockEls = null;
+
+       if (opts.fadeOut) {
+               els.fadeOut(opts.fadeOut);
+               setTimeout(function() { reset(els,data,opts,el); }, opts.fadeOut);
+       }
+       else
+               reset(els, data, opts, el);
+};
+
+// move blocking element back into the DOM where it started
+function reset(els,data,opts,el) {
+       els.each(function(i,o) {
+               // remove via DOM calls so we don't lose event handlers
+               if (this.parentNode)
+                       this.parentNode.removeChild(this);
+       });
+
+       if (data && data.el) {
+               data.el.style.display = data.display;
+               data.el.style.position = data.position;
+               if (data.parent)
+                       data.parent.appendChild(data.el);
+               $(el).removeData('blockUI.history');
+       }
+
+       if (typeof opts.onUnblock == 'function')
+               opts.onUnblock(el,opts);
+};
+
+// bind/unbind the handler
+function bind(b, el, opts) {
+       var full = el == window, $el = $(el);
+
+       // don't bother unbinding if there is nothing to unbind
+       if (!b && (full && !pageBlock || !full && !$el.data('blockUI.isBlocked')))
+               return;
+       if (!full)
+               $el.data('blockUI.isBlocked', b);
+
+       // don't bind events when overlay is not in use or if bindEvents is false
+       if (!opts.bindEvents || (b && !opts.showOverlay))
+               return;
+
+       // bind anchors and inputs for mouse and key events
+       var events = 'mousedown mouseup keydown keypress';
+       b ? $(document).bind(events, opts, handler) : $(document).unbind(events, handler);
+
+// former impl...
+//        var $e = $('a,:input');
+//        b ? $e.bind(events, opts, handler) : $e.unbind(events, handler);
+};
+
+// event handler to suppress keyboard/mouse events when blocking
+function handler(e) {
+       // allow tab navigation (conditionally)
+       if (e.keyCode && e.keyCode == 9) {
+               if (pageBlock && e.data.constrainTabKey) {
+                       var els = pageBlockEls;
+                       var fwd = !e.shiftKey && e.target == els[els.length-1];
+                       var back = e.shiftKey && e.target == els[0];
+                       if (fwd || back) {
+                               setTimeout(function(){focus(back)},10);
+                               return false;
+                       }
+               }
+       }
+       // allow events within the message content
+       if ($(e.target).parents('div.blockMsg').length > 0)
+               return true;
+
+       // allow events for content that is not being blocked
+       return $(e.target).parents().children().filter('div.blockUI').length == 0;
+};
+
+function focus(back) {
+       if (!pageBlockEls)
+               return;
+       var e = pageBlockEls[back===true ? pageBlockEls.length-1 : 0];
+       if (e)
+               e.focus();
+};
+
+function center(el, x, y) {
+       var p = el.parentNode, s = el.style;
+       var l = ((p.offsetWidth - el.offsetWidth)/2) - sz(p,'borderLeftWidth');
+       var t = ((p.offsetHeight - el.offsetHeight)/2) - sz(p,'borderTopWidth');
+       if (x) s.left = l > 0 ? (l+'px') : '0';
+       if (y) s.top  = t > 0 ? (t+'px') : '0';
+};
+
+function sz(el, p) {
+       return parseInt($.css(el,p))||0;
+};
+
+})(jQuery);
\ No newline at end of file
diff --git a/src/redakcja/static/js/lib/jquery/jquery.elastic.js b/src/redakcja/static/js/lib/jquery/jquery.elastic.js
new file mode 100644 (file)
index 0000000..24e16f4
--- /dev/null
@@ -0,0 +1,6 @@
+(function(jQuery){jQuery.fn.extend({elastic:function(){var mimics=['paddingTop','paddingRight','paddingBottom','paddingLeft','fontSize','lineHeight','fontFamily','width','fontWeight'];return this.each(function(){if(this.type!='textarea'){return false;}
+var $textarea=jQuery(this),$twin=jQuery('<div />').css({'position':'absolute','display':'none','word-wrap':'break-word'}),lineHeight=parseInt($textarea.css('line-height'),10)||parseInt($textarea.css('font-size'),'10'),minheight=parseInt($textarea.css('height'),10)||lineHeight*3,maxheight=parseInt($textarea.css('max-height'),10)||Number.MAX_VALUE,goalheight=0,i=0;if(maxheight<0){maxheight=Number.MAX_VALUE;}
+$twin.appendTo($textarea.parent());var i=mimics.length;while(i--){$twin.css(mimics[i].toString(),$textarea.css(mimics[i].toString()));}
+function setHeightAndOverflow(height,overflow){curratedHeight=Math.floor(parseInt(height,10));if($textarea.height()!=curratedHeight){$textarea.css({'height':curratedHeight+'px','overflow':overflow});}}
+function update(){var textareaContent=$textarea.val().replace(/&/g,'&amp;').replace(/  /g,'&nbsp;').replace(/<|>/g,'&gt;').replace(/\n/g,'<br />');var twinContent=$twin.html();if(textareaContent+'&nbsp;'!=twinContent){$twin.html(textareaContent+'&nbsp;');if(Math.abs($twin.height()+lineHeight-$textarea.height())>3){var goalheight=$twin.height()+lineHeight;if(goalheight>=maxheight){setHeightAndOverflow(maxheight,'auto');}else if(goalheight<=minheight){setHeightAndOverflow(minheight,'hidden');}else{setHeightAndOverflow(goalheight,'hidden');}}}}
+$textarea.css({'overflow':'hidden'});$textarea.keyup(function(){update();});$textarea.live('input paste',function(e){setTimeout(update,250);});update();});}});})(jQuery);
\ No newline at end of file
diff --git a/src/redakcja/static/js/lib/jquery/jquery.imgareaselect.js b/src/redakcja/static/js/lib/jquery/jquery.imgareaselect.js
new file mode 100644 (file)
index 0000000..7e1b790
--- /dev/null
@@ -0,0 +1,730 @@
+/*
+ * imgAreaSelect jQuery plugin
+ * version 0.9.10
+ *
+ * Copyright (c) 2008-2013 Michal Wojciechowski (odyniec.net)
+ *
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
+ * and GPL (GPL-LICENSE.txt) licenses.
+ *
+ * http://odyniec.net/projects/imgareaselect/
+ *
+ */
+
+(function($) {
+
+var abs = Math.abs,
+    max = Math.max,
+    min = Math.min,
+    round = Math.round;
+
+function div() {
+    return $('<div/>');
+}
+
+$.imgAreaSelect = function (img, options) {
+    var
+
+        $img = $(img),
+
+        imgLoaded,
+
+        $box = div(),
+        $area = div(),
+        $border = div().add(div()).add(div()).add(div()),
+        $outer = div().add(div()).add(div()).add(div()),
+        $handles = $([]),
+
+        $areaOpera,
+
+        left, top,
+
+        imgOfs = { left: 0, top: 0 },
+
+        imgWidth, imgHeight,
+
+        $parent,
+
+        parOfs = { left: 0, top: 0 },
+
+        zIndex = 0,
+
+        position = 'absolute',
+
+        startX, startY,
+
+        scaleX, scaleY,
+
+        resize,
+
+        minWidth, minHeight, maxWidth, maxHeight,
+
+        aspectRatio,
+
+        shown,
+
+        x1, y1, x2, y2,
+
+        selection = { x1: 0, y1: 0, x2: 0, y2: 0, width: 0, height: 0 },
+
+        docElem = document.documentElement,
+
+        ua = navigator.userAgent,
+
+        $p, d, i, o, w, h, adjusted;
+
+    function viewX(x) {
+        return x + imgOfs.left - parOfs.left;
+    }
+
+    function viewY(y) {
+        return y + imgOfs.top - parOfs.top;
+    }
+
+    function selX(x) {
+        return x - imgOfs.left + parOfs.left;
+    }
+
+    function selY(y) {
+        return y - imgOfs.top + parOfs.top;
+    }
+
+    function evX(event) {
+        return event.pageX - parOfs.left;
+    }
+
+    function evY(event) {
+        return event.pageY - parOfs.top;
+    }
+
+    function getSelection(noScale) {
+        var sx = noScale || scaleX, sy = noScale || scaleY;
+
+        return { x1: round(selection.x1 * sx),
+            y1: round(selection.y1 * sy),
+            x2: round(selection.x2 * sx),
+            y2: round(selection.y2 * sy),
+            width: round(selection.x2 * sx) - round(selection.x1 * sx),
+            height: round(selection.y2 * sy) - round(selection.y1 * sy) };
+    }
+
+    function setSelection(x1, y1, x2, y2, noScale) {
+        var sx = noScale || scaleX, sy = noScale || scaleY;
+
+        selection = {
+            x1: round(x1 / sx || 0),
+            y1: round(y1 / sy || 0),
+            x2: round(x2 / sx || 0),
+            y2: round(y2 / sy || 0)
+        };
+
+        selection.width = selection.x2 - selection.x1;
+        selection.height = selection.y2 - selection.y1;
+    }
+
+    function adjust() {
+        if (!imgLoaded || !$img.width())
+            return;
+
+        imgOfs = { left: round($img.offset().left), top: round($img.offset().top) };
+
+        imgWidth = $img.innerWidth();
+        imgHeight = $img.innerHeight();
+
+        imgOfs.top += ($img.outerHeight() - imgHeight) >> 1;
+        imgOfs.left += ($img.outerWidth() - imgWidth) >> 1;
+
+        minWidth = round(options.minWidth / scaleX) || 0;
+        minHeight = round(options.minHeight / scaleY) || 0;
+        maxWidth = round(min(options.maxWidth / scaleX || 1<<24, imgWidth));
+        maxHeight = round(min(options.maxHeight / scaleY || 1<<24, imgHeight));
+
+        if ($().jquery == '1.3.2' && position == 'fixed' &&
+            !docElem['getBoundingClientRect'])
+        {
+            imgOfs.top += max(document.body.scrollTop, docElem.scrollTop);
+            imgOfs.left += max(document.body.scrollLeft, docElem.scrollLeft);
+        }
+
+        parOfs = /absolute|relative/.test($parent.css('position')) ?
+            { left: round($parent.offset().left) - $parent.scrollLeft(),
+                top: round($parent.offset().top) - $parent.scrollTop() } :
+            position == 'fixed' ?
+                { left: $(document).scrollLeft(), top: $(document).scrollTop() } :
+                { left: 0, top: 0 };
+
+        left = viewX(0);
+        top = viewY(0);
+
+        if (selection.x2 > imgWidth || selection.y2 > imgHeight)
+            doResize();
+    }
+
+    function update(resetKeyPress) {
+        if (!shown) return;
+
+        $box.css({ left: viewX(selection.x1), top: viewY(selection.y1) })
+            .add($area).width(w = selection.width).height(h = selection.height);
+
+        $area.add($border).add($handles).css({ left: 0, top: 0 });
+
+        $border
+            .width(max(w - $border.outerWidth() + $border.innerWidth(), 0))
+            .height(max(h - $border.outerHeight() + $border.innerHeight(), 0));
+
+        $($outer[0]).css({ left: left, top: top,
+            width: selection.x1, height: imgHeight });
+        $($outer[1]).css({ left: left + selection.x1, top: top,
+            width: w, height: selection.y1 });
+        $($outer[2]).css({ left: left + selection.x2, top: top,
+            width: imgWidth - selection.x2, height: imgHeight });
+        $($outer[3]).css({ left: left + selection.x1, top: top + selection.y2,
+            width: w, height: imgHeight - selection.y2 });
+
+        w -= $handles.outerWidth();
+        h -= $handles.outerHeight();
+
+        switch ($handles.length) {
+        case 8:
+            $($handles[4]).css({ left: w >> 1 });
+            $($handles[5]).css({ left: w, top: h >> 1 });
+            $($handles[6]).css({ left: w >> 1, top: h });
+            $($handles[7]).css({ top: h >> 1 });
+        case 4:
+            $handles.slice(1,3).css({ left: w });
+            $handles.slice(2,4).css({ top: h });
+        }
+
+        if (resetKeyPress !== false) {
+            if ($.imgAreaSelect.onKeyPress != docKeyPress)
+                $(document).unbind($.imgAreaSelect.keyPress,
+                    $.imgAreaSelect.onKeyPress);
+
+            if (options.keys)
+                $(document)[$.imgAreaSelect.keyPress](
+                    $.imgAreaSelect.onKeyPress = docKeyPress);
+        }
+
+        if (msie && $border.outerWidth() - $border.innerWidth() == 2) {
+            $border.css('margin', 0);
+            setTimeout(function () { $border.css('margin', 'auto'); }, 0);
+        }
+    }
+
+    function doUpdate(resetKeyPress) {
+        adjust();
+        update(resetKeyPress);
+        x1 = viewX(selection.x1); y1 = viewY(selection.y1);
+        x2 = viewX(selection.x2); y2 = viewY(selection.y2);
+    }
+
+    function hide($elem, fn) {
+        options.fadeSpeed ? $elem.fadeOut(options.fadeSpeed, fn) : $elem.hide();
+
+    }
+
+    function areaMouseMove(event) {
+        var x = selX(evX(event)) - selection.x1,
+            y = selY(evY(event)) - selection.y1;
+
+        if (!adjusted) {
+            adjust();
+            adjusted = true;
+
+            $box.one('mouseout', function () { adjusted = false; });
+        }
+
+        resize = '';
+
+        if (options.resizable) {
+            if (y <= options.resizeMargin)
+                resize = 'n';
+            else if (y >= selection.height - options.resizeMargin)
+                resize = 's';
+            if (x <= options.resizeMargin)
+                resize += 'w';
+            else if (x >= selection.width - options.resizeMargin)
+                resize += 'e';
+        }
+
+        $box.css('cursor', resize ? resize + '-resize' :
+            options.movable ? 'move' : '');
+        if ($areaOpera)
+            $areaOpera.toggle();
+    }
+
+    function docMouseUp(event) {
+        $('body').css('cursor', '');
+        if (options.autoHide || selection.width * selection.height == 0)
+            hide($box.add($outer), function () { $(this).hide(); });
+
+        $(document).unbind('mousemove', selectingMouseMove);
+        $box.mousemove(areaMouseMove);
+
+        options.onSelectEnd(img, getSelection());
+    }
+
+    function areaMouseDown(event) {
+        if (event.which != 1) return false;
+
+        adjust();
+
+        if (resize) {
+            $('body').css('cursor', resize + '-resize');
+
+            x1 = viewX(selection[/w/.test(resize) ? 'x2' : 'x1']);
+            y1 = viewY(selection[/n/.test(resize) ? 'y2' : 'y1']);
+
+            $(document).mousemove(selectingMouseMove)
+                .one('mouseup', docMouseUp);
+            $box.unbind('mousemove', areaMouseMove);
+        }
+        else if (options.movable) {
+            startX = left + selection.x1 - evX(event);
+            startY = top + selection.y1 - evY(event);
+
+            $box.unbind('mousemove', areaMouseMove);
+
+            $(document).mousemove(movingMouseMove)
+                .one('mouseup', function () {
+                    options.onSelectEnd(img, getSelection());
+
+                    $(document).unbind('mousemove', movingMouseMove);
+                    $box.mousemove(areaMouseMove);
+                });
+        }
+        else
+            $img.mousedown(event);
+
+        return false;
+    }
+
+    function fixAspectRatio(xFirst) {
+        if (aspectRatio)
+            if (xFirst) {
+                x2 = max(left, min(left + imgWidth,
+                    x1 + abs(y2 - y1) * aspectRatio * (x2 > x1 || -1)));
+
+                y2 = round(max(top, min(top + imgHeight,
+                    y1 + abs(x2 - x1) / aspectRatio * (y2 > y1 || -1))));
+                x2 = round(x2);
+            }
+            else {
+                y2 = max(top, min(top + imgHeight,
+                    y1 + abs(x2 - x1) / aspectRatio * (y2 > y1 || -1)));
+                x2 = round(max(left, min(left + imgWidth,
+                    x1 + abs(y2 - y1) * aspectRatio * (x2 > x1 || -1))));
+                y2 = round(y2);
+            }
+    }
+
+    function doResize() {
+        x1 = min(x1, left + imgWidth);
+        y1 = min(y1, top + imgHeight);
+
+        if (abs(x2 - x1) < minWidth) {
+            x2 = x1 - minWidth * (x2 < x1 || -1);
+
+            if (x2 < left)
+                x1 = left + minWidth;
+            else if (x2 > left + imgWidth)
+                x1 = left + imgWidth - minWidth;
+        }
+
+        if (abs(y2 - y1) < minHeight) {
+            y2 = y1 - minHeight * (y2 < y1 || -1);
+
+            if (y2 < top)
+                y1 = top + minHeight;
+            else if (y2 > top + imgHeight)
+                y1 = top + imgHeight - minHeight;
+        }
+
+        x2 = max(left, min(x2, left + imgWidth));
+        y2 = max(top, min(y2, top + imgHeight));
+
+        fixAspectRatio(abs(x2 - x1) < abs(y2 - y1) * aspectRatio);
+
+        if (abs(x2 - x1) > maxWidth) {
+            x2 = x1 - maxWidth * (x2 < x1 || -1);
+            fixAspectRatio();
+        }
+
+        if (abs(y2 - y1) > maxHeight) {
+            y2 = y1 - maxHeight * (y2 < y1 || -1);
+            fixAspectRatio(true);
+        }
+
+        selection = { x1: selX(min(x1, x2)), x2: selX(max(x1, x2)),
+            y1: selY(min(y1, y2)), y2: selY(max(y1, y2)),
+            width: abs(x2 - x1), height: abs(y2 - y1) };
+
+        update();
+
+        options.onSelectChange(img, getSelection());
+    }
+
+    function selectingMouseMove(event) {
+        x2 = /w|e|^$/.test(resize) || aspectRatio ? evX(event) : viewX(selection.x2);
+        y2 = /n|s|^$/.test(resize) || aspectRatio ? evY(event) : viewY(selection.y2);
+
+        doResize();
+
+        return false;
+
+    }
+
+    function doMove(newX1, newY1) {
+        x2 = (x1 = newX1) + selection.width;
+        y2 = (y1 = newY1) + selection.height;
+
+        $.extend(selection, { x1: selX(x1), y1: selY(y1), x2: selX(x2),
+            y2: selY(y2) });
+
+        update();
+
+        options.onSelectChange(img, getSelection());
+    }
+
+    function movingMouseMove(event) {
+        x1 = max(left, min(startX + evX(event), left + imgWidth - selection.width));
+        y1 = max(top, min(startY + evY(event), top + imgHeight - selection.height));
+
+        doMove(x1, y1);
+
+        event.preventDefault();
+
+        return false;
+    }
+
+    function startSelection() {
+        $(document).unbind('mousemove', startSelection);
+        adjust();
+
+        x2 = x1;
+        y2 = y1;
+
+        doResize();
+
+        resize = '';
+
+        if (!$outer.is(':visible'))
+            $box.add($outer).hide().fadeIn(options.fadeSpeed||0);
+
+        shown = true;
+
+        $(document).unbind('mouseup', cancelSelection)
+            .mousemove(selectingMouseMove).one('mouseup', docMouseUp);
+        $box.unbind('mousemove', areaMouseMove);
+
+        options.onSelectStart(img, getSelection());
+    }
+
+    function cancelSelection() {
+        $(document).unbind('mousemove', startSelection)
+            .unbind('mouseup', cancelSelection);
+        hide($box.add($outer));
+
+        setSelection(selX(x1), selY(y1), selX(x1), selY(y1));
+
+        if (!(this instanceof $.imgAreaSelect)) {
+            options.onSelectChange(img, getSelection());
+            options.onSelectEnd(img, getSelection());
+        }
+    }
+
+    function imgMouseDown(event) {
+        if (event.which != 1 || $outer.is(':animated')) return false;
+
+        adjust();
+        startX = x1 = evX(event);
+        startY = y1 = evY(event);
+
+        $(document).mousemove(startSelection).mouseup(cancelSelection);
+
+        return false;
+    }
+
+    function windowResize() {
+        doUpdate(false);
+    }
+
+    function imgLoad() {
+        imgLoaded = true;
+
+        setOptions(options = $.extend({
+            classPrefix: 'imgareaselect',
+            movable: true,
+            parent: 'body',
+            resizable: true,
+            resizeMargin: 10,
+            onInit: function () {},
+            onSelectStart: function () {},
+            onSelectChange: function () {},
+            onSelectEnd: function () {}
+        }, options));
+
+        $box.add($outer).css({ visibility: '' });
+
+        if (options.show) {
+            shown = true;
+            adjust();
+            update();
+            $box.add($outer).hide().fadeIn(options.fadeSpeed||0);
+        }
+
+        setTimeout(function () { options.onInit(img, getSelection()); }, 0);
+    }
+
+    var docKeyPress = function(event) {
+        var k = options.keys, d, t, key = event.keyCode;
+
+        d = !isNaN(k.alt) && (event.altKey || event.originalEvent.altKey) ? k.alt :
+            !isNaN(k.ctrl) && event.ctrlKey ? k.ctrl :
+            !isNaN(k.shift) && event.shiftKey ? k.shift :
+            !isNaN(k.arrows) ? k.arrows : 10;
+
+        if (k.arrows == 'resize' || (k.shift == 'resize' && event.shiftKey) ||
+            (k.ctrl == 'resize' && event.ctrlKey) ||
+            (k.alt == 'resize' && (event.altKey || event.originalEvent.altKey)))
+        {
+            switch (key) {
+            case 37:
+                d = -d;
+            case 39:
+                t = max(x1, x2);
+                x1 = min(x1, x2);
+                x2 = max(t + d, x1);
+                fixAspectRatio();
+                break;
+            case 38:
+                d = -d;
+            case 40:
+                t = max(y1, y2);
+                y1 = min(y1, y2);
+                y2 = max(t + d, y1);
+                fixAspectRatio(true);
+                break;
+            default:
+                return;
+            }
+
+            doResize();
+        }
+        else {
+            x1 = min(x1, x2);
+            y1 = min(y1, y2);
+
+            switch (key) {
+            case 37:
+                doMove(max(x1 - d, left), y1);
+                break;
+            case 38:
+                doMove(x1, max(y1 - d, top));
+                break;
+            case 39:
+                doMove(x1 + min(d, imgWidth - selX(x2)), y1);
+                break;
+            case 40:
+                doMove(x1, y1 + min(d, imgHeight - selY(y2)));
+                break;
+            default:
+                return;
+            }
+        }
+
+        return false;
+    };
+
+    function styleOptions($elem, props) {
+        for (var option in props)
+            if (options[option] !== undefined)
+                $elem.css(props[option], options[option]);
+    }
+
+    function setOptions(newOptions) {
+        if (newOptions.parent)
+            ($parent = $(newOptions.parent)).append($box.add($outer));
+
+        $.extend(options, newOptions);
+
+        adjust();
+
+        if (newOptions.handles != null) {
+            $handles.remove();
+            $handles = $([]);
+
+            i = newOptions.handles ? newOptions.handles == 'corners' ? 4 : 8 : 0;
+
+            while (i--)
+                $handles = $handles.add(div());
+
+            $handles.addClass(options.classPrefix + '-handle').css({
+                position: 'absolute',
+                fontSize: 0,
+                zIndex: zIndex + 1 || 1
+            });
+
+            if (!parseInt($handles.css('width')) >= 0)
+                $handles.width(5).height(5);
+
+            if (o = options.borderWidth)
+                $handles.css({ borderWidth: o, borderStyle: 'solid' });
+
+            styleOptions($handles, { borderColor1: 'border-color',
+                borderColor2: 'background-color',
+                borderOpacity: 'opacity' });
+        }
+
+        scaleX = options.imageWidth / imgWidth || 1;
+        scaleY = options.imageHeight / imgHeight || 1;
+
+        if (newOptions.x1 != null) {
+            setSelection(newOptions.x1, newOptions.y1, newOptions.x2,
+                newOptions.y2);
+            newOptions.show = !newOptions.hide;
+        }
+
+        if (newOptions.keys)
+            options.keys = $.extend({ shift: 1, ctrl: 'resize' },
+                newOptions.keys);
+
+        $outer.addClass(options.classPrefix + '-outer');
+        $area.addClass(options.classPrefix + '-selection');
+        for (i = 0; i++ < 4;)
+            $($border[i-1]).addClass(options.classPrefix + '-border' + i);
+
+        styleOptions($area, { selectionColor: 'background-color',
+            selectionOpacity: 'opacity' });
+        styleOptions($border, { borderOpacity: 'opacity',
+            borderWidth: 'border-width' });
+        styleOptions($outer, { outerColor: 'background-color',
+            outerOpacity: 'opacity' });
+        if (o = options.borderColor1)
+            $($border[0]).css({ borderStyle: 'solid', borderColor: o });
+        if (o = options.borderColor2)
+            $($border[1]).css({ borderStyle: 'dashed', borderColor: o });
+
+        $box.append($area.add($border).add($areaOpera)).append($handles);
+
+        if (msie) {
+            if (o = ($outer.css('filter')||'').match(/opacity=(\d+)/))
+                $outer.css('opacity', o[1]/100);
+            if (o = ($border.css('filter')||'').match(/opacity=(\d+)/))
+                $border.css('opacity', o[1]/100);
+        }
+
+        if (newOptions.hide)
+            hide($box.add($outer));
+        else if (newOptions.show && imgLoaded) {
+            shown = true;
+            $box.add($outer).fadeIn(options.fadeSpeed||0);
+            doUpdate();
+        }
+
+        aspectRatio = (d = (options.aspectRatio || '').split(/:/))[0] / d[1];
+
+        $img.add($outer).unbind('mousedown', imgMouseDown);
+
+        if (options.disable || options.enable === false) {
+            $box.unbind('mousemove', areaMouseMove).unbind('mousedown', areaMouseDown);
+            $(window).unbind('resize', windowResize);
+        }
+        else {
+            if (options.enable || options.disable === false) {
+                if (options.resizable || options.movable)
+                    $box.mousemove(areaMouseMove).mousedown(areaMouseDown);
+
+                $(window).resize(windowResize);
+            }
+
+            if (!options.persistent)
+                $img.add($outer).mousedown(imgMouseDown);
+        }
+
+        options.enable = options.disable = undefined;
+    }
+
+    this.remove = function () {
+        setOptions({ disable: true });
+        $box.add($outer).remove();
+    };
+
+    this.getOptions = function () { return options; };
+
+    this.setOptions = setOptions;
+
+    this.getSelection = getSelection;
+
+    this.setSelection = setSelection;
+
+    this.cancelSelection = cancelSelection;
+
+    this.update = doUpdate;
+
+    var msie = (/msie ([\w.]+)/i.exec(ua)||[])[1],
+        opera = /opera/i.test(ua),
+        safari = /webkit/i.test(ua) && !/chrome/i.test(ua);
+
+    $p = $img;
+
+    while ($p.length) {
+        zIndex = max(zIndex,
+            !isNaN($p.css('z-index')) ? $p.css('z-index') : zIndex);
+        if ($p.css('position') == 'fixed')
+            position = 'fixed';
+
+        $p = $p.parent(':not(body)');
+    }
+
+    zIndex = options.zIndex || zIndex;
+
+    if (msie)
+        $img.attr('unselectable', 'on');
+
+    $.imgAreaSelect.keyPress = msie || safari ? 'keydown' : 'keypress';
+
+    if (opera)
+
+        $areaOpera = div().css({ width: '100%', height: '100%',
+            position: 'absolute', zIndex: zIndex + 2 || 2 });
+
+    $box.add($outer).css({ visibility: 'hidden', position: position,
+        overflow: 'hidden', zIndex: zIndex || '0' });
+    $box.css({ zIndex: zIndex + 2 || 2 });
+    $area.add($border).css({ position: 'absolute', fontSize: 0 });
+
+    img.complete || img.readyState == 'complete' || !$img.is('img') ?
+        imgLoad() : $img.one('load', imgLoad);
+
+    if (!imgLoaded && msie && msie >= 7)
+        img.src = img.src;
+};
+
+$.fn.imgAreaSelect = function (options) {
+    options = options || {};
+
+    this.each(function () {
+        if ($(this).data('imgAreaSelect')) {
+            if (options.remove) {
+                $(this).data('imgAreaSelect').remove();
+                $(this).removeData('imgAreaSelect');
+            }
+            else
+                $(this).data('imgAreaSelect').setOptions(options);
+        }
+        else if (!options.remove) {
+            if (options.enable === undefined && options.disable === undefined)
+                options.enable = true;
+
+            $(this).data('imgAreaSelect', new $.imgAreaSelect(this, options));
+        }
+    });
+
+    if (options.instance)
+        return $(this).data('imgAreaSelect');
+
+    return this;
+};
+
+})(jQuery);
diff --git a/src/redakcja/static/js/lib/jquery/jquery.xmlns.js b/src/redakcja/static/js/lib/jquery/jquery.xmlns.js
new file mode 100644 (file)
index 0000000..96c5a55
--- /dev/null
@@ -0,0 +1,411 @@
+//
+//  jquery.xmlns.js:  xml-namespace selector support for jQuery
+//
+//  This plugin modifies the jQuery tag and attribute selectors to
+//  support optional namespace specifiers as defined in CSS 3:
+//
+//    $("elem")      - matches 'elem' nodes in the default namespace
+//    $("|elem")     - matches 'elem' nodes that don't have a namespace
+//    $("NS|elem")   - matches 'elem' nodes in declared namespace 'NS'
+//    $("*|elem")    - matches 'elem' nodes in any namespace
+//    $("NS|*")      - matches any nodes in declared namespace 'NS'
+//
+//  A similar synax is also supported for attribute selectors, but note
+//  that the default namespace does *not* apply to attributes - a missing
+//  or empty namespace selector selects only attributes with no namespace.
+//
+//  In a slight break from the W3C standards, and with a nod to ease of
+//  implementation, the empty namespace URI is treated as equivalent to
+//  an unspecified namespace.  Plenty of browsers seem to make the same
+//  assumption...
+//
+//  Namespace declarations live in the $.xmlns object, which is a simple
+//  mapping from namespace ids to namespace URIs.  The default namespace
+//  is determined by the value associated with the empty string.
+//
+//    $.xmlns.D = "DAV:"
+//    $.xmlns.FOO = "http://www.foo.com/xmlns/foobar"
+//    $.xmlns[""] = "http://www.example.com/new/default/namespace/"
+//
+//  Unfortunately this is a global setting - I can't find a way to do
+//  query-object-specific namespaces since the jQuery selector machinery
+//  is stateless.  However, you can use the 'xmlns' function to push and
+//  pop namespace delcarations with ease:
+//
+//    $().xmlns({D:"DAV:"})     // pushes the DAV: namespace
+//    $().xmlns("DAV:")         // makes DAV: the default namespace
+//    $().xmlns(false)          // pops the namespace we just pushed
+//    $().xmlns(false)          // pops again, returning to defaults
+//
+//  To execute this as a kind of "transaction", pass a function as the
+//  second argument.  It will be executed in the context of the current
+//  jQuery object:
+//
+//    $().xmlns("DAV:",function() {
+//      //  The default namespace is DAV: within this function,
+//      //  but it is reset to the previous value on exit.
+//      return this.find("response").each(...);
+//    }).find("div")
+//
+//  If you pass a string as a function, it will be executed against the
+//  current jQuery object using find(); i.e. the following will find all
+//  "href" elements in the "DAV:" namespace:
+//
+//    $().xmlns("DAV:","href")
+//
+// 
+//  And finally, the legal stuff:
+//
+//    Copyright (c) 2009, Ryan Kelly.
+//    TAG and ATTR functions derived from jQuery's selector.js.
+//    Dual licensed under the MIT and GPL licenses.
+//    http://docs.jquery.com/License
+//
+
+(function($) {
+
+//  Some common default namespaces, that are treated specially by browsers.
+//
+var default_xmlns = {
+    "xml": "http://www.w3.org/XML/1998/namespace",
+    "xmlns": "http://www.w3.org/2000/xmlns/",
+    "html": "http://www.w3.org/1999/xhtml/"
+};
+
+//  A reverse mapping for common namespace prefixes.
+//
+var default_xmlns_rev = {}
+for(var k in default_xmlns) {
+    default_xmlns_rev[default_xmlns[k]] = k;
+}
+
+
+//  $.xmlns is a mapping from namespace identifiers to namespace URIs.
+//  The default default-namespace is "*", and we provide some additional
+//  defaults that are specified in the XML Namespaces standard.
+//
+$.extend({xmlns: $.extend({},default_xmlns,{"":"*"})});
+
+
+//  jQuery method to push/pop namespace declarations.
+//
+//  If a single argument is specified:
+//    * if it's a mapping, push those namespaces onto the stack
+//    * if it's a string, push that as the default namespace
+//    * if it evaluates to false, pop the latest namespace
+//
+//  If two arguments are specified, the second is executed "transactionally"
+//  using the namespace declarations found in the first.  It can be either a
+//  a selector string (in which case it is passed to this.find()) or a function
+//  (in which case it is called in the context of the current jQuery object).
+//  The given namespace mapping is automatically pushed before executing and
+//  popped afterwards.
+//
+var xmlns_stack = [];
+$.fn.extend({xmlns: function(nsmap,func) {
+    if(typeof nsmap == "string") {
+        nsmap = {"":nsmap};
+    }
+    if(nsmap) {
+        xmlns_stack.push($.xmlns);
+        $.xmlns = $.extend({},$.xmlns,nsmap);
+        if(func !== undefined) {
+            if(typeof func == "string") {
+                return this.find(func).xmlns(undefined)
+            } else {
+                var self = this;
+                try {
+                    self = func.call(this);
+                    if(!self) {
+                        self = this;
+                    }
+                } finally {
+                    self.xmlns(undefined);
+                }
+                return self
+            }
+        } else {
+            return this;
+        }
+    } else {
+        $.xmlns = (xmlns_stack ? xmlns_stack.pop() : {});
+        return this;
+    }
+}});
+
+
+//  Convert a namespace prefix into a namespace URI, based
+//  on the delcarations made in $.xmlns.
+//
+var getNamespaceURI = function(id) {
+    // No namespace id, use the default.
+    if(!id) {
+        return $.xmlns[""];
+    }
+    // Strip the pipe character from the specifier
+    id = id.substr(0,id.length-1);
+    // Certain special namespaces aren't mapped to a URI
+    if(id == "" || id == "*") {
+        return id;
+    }
+    var ns = $.xmlns[id];
+    if(typeof(ns) == "undefined") {
+        throw "Syntax error, undefined namespace prefix '" + id + "'";
+    }
+    return ns;
+};
+
+
+//  Update the regex used by $.expr to parse selector components for a
+//  particular type of selector (e.g. "TAG" or "ATTR").
+//
+//  This logic is taken straight from the jQuery/Sizzle sources.
+//
+var setExprMatchRegex = function(type,regex) {
+  $.expr.match[type] = new RegExp(regex.source + /(?![^\[]*\])(?![^\(]*\))/.source);
+  if($.expr.leftMatch) {
+      $.expr.leftMatch[type] = new RegExp(/(^(?:.|\r|\n)*?)/.source + $.expr.match[type].source.replace(/\\(\d+)/g, function(all, num){
+          return "\\" + (num - 0 + 1);
+      }));
+  }
+}
+
+
+
+//  Modify the TAG match regexp to include optional namespace selector.
+//  This is basically (namespace|)?(tagname).
+//
+setExprMatchRegex("TAG",/^((?:((?:[\w\u00c0-\uFFFF\*_-]*\|)?)((?:[\w\u00c0-\uFFFF\*_-]|\\.)+)))/);
+
+
+//  Perform some capability-testing.
+//
+var div = document.createElement("div");
+
+//  Sometimes getElementsByTagName("*") will return comment nodes,
+//  which we will have to remove from the results.
+//
+var gebtn_yields_comments = false;
+div.appendChild(document.createComment(""));
+if(div.getElementsByTagName("*").length > 0) {
+    gebtn_yields_comments = true;
+}
+
+//  Some browsers return node.localName in upper case, some in lower case.
+//
+var localname_is_uppercase = true;
+if(div.localName && div.localName == "div") {
+    localname_is_uppercase = false;
+}
+
+//  Allow the testing div to be garbage-collected.
+//
+div = null;
+
+
+//  Modify the TAG find function to account for a namespace selector.
+//
+$.expr.find.TAG = function(match,context,isXML) {
+    var ns = getNamespaceURI(match[2]);
+    var ln = match[3];
+    var res;
+    if(typeof context.getElementsByTagNameNS != "undefined") {
+        //  Easy case - we have getElementsByTagNameNS
+        res = context.getElementsByTagNameNS(ns,ln);
+    } else if(typeof context.selectNodes != "undefined") {
+        //  Use xpath if possible (not available on HTML DOM nodes in IE)
+        if(context.ownerDocument) {
+            context.ownerDocument.setProperty("SelectionLanguage","XPath");
+        } else {
+            context.setProperty("SelectionLanguage","XPath");
+        }
+        var predicate = "";
+        if(ns != "*") {
+            if(ln != "*") {
+                predicate="namespace-uri()='"+ns+"' and local-name()='"+ln+"'";
+            } else {
+                predicate="namespace-uri()='"+ns+"'";
+            }
+        } else {
+            if(ln != "*") {
+                predicate="local-name()='"+ln+"'";
+            }
+        }
+        if(predicate) {
+            res = context.selectNodes("descendant-or-self::*["+predicate+"]");
+        } else {
+            res = context.selectNodes("descendant-or-self::*");
+        }
+    } else {
+        //  Otherwise, we need to simulate using getElementsByTagName
+        res = context.getElementsByTagName(ln); 
+        if(gebtn_yields_comments && ln == "*") {
+            var tmp = [];
+            for(var i=0; res[i]; i++) {
+                if(res[i].nodeType == 1) {
+                    tmp.push(res[i]);
+                }
+            }
+            res = tmp;
+        }
+        if(res && ns != "*") {
+            var tmp = [];
+            for(var i=0; res[i]; i++) {
+               if(res[i].namespaceURI == ns || res[i].tagUrn == ns) {
+                   tmp.push(res[i]);
+               }
+            }
+            res = tmp;
+        }
+    }
+    return res;
+};
+
+
+//  Check whether a node is part of an XML document.
+//  Copied verbatim from jQuery sources, needed in TAG preFilter below.
+//
+var isXML = function(elem){
+    return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" ||
+            !!elem.ownerDocument && elem.ownerDocument.documentElement.nodeName !== "HTML";
+};
+
+
+//  Modify the TAG preFilter function to work with modified match regexp.
+//  This normalises case of the tag name if we're in a HTML document.
+//
+$.expr.preFilter.TAG = function(match, curLoop, inplace, result, not, isXML) {
+  var ln = match[3];
+  if(!isXML) {
+      if(localname_is_uppercase) {
+          ln = ln.toUpperCase();
+      } else {
+          ln = ln.toLowerCase();
+      }
+  }
+  return [match[0],getNamespaceURI(match[2]),ln];
+};
+
+
+//  Modify the TAG filter function to account for a namespace selector.
+//
+$.expr.filter.TAG = function(elem,match) {
+    var ns = match[1];
+    var ln = match[2];
+    var e_ns = elem.namespaceURI ? elem.namespaceURI : elem.tagUrn;
+    var e_ln = elem.localName ? elem.localName : elem.tagName;
+    if(ns == "*" || e_ns == ns || (ns == "" && !e_ns)) {
+        return ((ln == "*" && elem.nodeType == 1)  || e_ln == ln);
+    }
+    return false;
+};
+
+
+//  Modify the ATTR match regexp to extract a namespace selector.
+//  This is basically ([namespace|])(attrname)(op)(quote)(pattern)(quote)
+//
+setExprMatchRegex("ATTR",/\[\s*((?:((?:[\w\u00c0-\uFFFF\*_-]*\|)?)((?:[\w\u00c0-\uFFFF_-]|\\.)+)))\s*(?:(\S?=)\s*(['"]*)(.*?)\5|)\s*\]/);
+
+
+//  Modify the ATTR preFilter function to account for new regexp match groups,
+//  and normalise the namespace URI.
+//
+$.expr.preFilter.ATTR = function(match, curLoop, inplace, result, not, isXML) {
+    var name = match[3].replace(/\\/g, "");
+    if(!isXML && $.expr.attrMap[name]) {
+        match[3] = $.expr.attrMap[name];
+    }
+    if( match[4] == "~=" ) {
+        match[6] = " " + match[6] + " ";
+    }
+    if(!match[2] || match[2] == "|") {
+        match[2] = "";
+    } else {
+        match[2] = getNamespaceURI(match[2]);
+    }
+    return match;
+};
+
+
+//  Modify the ATTR filter function to account for namespace selector.
+//  Unfortunately this means factoring out the attribute-checking code
+//  into a separate function, since it might be called multiple times.
+//
+var filter_attr = function(result,type,check) {
+    var value = result + "";
+    return result == null ?
+                type === "!=" :
+                type === "=" ?
+                value === check :
+                type === "*=" ?
+                value.indexOf(check) >= 0 :
+                type === "~=" ?
+                (" " + value + " ").indexOf(check) >= 0 :
+                !check ?
+                value && result !== false :
+                type === "!=" ?
+                value != check :
+                type === "^=" ?
+                value.indexOf(check) === 0 :
+                type === "$=" ?
+                value.substr(value.length - check.length) === check :
+                type === "|=" ?
+                value === check || value.substr(0,check.length+1)===check+"-" :
+                false;
+}
+
+
+$.expr.filter.ATTR = function(elem, match) {
+    var ns = match[2];
+    var name = match[3];
+    var type = match[4];
+    var check = match[6];
+    var result;
+    //  No namespace, just use ordinary attribute lookup.
+    if(ns == "") {
+        result = $.expr.attrHandle[name] ?
+                     $.expr.attrHandle[name](elem) :
+                     elem[name] != null ?
+                         elem[name] :
+                         elem.getAttribute(name);
+        return filter_attr(result,type,check);
+    }
+    //  Directly use getAttributeNS if applicable and available
+    if(ns != "*" && typeof elem.getAttributeNS != "undefined") {
+        return filter_attr(elem.getAttributeNS(ns,name),type,check);
+    }
+    //  Need to iterate over all attributes, either because we couldn't
+    //  look it up or because we need to match all namespaces.
+    var attrs = elem.attributes;
+    for(var i=0; attrs[i]; i++) {
+        var ln = attrs[i].localName;
+        if(!ln) {
+            ln = attrs[i].nodeName
+            var idx = ln.indexOf(":");
+            if(idx >= 0) {
+                ln = ln.substr(idx+1);
+            }
+        }
+        if(ln == name) {
+            result = attrs[i].nodeValue;
+            if(ns == "*" || attrs[i].namespaceURI == ns) {
+                if(filter_attr(result,type,check)) {
+                    return true;
+                }
+            }
+            if(attrs[i].namespaceURI === "" && attrs[i].prefix) {
+                if(attrs[i].prefix == default_xmlns_rev[ns]) {
+                    if(filter_attr(result,type,check)) {
+                        return true;
+                    }
+                }
+            }
+        }
+    }
+    return false;
+};
+
+
+})(jQuery);
+
diff --git a/src/redakcja/static/js/slugify.js b/src/redakcja/static/js/slugify.js
new file mode 100644 (file)
index 0000000..9e17ba7
--- /dev/null
@@ -0,0 +1,126 @@
+(function() {
+    var ALL_DOWNCODE_MAPS = new Array()
+
+    ALL_DOWNCODE_MAPS[0] = {
+        // LATIN_MAP
+        'À': 'A', 'Á': 'A', 'Â': 'A', 'Ã': 'A', 'Ä': 'A', 'Å': 'A', 'Æ': 'AE', 'Ç':
+        'C', 'È': 'E', 'É': 'E', 'Ê': 'E', 'Ë': 'E', 'Ì': 'I', 'Í': 'I', 'Î': 'I',
+        'Ï': 'I', 'Ð': 'D', 'Ñ': 'N', 'Ò': 'O', 'Ó': 'O', 'Ô': 'O', 'Õ': 'O', 'Ö':
+        'O', 'Ő': 'O', 'Ø': 'O', 'Ù': 'U', 'Ú': 'U', 'Û': 'U', 'Ü': 'U', 'Ű': 'U',
+        'Ý': 'Y', 'Þ': 'TH', 'ß': 'ss', 'à':'a', 'á':'a', 'â': 'a', 'ã': 'a', 'ä':
+        'a', 'å': 'a', 'æ': 'ae', 'ç': 'c', 'è': 'e', 'é': 'e', 'ê': 'e', 'ë': 'e',
+        'ì': 'i', 'í': 'i', 'î': 'i', 'ï': 'i', 'ð': 'd', 'ñ': 'n', 'ò': 'o', 'ó':
+        'o', 'ô': 'o', 'õ': 'o', 'ö': 'o', 'ő': 'o', 'ø': 'o', 'ù': 'u', 'ú': 'u',
+        'û': 'u', 'ü': 'u', 'ű': 'u', 'ý': 'y', 'þ': 'th', 'ÿ': 'y'
+    }
+    ALL_DOWNCODE_MAPS[1] = {
+        // LATIN_SYMBOLS_MAP
+        '©':'(c)'
+    }
+    ALL_DOWNCODE_MAPS[2] = {
+        // GREEK_MAP
+        'α':'a', 'β':'b', 'γ':'g', 'δ':'d', 'ε':'e', 'ζ':'z', 'η':'h', 'θ':'8',
+        'ι':'i', 'κ':'k', 'λ':'l', 'μ':'m', 'ν':'n', 'ξ':'3', 'ο':'o', 'π':'p',
+        'ρ':'r', 'σ':'s', 'τ':'t', 'υ':'y', 'φ':'f', 'χ':'x', 'ψ':'ps', 'ω':'w',
+        'ά':'a', 'έ':'e', 'ί':'i', 'ό':'o', 'ύ':'y', 'ή':'h', 'ώ':'w', 'ς':'s',
+        'ϊ':'i', 'ΰ':'y', 'ϋ':'y', 'ΐ':'i',
+        'Α':'A', 'Β':'B', 'Γ':'G', 'Δ':'D', 'Ε':'E', 'Ζ':'Z', 'Η':'H', 'Θ':'8',
+        'Ι':'I', 'Κ':'K', 'Λ':'L', 'Μ':'M', 'Ν':'N', 'Ξ':'3', 'Ο':'O', 'Π':'P',
+        'Ρ':'R', 'Σ':'S', 'Τ':'T', 'Υ':'Y', 'Φ':'F', 'Χ':'X', 'Ψ':'PS', 'Ω':'W',
+        'Ά':'A', 'Έ':'E', 'Ί':'I', 'Ό':'O', 'Ύ':'Y', 'Ή':'H', 'Ώ':'W', 'Ϊ':'I',
+        'Ϋ':'Y'
+    }
+    ALL_DOWNCODE_MAPS[3] = {
+        // TURKISH_MAP
+        'ş':'s', 'Ş':'S', 'ı':'i', 'İ':'I', 'ç':'c', 'Ç':'C', 'ü':'u', 'Ü':'U',
+        'ö':'o', 'Ö':'O', 'ğ':'g', 'Ğ':'G'
+    }
+    ALL_DOWNCODE_MAPS[4] = {
+        // RUSSIAN_MAP
+        'а':'a', 'б':'b', 'в':'v', 'г':'g', 'д':'d', 'е':'e', 'ё':'yo', 'ж':'zh',
+        'з':'z', 'и':'i', 'й':'j', 'к':'k', 'л':'l', 'м':'m', 'н':'n', 'о':'o',
+        'п':'p', 'р':'r', 'с':'s', 'т':'t', 'у':'u', 'ф':'f', 'х':'h', 'ц':'c',
+        'ч':'ch', 'ш':'sh', 'щ':'sh', 'ъ':'', 'ы':'y', 'ь':'', 'э':'e', 'ю':'yu',
+        'я':'ya',
+        'А':'A', 'Б':'B', 'В':'V', 'Г':'G', 'Д':'D', 'Е':'E', 'Ё':'Yo', 'Ж':'Zh',
+        'З':'Z', 'И':'I', 'Й':'J', 'К':'K', 'Л':'L', 'М':'M', 'Н':'N', 'О':'O',
+        'П':'P', 'Р':'R', 'С':'S', 'Т':'T', 'У':'U', 'Ф':'F', 'Х':'H', 'Ц':'C',
+        'Ч':'Ch', 'Ш':'Sh', 'Щ':'Sh', 'Ъ':'', 'Ы':'Y', 'Ь':'', 'Э':'E', 'Ю':'Yu',
+        'Я':'Ya'
+    }
+    ALL_DOWNCODE_MAPS[5] = {
+        // UKRAINIAN_MAP
+        'Є':'Ye', 'І':'I', 'Ї':'Yi', 'Ґ':'G', 'є':'ye', 'і':'i', 'ї':'yi', 'ґ':'g'
+    }
+    ALL_DOWNCODE_MAPS[6] = {
+        // CZECH_MAP
+        'č':'c', 'ď':'d', 'ě':'e', 'ň': 'n', 'ř':'r', 'š':'s', 'ť':'t', 'ů':'u',
+        'ž':'z', 'Č':'C', 'Ď':'D', 'Ě':'E', 'Ň': 'N', 'Ř':'R', 'Š':'S', 'Ť':'T',
+        'Ů':'U', 'Ž':'Z'
+    }
+
+    ALL_DOWNCODE_MAPS[7] = {
+        // POLISH_MAP
+        'ą':'a', 'ć':'c', 'ę':'e', 'ł':'l', 'ń':'n', 'ó':'o', 'ś':'s', 'ź':'z',
+        'ż':'z', 'Ą':'A', 'Ć':'C', 'Ę':'e', 'Ł':'L', 'Ń':'N', 'Ó':'o', 'Ś':'S',
+        'Ź':'Z', 'Ż':'Z'
+    }
+
+    ALL_DOWNCODE_MAPS[8] = {
+        // LATVIAN_MAP
+        'ā':'a', 'č':'c', 'ē':'e', 'ģ':'g', 'ī':'i', 'ķ':'k', 'ļ':'l', 'ņ':'n',
+        'š':'s', 'ū':'u', 'ž':'z', 'Ā':'A', 'Č':'C', 'Ē':'E', 'Ģ':'G', 'Ī':'i',
+        'Ķ':'k', 'Ļ':'L', 'Ņ':'N', 'Š':'S', 'Ū':'u', 'Ž':'Z'
+    }
+
+    var Downcoder = new Object();
+
+    Downcoder.Initialize = function() {
+        if (Downcoder.map) // already made
+            return ;
+        Downcoder.map ={}
+        Downcoder.chars = '';
+        for(var i in ALL_DOWNCODE_MAPS) {
+            var lookup = ALL_DOWNCODE_MAPS[i]
+            for (var c in lookup) {
+                Downcoder.map[c] = lookup[c];
+                Downcoder.chars += c;
+            }
+         }
+        Downcoder.regex = new RegExp('[' + Downcoder.chars + ']|[^' + Downcoder.chars + ']+','g');
+    }
+
+    downcode = function(slug) {
+        Downcoder.Initialize();
+        var downcoded =""
+        var pieces = slug.match(Downcoder.regex);
+        if(pieces) {
+            for (var i = 0 ; i < pieces.length ; i++) {
+                if (pieces[i].length == 1) {
+                    var mapped = Downcoder.map[pieces[i]];
+                    if (mapped != null) {
+                        downcoded+=mapped;
+                        continue;
+                    }
+                }
+                downcoded+=pieces[i];
+            }
+        } else {
+            downcoded = slug;
+        }
+        return downcoded;
+    }
+
+    slugify = function(s) {
+        s = downcode(s);
+        // if downcode doesn't hit, the char will be stripped here
+        s = s.replace(/[^-\w\s]/g, '');  // remove unneeded chars
+        s = s.replace(/^\s+|\s+$/g, ''); // trim leading/trailing spaces
+        s = s.replace(/[-\s]+/g, '-');   // convert spaces to hyphens
+        s = s.toLowerCase();             // convert to lowercase
+
+        return s;
+    }
+})();
+
+
diff --git a/src/redakcja/static/js/wiki/base.js b/src/redakcja/static/js/wiki/base.js
new file mode 100644 (file)
index 0000000..ad2ee3a
--- /dev/null
@@ -0,0 +1,339 @@
+(function($)
+{
+       var noop = function() { };
+
+       $.wiki = {
+               perspectives: {},
+               cls: {},
+               state: {
+                       "version": 1,
+                       "perspectives": {
+                               "ScanGalleryPerspective": {
+                                       "show": true,
+                                       "page": undefined
+                               },
+                               "CodeMirrorPerspective": {}
+                               /*
+                               "VisualPerspective": {},
+                               "HistoryPerspective": {},
+                               "SummaryPerspective": {}
+                               */
+                       }
+               }
+       };
+
+       $.wiki.loadConfig = function() {
+               if(!window.localStorage)
+                       return;
+
+               try {
+                       var value = window.localStorage.getItem(CurrentDocument.id) || "{}";
+                       var config = JSON.parse(value);
+
+                       if (config.version == $.wiki.state.version) {
+                               $.wiki.state.perspectives = $.extend($.wiki.state.perspectives, config.perspectives);
+                       }
+               } catch(e) {
+                       console.log("Failed to load config, using default.");
+               }
+
+               console.log("Loaded:", $.wiki.state, $.wiki.state.version);
+       };
+
+       $(window).bind('unload', function() {
+               if(window.localStorage)
+                       window.localStorage.setItem(CurrentDocument.id, JSON.stringify($.wiki.state));
+       })
+
+
+       $.wiki.activePerspective = function() {
+               return this.perspectives[$("#tabs li.active").attr('id')];
+       };
+
+       $.wiki.exitContext = function() {
+               var ap = this.activePerspective();
+               if(ap) ap.onExit();
+               return ap;
+       };
+
+       $.wiki.enterContext = function(ap) {
+               if(ap) ap.onEnter();
+       };
+
+       $.wiki.isDirty = function() {
+               var ap = this.activePerspective();
+               return (!!CurrentDocument && CurrentDocument.has_local_changes) || ap.dirty();
+       };
+
+       $.wiki.newTab = function(doc, title, klass) {
+               var base_id = 'id' + Math.floor(Math.random()* 5000000000);
+               var id = (''+klass)+'_' + base_id;
+               var $tab = $('<li id="'+id+'" data-ui-related="'+base_id+'" data-ui-jsclass="'+klass+'" >'
+                               + title + '<img src="'+STATIC_URL+'icons/close.png" class="tabclose"></li>');
+               var $view = $('<div class="editor '+klass+'" id="'+base_id+'"> </div>');
+
+               this.perspectives[id] = new $.wiki[klass]({
+                       doc: doc,
+                       id: id,
+                       base_id: base_id,
+               });
+
+               $('#tabs').append($tab);
+               $view.hide().appendTo('#editor');
+               return {
+                       tab: $tab[0],
+                       view: $view[0],
+               };
+       };
+
+       $.wiki.initTab = function(options) {
+               var klass = $(options.tab).attr('data-ui-jsclass');
+
+               return new $.wiki[klass]({
+                       doc: options.doc,
+                       id: $(options.tab).attr('id'),
+                       callback: function() {
+                               $.wiki.perspectives[this.perspective_id] = this;
+                               if(options.callback)
+                                       options.callback.call(this);
+                       }
+               });
+       };
+
+       $.wiki.perspectiveForTab = function(tab) { // element or id
+               return this.perspectives[ $(tab).attr('id')];
+       }
+
+       $.wiki.switchToTab = function(tab){
+               var self = this;
+               var $tab = $(tab);
+
+               if($tab.length != 1)
+                       $tab = $(DEFAULT_PERSPECTIVE);
+
+               var $old = $tab.closest('.tabs').find('.active');
+
+               $old.each(function(){
+                       $(this).removeClass('active');
+                       self.perspectives[$(this).attr('id')].onExit();
+                       $('#' + $(this).attr('data-ui-related')).hide();
+               });
+
+               /* show new */
+               $tab.addClass('active');
+               $('#' + $tab.attr('data-ui-related')).show();
+
+               console.log($tab);
+               console.log($.wiki.perspectives);
+
+               $.wiki.perspectives[$tab.attr('id')].onEnter();
+       };
+
+       /*
+        * Basic perspective.
+        */
+       $.wiki.Perspective = function(options) {
+               if(!options) return;
+
+               this.doc = options.doc;
+               if (options.id) {
+                       this.perspective_id = options.id;
+               }
+               else {
+                       this.perspective_id = '';
+               }
+
+               if(options.callback)
+                       options.callback.call(this);
+       };
+
+       $.wiki.Perspective.prototype.config = function() {
+               return $.wiki.state.perspectives[this.perspective_id];
+       }
+
+       $.wiki.Perspective.prototype.toString = function() {
+               return this.perspective_id;
+       };
+
+       $.wiki.Perspective.prototype.dirty = function() {
+               return true;
+       };
+
+       $.wiki.Perspective.prototype.onEnter = function () {
+               // called when perspective in initialized
+               if (!this.noupdate_hash_onenter) {
+                       document.location.hash = '#' + this.perspective_id;
+               }
+       };
+
+       $.wiki.Perspective.prototype.onExit = function () {
+               // called when user switches to another perspective
+               if (!this.noupdate_hash_onenter) {
+                       document.location.hash = '';
+               }
+       };
+
+       $.wiki.Perspective.prototype.destroy = function() {
+               // pass
+       };
+
+       $.wiki.Perspective.prototype.freezeState = function () {
+               // free UI state (don't store data here)
+       };
+
+       $.wiki.Perspective.prototype.unfreezeState = function (frozenState) {
+               // restore UI state
+       };
+
+       /*
+        * Stub rendering (used in generating history)
+        */
+       $.wiki.renderStub = function(params)
+       {
+               params = $.extend({ 'filters': {} }, params);
+               var $elem = params.stub.clone();
+               $elem.removeClass('row-stub');
+               params.container.append($elem);
+
+               $('*[data-stub-value]', $elem).each(function() {
+                       var $this = $(this);
+                       var field = $this.attr('data-stub-value');
+
+                       var value = params.data[field];
+
+                       if(params.filters[field])
+                               value = params.filters[field](value);
+
+                       if(value === null || value === undefined) return;
+
+                       if(!$this.attr('data-stub-target')) {
+                               $this.text(value);
+                       }
+                       else {
+                               $this.attr($this.attr('data-stub-target'), value);
+                               $this.removeAttr('data-stub-target');
+                               $this.removeAttr('data-stub-value');
+                       }
+               });
+
+               $elem.show();
+               return $elem;
+       };
+
+       /*
+        * Dialogs
+        */
+       function GenericDialog(element) {
+               if(!element) return;
+
+               var self = this;
+
+               self.$elem = $(element);
+
+               if(!self.$elem.attr('data-ui-initialized')) {
+                       console.log("Initializing dialog", this);
+                       self.initialize();
+                       self.$elem.attr('data-ui-initialized', true);
+               }
+
+               self.show();
+       };
+
+       GenericDialog.prototype = {
+
+               /*
+               * Steps to follow when the dialog in first loaded on page.
+               */
+               initialize: function(){
+                       var self = this;
+
+                       /* bind buttons */
+                       $('button[data-ui-action]', self.$elem).click(function(event) {
+                               event.preventDefault();
+
+                               var action = $(this).attr('data-ui-action');
+                               console.log("Button pressed, action: ", action);
+
+                               try {
+                                       self[action + "Action"].call(self);
+                               } catch(e) {
+                                       console.log("Action failed:", e);
+                                       // always hide on cancel
+                                       if(action == 'cancel')
+                                               self.hide();
+                               }
+                       });
+               },
+
+               /*
+                * Prepare dialog for user. Clear any unnessary data.
+               */
+               show: function() {
+                       $.blockUI({
+                               message: this.$elem,
+                               css: {
+                                       'top': '25%',
+                                       'left': '25%',
+                                       'width': '50%'
+                               }
+                       });
+               },
+
+               hide: function(){
+                       $.unblockUI();
+               },
+
+               cancelAction: function() {
+                       this.hide();
+               },
+
+               doneAction: function() {
+                       this.hide();
+               },
+
+               clearForm: function() {
+                       $("*[data-ui-error-for]", this.$elem).text('');
+               },
+
+               reportErrors: function(errors) {
+                       var global = $("*[data-ui-error-for='__all__']", this.$elem);
+                       var unassigned = [];
+
+            $("*[data-ui-error-for]", this.$elem).text('');
+                       for (var field_name in errors)
+                       {
+                               var span = $("*[data-ui-error-for='"+field_name+"']", this.$elem);
+
+                               if(!span.length) {
+                                       unassigned.push(field_name);
+                                       continue;
+                               }
+
+                               span.text(errors[field_name].join(' '));
+                       }
+
+                       if(unassigned.length > 0)
+                               global.text( global.text() + 'W formularzu wystąpiły błędy');
+               }
+       };
+
+       $.wiki.cls.GenericDialog = GenericDialog;
+
+       $.wiki.showDialog = function(selector, options) {
+               var elem = $(selector);
+
+               if(elem.length != 1) {
+                       console.log("Failed to show dialog:", selector, elem);
+                       return false;
+               }
+
+               try {
+                   var klass = elem.attr('data-ui-jsclass');
+                       return new $.wiki.cls[klass](elem, options);
+               } catch(e) {
+                       console.log("Failed to show dialog", selector, klass, e);
+                       return false;
+               }
+       };
+
+})(jQuery);
diff --git a/src/redakcja/static/js/wiki/dialog_pubmark.js b/src/redakcja/static/js/wiki/dialog_pubmark.js
new file mode 100755 (executable)
index 0000000..902a737
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Dialog for marking document for publishing
+ *
+ */
+(function($){
+
+    function PubmarkDialog(element, options){
+        if (!options.revision  && options.revision != 0)
+            throw "PubmarkDialog needs a revision number.";
+
+        this.ctx = $.wiki.exitContext();
+        this.clearForm();
+
+        /* fill out hidden fields */
+        this.$form = $('form', element);
+
+        $("input[name='pubmark-id']", this.$form).val(CurrentDocument.id);
+        $("input[name='pubmark-revision']", this.$form).val(options.revision);
+
+        $.wiki.cls.GenericDialog.call(this, element);
+    };
+
+    PubmarkDialog.prototype = $.extend(new $.wiki.cls.GenericDialog(), {
+        cancelAction: function(){
+            $.wiki.enterContext(this.ctx);
+            this.hide();
+        },
+
+        saveAction: function(){
+            var self = this;
+
+            self.$elem.block({
+                message: "Oznaczanie wersji",
+                fadeIn: 0,
+            });
+
+            CurrentDocument.pubmark({
+                form: self.$form,
+                success: function(doc, changed, info){
+                    self.$elem.block({
+                        message: info,
+                        timeout: 2000,
+                        fadeOut: 0,
+                        onUnblock: function(){
+                            self.hide();
+                            $.wiki.enterContext(self.ctx);
+                        }
+                    });
+                },
+                failure: function(doc, info){
+                    console.log("Failure", info);
+                    self.reportErrors(info);
+                    self.$elem.unblock();
+                }
+            });
+        }
+    });
+
+    /* make it global */
+    $.wiki.cls.PubmarkDialog = PubmarkDialog;
+})(jQuery);
diff --git a/src/redakcja/static/js/wiki/dialog_revert.js b/src/redakcja/static/js/wiki/dialog_revert.js
new file mode 100644 (file)
index 0000000..4d550f9
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Dialog for reverting document on the server
+ *
+ */
+(function($) {
+
+    function RevertDialog(element, options) {
+        this.ctx = $.wiki.exitContext();
+        this.clearForm();
+
+        /* fill out hidden fields */
+        this.$form = $('form', element);
+
+        $("input[name='textrevert-revision']", this.$form).val(options.revision);
+
+        $.wiki.cls.GenericDialog.call(this, element);
+    };
+
+    RevertDialog.prototype = new $.wiki.cls.GenericDialog();
+
+    RevertDialog.prototype.cancelAction = function() {
+        $.wiki.enterContext(this.ctx);
+        this.hide();
+    };
+
+    RevertDialog.prototype.revertAction = function() {
+            var self = this;
+
+            self.$elem.block({
+                message: "Przywracanie...",
+                fadeIn: 0,
+            });
+            $.wiki.blocking = self.$elem;
+
+            try {
+
+                CurrentDocument.revertToVersion({
+                    form: self.$form,
+                    success: function(e, msg) {
+                        self.$elem.block({
+                            message: msg,
+                            timeout: 2000,
+                            fadeOut: 0,
+                            onUnblock: function() {
+                                self.hide();
+                                $.wiki.enterContext(self.ctx);
+                            }
+                        });
+                    },
+                    'failure': function(e, info) {
+                        console.log("Failure", info);
+                        self.reportErrors(info);
+                        self.$elem.unblock();
+                    }
+                });
+
+            } catch(e) {
+                console.log('Exception:', e)
+                self.$elem.unblock();
+            }
+    }; /* end of revert dialog */
+
+    /* make it global */
+    $.wiki.cls.RevertDialog = RevertDialog;
+})(jQuery);
diff --git a/src/redakcja/static/js/wiki/dialog_save.js b/src/redakcja/static/js/wiki/dialog_save.js
new file mode 100644 (file)
index 0000000..903c0e1
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Dialog for saving document to the server
+ *
+ */
+(function($) {
+
+       function SaveDialog(element) {
+               this.ctx = $.wiki.exitContext();
+               this.clearForm();
+
+               /* fill out hidden fields */
+               this.$form = $('form', element);
+
+               $("input[name='textsave-parent_revision']", this.$form).val(CurrentDocument.revision);
+
+               $.wiki.cls.GenericDialog.call(this, element);
+       };
+
+       SaveDialog.prototype = new $.wiki.cls.GenericDialog();
+
+       SaveDialog.prototype.cancelAction = function() {
+               $.wiki.enterContext(this.ctx);
+               this.hide();
+       };
+
+       SaveDialog.prototype.saveAction = function() {
+                       var self = this;
+
+                       self.$elem.block({
+                               message: "Zapisywanie... <br/><button id='save-hide'>ukryj</button>",
+                               fadeIn: 0,
+                       });
+            $.wiki.blocking = self.$elem;
+
+                       try {
+
+                               CurrentDocument.save({
+                                       form: self.$form,
+                                       success: function(doc, changed, info){
+                                               self.$elem.block({
+                                                       message: info,
+                                                       timeout: 2000,
+                                                       fadeOut: 0,
+                                                       onUnblock: function() {
+                                                               self.hide();
+                                                               $.wiki.enterContext(self.ctx);
+                                                       }
+                                               });
+                                       },
+                                       failure: function(doc, info) {
+                                               console.log("Failure", info);
+                                               self.reportErrors(info);
+                                               self.$elem.unblock();
+                                       }
+                               });
+                       } catch(e) {
+                               console.log('Exception:', e)
+                               self.$elem.unblock();
+                       }
+       }; /* end of save dialog */
+
+       /* make it global */
+       $.wiki.cls.SaveDialog = SaveDialog;
+})(jQuery);
diff --git a/src/redakcja/static/js/wiki/loader.js b/src/redakcja/static/js/wiki/loader.js
new file mode 100644 (file)
index 0000000..1223992
--- /dev/null
@@ -0,0 +1,189 @@
+if (!window.console) {
+    window.console = {
+        log: function(){
+        }
+    }
+}
+
+var DEFAULT_PERSPECTIVE = "#VisualPerspective";
+
+$(function()
+{
+       var tabs = $('ol#tabs li');
+       var gallery = null;
+       CurrentDocument = new $.wikiapi.WikiDocument("document-meta");
+
+       $.blockUI.defaults.baseZ = 10000;
+
+    function initialize()
+       {
+        var splitter = $('#splitter'),
+            editors = $('#editor .editor'),
+            vsplitbar = $('.vsplitbar'),
+            sidebar = $('#sidebar'),
+            dragLayer = $('#drag-layer'),
+            vsplitbarWidth = vsplitbar.outerWidth(),
+            isHolding = false;
+
+        // Moves panes so that left border of the vsplitbar lands x pixels from the left border of the splitter
+        function setSplitbarAt(x) {
+            var right = splitterWidth - x;
+            editors.each(function() {
+                this.style.right = right + 'px';
+            });
+            vsplitbar[0].style.right = sidebar[0].style.width = (right - vsplitbarWidth) + 'px';
+        };
+
+               $(document).keydown(function(event) {
+                       console.log("Received key:", event);
+               });
+
+               /* The save button */
+        $('#save-button').click(function(event){
+            event.preventDefault();
+                       $.wiki.showDialog('#save_dialog');
+        });
+
+               $('.editor').hide();
+
+               /*
+                * TABS
+                */
+        $('.tabs li').live('click', function(event, callback) {
+            event.preventDefault();
+                       $.wiki.switchToTab(this);
+        });
+
+               $('#tabs li > .tabclose').live('click', function(event, callback) {
+                       var $tab = $(this).parent();
+
+                       if($tab.is('.active'))
+                               $.wiki.switchToTab(DEFAULT_PERSPECTIVE);
+
+                       var p = $.wiki.perspectiveForTab($tab);
+                       p.destroy();
+
+                       return false;
+        });
+
+
+        $(window).resize(function(){
+            $('iframe').height($(window).height() - $('#tabs').outerHeight() - $('#source-editor .toolbar').outerHeight());
+            splitterWidth = splitter.width();
+        });
+
+        $(window).resize();
+
+        vsplitbar.toggle(
+                       function() {
+                               $.wiki.state.perspectives.ScanGalleryPerspective.show = true;
+                               setSplitbarAt(splitterWidth - (480 + vsplitbarWidth));
+                               $('.vsplitbar').addClass('active');
+                               $(window).resize();
+                               $.wiki.perspectiveForTab('#tabs-right .active').onEnter();
+                       },
+                       function() {
+                           var active_right = $.wiki.perspectiveForTab('#tabs-right .active');
+                               $.wiki.state.perspectives.ScanGalleryPerspective.show = false;
+                               $(".vsplitbar-title").html("&uarr;&nbsp;" + active_right.vsplitbar + "&nbsp;&uarr;");
+                               setSplitbarAt(splitterWidth - vsplitbarWidth);
+                               $('.vsplitbar').removeClass('active');
+                               $(window).resize();
+                               active_right.onExit();
+                       }
+               );
+
+
+        /* Splitbar dragging support */
+        vsplitbar
+            .mousedown(function(e) {
+                e.preventDefault();
+                isHolding = true;
+            })
+            .mousemove(function(e) {
+                if(isHolding) {
+                    dragLayer.show(); // We don't show it up until now so that we don't lose single click events on vsplitbar
+                }
+            });
+        dragLayer.mousemove(function(e) {
+            setSplitbarAt(e.clientX - vsplitbarWidth/2);
+        });
+        $('body').mouseup(function(e) {
+            dragLayer.hide();
+            isHolding = false;
+        });
+
+
+               if($.wiki.state.perspectives.ScanGalleryPerspective.show){
+            $('.vsplitbar').trigger('click');
+            $(".vsplitbar-title").html("&darr;&nbsp;GALERIA&nbsp;&darr;");
+        } else {
+            $(".vsplitbar-title").html("&uarr;&nbsp;GALERIA&nbsp;&uarr;");
+        }
+        window.onbeforeunload = function(e) {
+            if($.wiki.isDirty()) {
+                               e.returnValue = "Na stronie mogą być nie zapisane zmiany.";
+                               return "Na stronie mogą być nie zapisane zmiany.";
+                       };
+        };
+
+               console.log("Fetching document's text");
+
+               $(document).bind('wlapi_document_changed', function(event, doc) {
+                       try {
+                               $('#document-revision').text(doc.revision);
+                       } catch(e) {
+                               console.log("Failed handler", e);
+                       }
+               });
+
+               CurrentDocument.fetch({
+                       success: function(){
+                               console.log("Fetch success");
+                               $('#loading-overlay').fadeOut();
+                               var active_tab = document.location.hash || DEFAULT_PERSPECTIVE;
+
+                               if(active_tab == "#ScanGalleryPerspective")
+                                       active_tab = DEFAULT_PERSPECTIVE;
+
+                               console.log("Initial tab is:", active_tab)
+                               $.wiki.switchToTab(active_tab);
+
+                /* every 5 minutes check for a newer version */
+                var revTimer = setInterval(function() {
+                        CurrentDocument.checkRevision({outdated: function(){
+                            $('#header').addClass('out-of-date');
+                            clearInterval(revTimer);
+                        }});
+                    }, 300000);
+                       },
+                       failure: function() {
+                               $('#loading-overlay').fadeOut();
+                               alert("FAILURE");
+                       }
+               });
+    }; /* end of initialize() */
+
+
+       /* Load configuration */
+       $.wiki.loadConfig();
+
+       var initAll = function(a, f) {
+               if (a.length == 0) return f();
+
+               $.wiki.initTab({
+                       tab: a.pop(),
+                       doc: CurrentDocument,
+                       callback: function(){
+                               initAll(a, f);
+                       }
+               });
+       };
+
+
+       /*
+        * Initialize all perspectives
+        */
+       initAll( $.makeArray($('.tabs li')), initialize);
+       console.log(location.hash);
+});
diff --git a/src/redakcja/static/js/wiki/loader_readonly.js b/src/redakcja/static/js/wiki/loader_readonly.js
new file mode 100644 (file)
index 0000000..a859ec6
--- /dev/null
@@ -0,0 +1,93 @@
+if (!window.console) {
+    window.console = {
+        log: function(){
+        }
+    }
+}
+
+
+DEFAULT_PERSPECTIVE = "#VisualPerspective";
+
+$(function()
+{
+       var tabs = $('ol#tabs li');
+       var gallery = null;
+
+       CurrentDocument = new $.wikiapi.WikiDocument("document-meta");
+       $.blockUI.defaults.baseZ = 10000;
+
+       function initialize()
+       {
+               $('.editor').hide();
+
+               /*
+                * TABS
+                */
+        $('#tabs li').live('click', function(event, callback) {
+            event.preventDefault();
+                       $.wiki.switchToTab(this);
+        });
+
+               $('#tabs li > .tabclose').live('click', function(event, callback) {
+                       var $tab = $(this).parent();
+
+                       if($tab.is('.active'))
+                               $.wiki.switchToTab(DEFAULT_PERSPECTIVE);
+
+                       var p = $.wiki.perspectiveForTab($tab);
+                       p.destroy();
+                       return false;
+        });
+
+        $(window).resize(function(){
+            $('iframe').height($(window).height() - $('#tabs').outerHeight() - $('#source-editor .toolbar').outerHeight());
+        });
+
+               $(document).bind('wlapi_document_changed', function(event, doc) {
+                       try {
+                               $('#document-revision').text(doc.revision);
+                       } catch(e) {
+                               console.log("Failed handler", e);
+                       }
+               });
+
+               CurrentDocument.fetch({
+                       success: function(){
+                               console.log("Fetch success");
+                               $('#loading-overlay').fadeOut();
+                               var active_tab = document.location.hash || DEFAULT_PERSPECTIVE;
+
+                               $(window).resize();
+
+                               console.log("Initial tab is:", active_tab)
+                               $.wiki.switchToTab(active_tab);
+                       },
+                       failure: function() {
+                               $('#loading-overlay').fadeOut();
+                               alert("FAILURE");
+                       }
+               });
+    }; /* end of initialize() */
+
+       /* Load configuration */
+       $.wiki.loadConfig();
+
+       var initAll = function(a, f) {
+               if (a.length == 0) return f();
+
+               $.wiki.initTab({
+                       tab: a.pop(),
+                       doc: CurrentDocument,
+                       callback: function(){
+                               initAll(a, f);
+                       }
+               });
+       };
+
+
+       /*
+        * Initialize all perspectives
+        */
+       initAll( $.makeArray($('ol#tabs li')), initialize);
+       console.log(location.hash);
+});
\ No newline at end of file
diff --git a/src/redakcja/static/js/wiki/toolbar.js b/src/redakcja/static/js/wiki/toolbar.js
new file mode 100644 (file)
index 0000000..3eafdae
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Toolbar plugin.
+ */
+(function($) {
+
+    $.fn.toolbarize = function(options) {
+        var $toolbar = $(this);
+        var $container = $('.button_group_container', $toolbar);
+
+        $('.button_group button', $toolbar).click(function(event){
+            event.preventDefault();
+
+            var params = eval("(" + $(this).attr('data-ui-action-params') + ")");
+
+            scriptletCenter.callInteractive({
+                action: $(this).attr('data-ui-action'),
+                context: options.actionContext,
+                extra: params
+            });
+        });
+
+        $toolbar.children().filter('select').change(function(event){
+            var slug = $(this).val();
+            $container.scrollLeft(0);
+            $('*[data-group]').hide().filter('[data-group=' + slug + ']').show();
+        }).change();
+
+        $('button.next', $toolbar).click(function() {
+            var $current_group = $('.button_group:visible', $toolbar);
+            var scroll = $container.scrollLeft();
+
+            var $hidden = $("li", $current_group).filter(function() {
+                return ($(this).position().left + $(this).outerWidth()) > $container.width();
+            }).first();
+
+            if($hidden.length > 0) {
+                scroll = $hidden.position().left + scroll + $hidden.outerWidth() - $container.width() + 1;
+                $container.scrollLeft(scroll);
+            };
+        });
+
+        $('button.prev', $toolbar).click(function() {
+            var $current_group = $('.button_group:visible', $toolbar);
+            var scroll = $container.scrollLeft();
+
+            /* first not visible element is: */
+            var $hidden = $("li", $current_group).filter(function() {
+                return $(this).position().left < 0;
+            }).last();
+
+            if( $hidden.length > 0)
+            {
+                scroll = scroll + $hidden.position().left;
+                $container.scrollLeft(scroll);
+            };
+        });
+
+    };
+
+})(jQuery);
\ No newline at end of file
diff --git a/src/redakcja/static/js/wiki/view_annotations.js b/src/redakcja/static/js/wiki/view_annotations.js
new file mode 100644 (file)
index 0000000..b55effd
--- /dev/null
@@ -0,0 +1,180 @@
+(function($){
+
+    /*
+     * Perspective
+     */
+    function AnnotationsPerspective(options){
+        var old_callback = options.callback || function() { };
+
+        this.noupdate_hash_onenter = true;
+        this.vsplitbar = 'PRZYPISY';
+
+        options.callback = function(){
+            var self = this;
+
+            this.$element = $("#side-annotations");
+            this.$error = $('.error-message', this.$element);
+            this.$annos = $('.annotations-list', this.$element);
+            this.$spinner = $('.spinner', this.$element);
+            this.$refresh = $('.refresh', this.$element);
+
+            this.$refresh.click(function() {
+                var $this = $(this);
+
+                self.$refresh.removeClass('active');
+                $this.addClass('active');
+                var atype = $this.attr('data-tag');
+
+                self.$annos.hide();
+                self.$error.hide();
+                self.$spinner.show(100, function(){
+                    self.refresh(self, atype);
+                });
+            });
+
+            old_callback.call(this);
+        };
+
+        $.wiki.Perspective.call(this, options);
+    }
+
+    AnnotationsPerspective.prototype = new $.wiki.Perspective();
+
+    AnnotationsPerspective.prototype.updateAnnotationIds = function(self){
+        self.annotationToAnchor = {};
+        $('#html-view').find('.annotation-inline-box').each(
+            function(i, annoBox) {
+                var $annoBox = $(annoBox);
+                var $anchor = $("a[name|=anchor]", $annoBox);
+                var htmlContent = $('span', $annoBox).html();
+                // TBD: perhaps use a hash of htmlContent as key
+                self.annotationToAnchor[htmlContent] = $anchor.attr('name');
+                });
+    };
+
+    AnnotationsPerspective.prototype.goToAnnotation = function(self, srcNode){
+        var content = $(srcNode).html();
+        content = content.replace(/&gt;/g, '>').replace(/&lt;/g, '<').replace(/&amp;/g, '&');
+        xml2html({
+            xml: '<root>'+content+'</root>',
+            success: function(txt) {
+                content = $(txt).html();
+                },
+            error: function(text) {
+                $.unblockUI();
+                self.$error.html(text);
+                self.$spinner.hide();
+                self.$error.show();
+            }
+        });
+
+        var anchor = self.annotationToAnchor[content];
+        if (anchor != undefined) {
+            var $htmlView = $("#html-view");
+            var top = $htmlView.offset().top + 
+                $("[name=" + anchor + "]", $htmlView).offset().top - 
+                $htmlView.children().eq(0).offset().top;
+
+            $htmlView.animate({scrollTop: top}, 250);
+        }
+    };
+
+    AnnotationsPerspective.prototype.refresh = function(self, atype) {
+        var xml;
+
+        var persp = $.wiki.activePerspective();
+        if (persp == 'CodeMirrorPerspective') {
+            xml = $.wiki.perspectives[persp].codemirror.getCode();
+        }
+        else if (persp == 'VisualPerspective') {
+            html2text({
+                element: $('#html-view').find('div').get(0),
+                success: function(text){
+                    xml = text;
+                },
+                error: function(text){
+                    self.$error.html('<p>Wystąpił błąd:</p><pre>' + text + '</pre>');
+                    self.$spinner.hide();
+                    self.$error.show();
+                }
+            });
+            self.updateAnnotationIds(self);
+        }
+        else {
+            xml = this.doc.text;
+        }
+
+        var parser = new DOMParser();
+        var serializer = new XMLSerializer();
+        var doc = parser.parseFromString(xml, 'text/xml');
+        var error = $('parsererror', doc);
+
+        if (error.length > 0) {
+            self.$error.html('Błąd parsowania XML.');
+            self.$spinner.hide();
+            self.$error.show();
+        }
+        else {
+            self.$annos.html('');
+            var anno_list = [];
+            var annos = $(atype, doc);
+            var counter = annos.length;
+            var atype_rx = atype.replace(/,/g, '|');
+            var ann_expr = new RegExp("^<("+atype_rx+")[^>]*>|</("+atype_rx+")>$", "g");
+
+            if (annos.length == 0)
+            {
+                self.$annos.html('Nie ma żadnych przypisów');
+                self.$spinner.hide();
+                self.$annos.show();
+            }
+            annos.each(function (i, elem) {
+                var xml_text = serializer.serializeToString(elem).replace(ann_expr, "");
+                xml2html({
+                    xml: "<akap>" + xml_text + "</akap>",
+                    success: function(xml_text){
+                        return function(elem){
+                            elem.sortby = $(elem).text().trim();
+                            $(elem).append("<div class='src'>"+ xml_text.replace(/&/g, "&amp;").replace(/</g, "&lt;") +"</div>");
+                            anno_list.push(elem);
+                            $(".src", elem).click(function() { self.goToAnnotation(self, this); });
+                            counter--;
+
+                            if (!counter) {
+                                anno_list.sort(function(a, b){return a.sortby.localeCompare(b.sortby);});
+                                for (i in anno_list)
+                                    self.$annos.append(anno_list[i]);
+                                self.$spinner.hide();
+                                self.$annos.show();
+                            }
+
+                        }
+                    }(xml_text),
+                    error: function(text) {
+                        $.unblockUI();
+                        self.$error.html(text);
+                        self.$spinner.hide();
+                        self.$error.show();
+                    }
+                });
+            });
+        }
+    };
+
+
+    AnnotationsPerspective.prototype.onEnter = function(){
+        $.wiki.Perspective.prototype.onEnter.call(this);
+
+        $('.vsplitbar').not('.active').trigger('click');
+        $(".vsplitbar-title").html("&darr;&nbsp;PRZYPISY&nbsp;&darr;");
+        this.$refresh.filter('.active').trigger('click');
+
+    };
+
+        AnnotationsPerspective.prototype.onExit = function(success, failure) {
+
+        };
+
+    $.wiki.AnnotationsPerspective = AnnotationsPerspective;
+
+})(jQuery);
diff --git a/src/redakcja/static/js/wiki/view_column_diff.js b/src/redakcja/static/js/wiki/view_column_diff.js
new file mode 100644 (file)
index 0000000..33ffb6a
--- /dev/null
@@ -0,0 +1,35 @@
+(function($){
+
+       function DiffPerspective(options) {
+               var old_callback = options.callback || function() {};
+               var self = this;
+
+        options.callback = function(){
+                       self.base_id = options.base_id;
+                       old_callback.call(this);
+               };
+
+               $.wiki.Perspective.call(this, options);
+    };
+
+    DiffPerspective.prototype = new $.wiki.Perspective();
+
+    DiffPerspective.prototype.freezeState = function(){
+        // must
+    };
+
+       DiffPerspective.prototype.destroy = function() {
+        $.wiki.switchToTab('#HistoryPerspective');
+               $('#' + this.base_id).remove();
+               $('#' + this.perspective_id).remove();
+       };
+
+       DiffPerspective.prototype.onEnter = function(success, failure){
+               $.wiki.Perspective.prototype.onEnter.call(this);
+               console.log("Entered diff view");
+       };
+
+       $.wiki.DiffPerspective = DiffPerspective;
+
+})(jQuery);
+
diff --git a/src/redakcja/static/js/wiki/view_editor_source.js b/src/redakcja/static/js/wiki/view_editor_source.js
new file mode 100644 (file)
index 0000000..547456f
--- /dev/null
@@ -0,0 +1,109 @@
+/* COMMENT */
+(function($) {
+
+       function CodeMirrorPerspective(options)
+       {
+               var old_callback = options.callback;
+        options.callback = function(){
+                       var self = this;
+
+                       this.codemirror = CodeMirror.fromTextArea('codemirror_placeholder', {
+                               parserfile: 'parsexml.js',
+                               path: STATIC_URL + "js/lib/codemirror-0.8/",
+                               stylesheet: STATIC_URL + "css/xmlcolors_20100906.css",
+                               parserConfig: {
+                                       useHTMLKludges: false
+                               },
+                               iframeClass: 'xml-iframe',
+                               textWrapping: true,
+                               lineNumbers: true,
+                               width: "100%",
+                               height: "100%",
+                               tabMode: 'default',
+                               indentUnit: 0,
+                               readOnly: CurrentDocument.readonly || false,
+                               initCallback: function(){
+
+                                       self.codemirror.grabKeys(function(event) {
+                                               if (event.button) {
+                                                       $(event.button).trigger('click');
+                                                       event.button = null;
+                                               }
+                                       }, function(keycode, event) {
+                                               if(!event.altKey)
+                                                       return false;
+
+                                               var c = String.fromCharCode(keycode).toLowerCase();
+                                               var button = $("#source-editor button[data-ui-accesskey='"+c+"']");
+                                               if(button.length == 0)
+                                                       return false;
+
+                                               /* it doesn't matter which button we pick - all do the same */
+                                               event.button = button[0];
+                                               return true;
+                                       });
+
+                                       $('#source-editor .toolbar').toolbarize({
+                                           actionContext: self.codemirror
+                                       });
+
+                                       console.log("Initialized CodeMirror");
+
+                                       // textarea is no longer needed
+                                       $('codemirror_placeholder').remove();
+
+                                       old_callback.call(self);
+                               }
+                       });
+               };
+
+               $.wiki.Perspective.call(this, options);
+       };
+
+
+       CodeMirrorPerspective.prototype = new $.wiki.Perspective();
+
+       CodeMirrorPerspective.prototype.freezeState = function() {
+               this.config().position = this.codemirror.win.scrollY || 0;
+       };
+
+       CodeMirrorPerspective.prototype.unfreezeState = function () {
+               this.codemirror.win.scroll(0, this.config().position || 0);
+       };
+
+       CodeMirrorPerspective.prototype.onEnter = function(success, failure) {
+               $.wiki.Perspective.prototype.onEnter.call(this);
+
+               console.log('Entering', this.doc);
+               this.codemirror.setCode(this.doc.text);
+
+               /* fix line numbers bar */
+               var $nums = $('.CodeMirror-line-numbers');
+           var barWidth = $nums.width();
+
+               $(this.codemirror.frame.contentDocument.body).css('padding-left', barWidth);
+               // $nums.css('left', -barWidth);
+
+               $(window).resize();
+               this.unfreezeState(this._uistate);
+
+               if(success) success();
+       }
+
+       CodeMirrorPerspective.prototype.onExit = function(success, failure) {
+               this.freezeState();
+
+               $.wiki.Perspective.prototype.onExit.call(this);
+               console.log('Exiting', this.doc);
+               this.doc.setText(this.codemirror.getCode());
+
+        if ($('.vsplitbar').hasClass('active') && $('#SearchPerspective').hasClass('active')) {
+            $.wiki.switchToTab('#ScanGalleryPerspective');
+        }
+
+               if(success) success();
+       }
+
+       $.wiki.CodeMirrorPerspective = CodeMirrorPerspective;
+
+})(jQuery);
diff --git a/src/redakcja/static/js/wiki/view_editor_wysiwyg.js b/src/redakcja/static/js/wiki/view_editor_wysiwyg.js
new file mode 100644 (file)
index 0000000..3ec4f70
--- /dev/null
@@ -0,0 +1,676 @@
+(function($){
+
+    /* Show theme to the user */
+    function selectTheme(themeId){
+        var selection = window.getSelection();
+        selection.removeAllRanges();
+
+        var range = document.createRange();
+        var s = $(".motyw[theme-class='" + themeId + "']")[0];
+        var e = $(".end[theme-class='" + themeId + "']")[0];
+
+        if (s && e) {
+            range.setStartAfter(s);
+            range.setEndBefore(e);
+            selection.addRange(range);
+        }
+    };
+
+    /* Verify insertion port for annotation or theme */
+    function verifyTagInsertPoint(node){
+        if (node.nodeType == 3) { // Text Node
+            node = node.parentNode;
+        }
+
+        if (node.nodeType != 1) {
+            return false;
+        }
+
+        node = $(node);
+        var xtype = node.attr('x-node');
+
+        if (!xtype || (xtype.search(':') >= 0) ||
+        xtype == 'motyw' ||
+        xtype == 'begin' ||
+        xtype == 'end') {
+            return false;
+        }
+
+        // don't allow themes inside annotations
+        if (node.closest('[x-node="pe"]').length > 0)
+            return false;
+
+        return true;
+    }
+
+    /* Convert HTML fragment to plaintext */
+    var ANNOT_FORBIDDEN = ['pt', 'pa', 'pr', 'pe', 'begin', 'end', 'motyw'];
+
+    function html2plainText(fragment){
+        var text = "";
+
+        $(fragment.childNodes).each(function(){
+            if (this.nodeType == 3) // textNode
+                text += this.nodeValue;
+            else {
+                if (this.nodeType == 1 &&
+                        $.inArray($(this).attr('x-node'), ANNOT_FORBIDDEN) == -1) {
+                    text += html2plainText(this);
+                }
+            };
+        });
+
+        return text;
+    }
+
+
+    /* Insert annotation using current selection */
+    function addAnnotation(){
+        var selection = window.getSelection();
+        var n = selection.rangeCount;
+
+        if (n == 0) {
+            window.alert("Nie zaznaczono żadnego obszaru");
+            return false;
+        }
+
+        // for now allow only 1 range
+        if (n > 1) {
+            window.alert("Zaznacz jeden obszar");
+            return false;
+        }
+
+        // remember the selected range
+        var range = selection.getRangeAt(0);
+
+        if (!verifyTagInsertPoint(range.endContainer)) {
+            window.alert("Nie można wstawić w to miejsce przypisu.");
+            return false;
+        }
+
+        // BUG #273 - selected text can contain themes, which should be omitted from
+        // defining term
+        var text = html2plainText(range.cloneContents());
+        var tag = $('<span></span>');
+        range.collapse(false);
+        range.insertNode(tag[0]);
+
+        xml2html({
+            xml: '<pe><slowo_obce>' + text + '</slowo_obce> --- </pe>',
+            success: function(text){
+                var t = $(text);
+                tag.replaceWith(t);
+                openForEdit(t);
+            },
+            error: function(){
+                tag.remove();
+                alert('Błąd przy dodawaniu przypisu:' + errors);
+            }
+        })
+    }
+
+
+    /* Insert theme using current selection */
+
+    function addTheme(){
+        var selection = window.getSelection();
+        var n = selection.rangeCount;
+
+        if (n == 0) {
+            window.alert("Nie zaznaczono żadnego obszaru");
+            return false;
+        }
+
+        // for now allow only 1 range
+        if (n > 1) {
+            window.alert("Zaznacz jeden obszar.");
+            return false;
+        }
+
+
+        // remember the selected range
+        var range = selection.getRangeAt(0);
+
+
+        if ($(range.startContainer).is('.html-editarea') ||
+        $(range.endContainer).is('.html-editarea')) {
+            window.alert("Motywy można oznaczać tylko na tekście nie otwartym do edycji. \n Zamknij edytowany fragment i spróbuj ponownie.");
+            return false;
+        }
+
+        // verify if the start/end points make even sense -
+        // they must be inside a x-node (otherwise they will be discarded)
+        // and the x-node must be a main text
+        if (!verifyTagInsertPoint(range.startContainer)) {
+            window.alert("Motyw nie może się zaczynać w tym miejscu.");
+            return false;
+        }
+
+        if (!verifyTagInsertPoint(range.endContainer)) {
+            window.alert("Motyw nie może się kończyć w tym miejscu.");
+            return false;
+        }
+
+        var date = (new Date()).getTime();
+        var random = Math.floor(4000000000 * Math.random());
+        var id = ('' + date) + '-' + ('' + random);
+
+        var createPoint = function(container, offset) {
+            var offsetBetweenCommas = function(text, offset) {
+                if(text.length < 2 || offset < 1 || offset > text.length)
+                    return false;
+                return text[offset-1] === ',' && text[offset] === ',';
+            }
+            var point = document.createRange();
+            offset = offsetBetweenCommas(container.textContent, offset) ? offset - 1 : offset;
+            point.setStart(container, offset);
+            return point;
+        }
+        
+        var spoint = createPoint(range.startContainer, range.startOffset);
+        var epoint = createPoint(range.endContainer, range.endOffset);
+               
+        var mtag, btag, etag, errors;
+
+        // insert theme-ref
+
+        xml2html({
+            xml: '<end id="e' + id + '" />',
+            success: function(text){
+                etag = $('<span></span>');
+                epoint.insertNode(etag[0]);
+                etag.replaceWith(text);
+                xml2html({
+                    xml: '<motyw id="m' + id + '"></motyw>',
+                    success: function(text){
+                        mtag = $('<span></span>');
+                        spoint.insertNode(mtag[0]);
+                        mtag.replaceWith(text);
+                        xml2html({
+                            xml: '<begin id="b' + id + '" />',
+                            success: function(text){
+                                btag = $('<span></span>');
+                                spoint.insertNode(btag[0])
+                                btag.replaceWith(text);
+                                selection.removeAllRanges();
+                                openForEdit($('.motyw[theme-class=' + id + ']'));
+                            }
+                        });
+                    }
+                });
+            }
+        });
+    }
+
+    function addSymbol() {
+        if($('div.html-editarea textarea')[0]) {
+            var specialCharsContainer = $("<div id='specialCharsContainer'><a href='#' id='specialCharsClose'>Zamknij</a><table id='tableSpecialChars' style='width: 600px;'></table></div>");
+                        
+            var specialChars = [' ', 'Ą','ą','Ć','ć','Ę','ę','Ł','ł','Ń','ń','Ó','ó','Ś','ś','Ż','ż','Ź','ź','Á','á','À','à',
+            'Â','â','Ä','ä','Å','å','Ā','ā','Ă','ă','Ã','ã',
+            'Æ','æ','Ç','ç','Č','č','Ċ','ċ','Ď','ď','É','é','È','è',
+            'Ê','ê','Ë','ë','Ē','ē','Ě','ě','Ġ','ġ','Ħ','ħ','Í','í','Î','î',
+            'Ī','ī','Ĭ','ĭ','Ľ','ľ','Ñ','ñ','Ň','ň','Ó','ó','Ö','ö',
+            'Ô','ô','Ō','ō','Ǒ','ǒ','Œ','œ','Ø','ø','Ř','ř','Š',
+            'š','Ş','ş','Ť','ť','Ţ','ţ','Ű','ű','Ú','ú','Ù','ù',
+            'Ü','ü','Ů','ů','Ū','ū','Û','û','Ŭ','ŭ',
+            'Ý','ý','Ž','ž','ß','Ð','ð','Þ','þ','А','а','Б',
+            'б','В','в','Г','г','Д','д','Е','е','Ё','ё','Ж',
+            'ж','З','з','И','и','Й','й','К','к','Л','л','М',
+            'м','Н','н','О','о','П','п','Р','р','С','с',
+            'Т','т','У','у','Ф','ф','Х','х','Ц','ц','Ч',
+            'ч','Ш','ш','Щ','щ','Ъ','ъ','Ы','ы','Ь','ь','Э',
+            'э','Ю','ю','Я','я','ѓ','є','і','ї','ј','љ','њ',
+            'Ґ','ґ','Α','α','Β','β','Γ','γ','Δ','δ','Ε','ε',
+            'Ζ','ζ','Η','η','Θ','θ','Ι','ι','Κ','κ','Λ','λ','Μ',
+            'μ','Ν','ν','Ξ','ξ','Ο','ο','Π','π','Ρ','ρ','Σ','ς','σ',
+            'Τ','τ','Υ','υ','Φ','φ','Χ','χ','Ψ','ψ','Ω','ω','–',
+            '—','¡','¿','$','¢','£','€','©','®','°','¹','²','³',
+            '¼','½','¾','†','§','‰','•','←','↑','→','↓',
+            '„','”','„”','«','»','«»','»«','’','[',']','~','|','−','·',
+            '×','÷','≈','≠','±','≤','≥','∈'];
+            var tableContent = "<tr>";
+            
+            for(var i in specialChars) {
+                if(i % 14 == 0 && i > 0) {
+                    tableContent += "</tr><tr>";
+                }              
+                tableContent += "<td><input type='button' class='specialBtn' value='"+specialChars[i]+"'/></td>";              
+            }
+            
+            tableContent += "</tr>";                                   
+            $("#content").append(specialCharsContainer);
+            
+            
+             // localStorage for recently used characters - reading
+             if (typeof(localStorage) != 'undefined') {
+                 if (localStorage.getItem("recentSymbols")) {
+                     var recent = localStorage.getItem("recentSymbols");
+                     var recentArray = recent.split(";");
+                     var recentRow = "";
+                     for(var i in recentArray.reverse()) {
+                        recentRow += "<td><input type='button' class='specialBtn recentSymbol' value='"+recentArray[i]+"'/></td>";              
+                     }
+                     recentRow = "<tr>" + recentRow + "</tr>";                              
+                 }
+             }            
+            $("#tableSpecialChars").append(recentRow);
+            $("#tableSpecialChars").append(tableContent);
+            
+            /* events */
+            
+            $('.specialBtn').click(function(){
+                var editArea = $('div.html-editarea textarea')[0];
+                var insertVal = $(this).val();
+                
+                // if we want to surround text with quotes
+                // not sure if just check if value has length == 2
+                
+                if (insertVal.length == 2) {
+                    var startTag = insertVal[0];
+                    var endTag = insertVal[1];
+                               var textAreaOpened = editArea;                                                  
+                               //IE support
+                               if (document.selection) {
+                                   textAreaOpened.focus();
+                                   sel = document.selection.createRange();
+                                   sel.text = startTag + sel.text + endTag;
+                               }
+                               //MOZILLA/NETSCAPE support
+                               else if (textAreaOpened.selectionStart || textAreaOpened.selectionStart == '0') {
+                                   var startPos = textAreaOpened.selectionStart;
+                                   var endPos = textAreaOpened.selectionEnd;
+                                   textAreaOpened.value = textAreaOpened.value.substring(0, startPos)
+                                         + startTag + textAreaOpened.value.substring(startPos, endPos) + endTag + textAreaOpened.value.substring(endPos, textAreaOpened.value.length);
+                               }                
+                } else {
+                    // if we just want to insert single symbol
+                    insertAtCaret(editArea, insertVal);
+                }
+                
+                // localStorage for recently used characters - saving
+                if (typeof(localStorage) != 'undefined') {
+                    if (localStorage.getItem("recentSymbols")) {
+                        var recent = localStorage.getItem("recentSymbols");
+                        var recentArray = recent.split(";");
+                        var valIndex = $.inArray(insertVal, recentArray);
+                        //alert(valIndex);
+                        if(valIndex == -1) {
+                            // value not present in array yet
+                            if(recentArray.length > 13){
+                                recentArray.shift();
+                                recentArray.push(insertVal);
+                            } else {
+                                recentArray.push(insertVal);
+                            }
+                        } else  {
+                            // value already in the array
+                            for(var i = valIndex; i < recentArray.length; i++){
+                                recentArray[i] = recentArray[i+1];
+                            }
+                            recentArray[recentArray.length-1] = insertVal;
+                        }
+                        localStorage.setItem("recentSymbols", recentArray.join(";"));
+                    } else {
+                        localStorage.setItem("recentSymbols", insertVal);
+                    }
+                }
+                $(specialCharsContainer).remove();
+            });         
+            $('#specialCharsClose').click(function(){
+                $(specialCharsContainer).remove();
+            });                   
+            
+        } else {
+            window.alert('Najedź na fragment tekstu, wybierz "Edytuj" i ustaw kursor na miejscu gdzie chcesz wstawić symbol.');
+        }
+    }
+
+    function insertAtCaret(txtarea,text) { 
+        /* http://www.scottklarr.com/topic/425/how-to-insert-text-into-a-textarea-where-the-cursor-is/ */
+        var scrollPos = txtarea.scrollTop; 
+        var strPos = 0; 
+        var backStart = 0;
+        var br = ((txtarea.selectionStart || txtarea.selectionStart == '0') ? "ff" : (document.selection ? "ie" : false ) );
+        if (br == "ie") { 
+            txtarea.focus();
+            var range = document.selection.createRange(); 
+            range.moveStart ('character', -txtarea.value.length); 
+            strPos = backStart = range.text.length; 
+        } else if (br == "ff") {
+            strPos = txtarea.selectionStart; 
+            backStart = txtarea.selectionEnd;
+        }
+        var front = (txtarea.value).substring(0,strPos); 
+        var back = (txtarea.value).substring(backStart,txtarea.value.length); 
+        txtarea.value=front+text+back; 
+        strPos = strPos + text.length; 
+        if (br == "ie") { 
+            txtarea.focus(); 
+            var range = document.selection.createRange(); 
+            range.moveStart ('character', -txtarea.value.length); 
+            range.moveStart ('character', strPos); 
+            range.moveEnd ('character', 0); 
+            range.select(); 
+        } else if (br == "ff") { 
+            txtarea.selectionStart = strPos; 
+            txtarea.selectionEnd = strPos; 
+            txtarea.focus(); 
+        } 
+        txtarea.scrollTop = scrollPos; 
+    } 
+
+    /* open edition window for selected fragment */
+    function openForEdit($origin){
+        var $box = null
+
+        // annotations overlay their sub box - not their own box //
+        if ($origin.is(".annotation-inline-box")) {
+            $box = $("*[x-annotation-box]", $origin);
+        }
+        else {
+            $box = $origin;
+        }
+        var x = $box[0].offsetLeft;
+        var y = $box[0].offsetTop;        
+        
+        var w = $box.outerWidth();
+        var h = $box.innerHeight();
+
+        if ($origin.is(".annotation-inline-box")) {
+            w = Math.max(w, 400);
+            h = Math.max(h, 60);
+            if($('.htmlview div').offset().left + $('.htmlview div').width() > ($('.vsplitbar').offset().left - 480)){
+                x = -(Math.max($origin.offset().left, $origin.width())); 
+            } else {
+                x = 100;
+            }
+        }
+
+        // start edition on this node
+        var $overlay = $('<div class="html-editarea"><button class="accept-button">Zapisz</button><button class="delete-button">Usuń</button><button class="tytul-button akap-edit-button">tytuł dzieła</button><button class="wyroznienie-button akap-edit-button">wyróżnienie</button><button class="slowo-button akap-edit-button">słowo obce</button><button class="znak-button akap-edit-button">znak spec.</button><textarea></textarea></div>').css({
+            position: 'absolute',
+            height: h,
+            left: x,
+            top: y,
+            width: w
+        }).appendTo($box[0].offsetParent || $box.parent()).show();
+        
+
+        if ($origin.is('.motyw')) {
+           $('.akap-edit-button').remove();
+            withThemes(function(canonThemes){
+                $('textarea', $overlay).autocomplete(canonThemes, {
+                    autoFill: true,
+                    multiple: true,
+                    selectFirst: true,
+                    highlight: false
+                });
+            })
+        }
+
+        if ($origin.is('.motyw')){
+            $('.delete-button', $overlay).click(function(){
+                if (window.confirm("Czy jesteś pewien, że chcesz usunąć ten motyw ?")) {
+                    $('[theme-class=' + $origin.attr('theme-class') + ']').remove();
+                    $overlay.remove();
+                    $(document).unbind('click.blur-overlay');
+                    return false;
+                };
+            });
+        }
+        else if($box.is('*[x-annotation-box]')) {
+            $('.delete-button', $overlay).click(function(){
+                if (window.confirm("Czy jesteś pewien, że chcesz usunąć ten przypis?")) {
+                    $origin.remove();
+                    $overlay.remove();
+                    $(document).unbind('click.blur-overlay');
+                    return false;
+                };
+            });
+        }
+        else {
+            $('.delete-button', $overlay).html("Anuluj");
+            $('.delete-button', $overlay).click(function(){
+                if (window.confirm("Czy jesteś pewien, że chcesz anulować zmiany?")) {
+                    $overlay.remove();
+                    $(document).unbind('click.blur-overlay');
+                    return false;
+                };
+            });
+        }
+
+
+        var serializer = new XMLSerializer();
+
+        html2text({
+            element: $box[0],
+            stripOuter: true,
+            success: function(text){
+                $('textarea', $overlay).val($.trim(text));
+
+                setTimeout(function(){
+                    $('textarea', $overlay).elastic().focus();
+                }, 50);
+
+                function save(argument){
+                    var nodeName = $box.attr('x-node') || 'pe';
+                    var insertedText = $('textarea', $overlay).val();
+
+                    if ($origin.is('.motyw')) {
+                        insertedText = insertedText.replace(/,\s*$/, '');
+                    }
+
+                    xml2html({
+                        xml: '<' + nodeName + '>' + insertedText + '</' + nodeName + '>',
+                        success: function(element){
+                            if (nodeName == 'out-of-flow-text') {
+                                $(element).children().insertAfter($origin);
+                                $origin.remove()
+                            }
+                            else {
+                                $origin.html($(element).html());
+                            }
+                            $overlay.remove();
+                        },
+                        error: function(text){
+                            alert('Błąd! ' + text);
+                        }
+                    })
+                    
+                    var msg = $("<div class='saveNotify'><p>Pamiętaj, żeby zapisać swoje zmiany.</p></div>");
+                    $("#base").prepend(msg);
+                    $('#base .saveNotify').fadeOut(3000, function(){
+                        $(this).remove(); 
+                    });
+                }
+
+               $('.akap-edit-button', $overlay).click(function(){
+                       var textAreaOpened = $('textarea', $overlay)[0];
+                       var startTag = "";
+                       var endTag = "";
+                       var buttonName = this.innerHTML;
+
+                       if(buttonName == "słowo obce") {
+                               startTag = "<slowo_obce>";
+                               endTag = "</slowo_obce>";
+                       } else if (buttonName == "wyróżnienie") {
+                               startTag = "<wyroznienie>";
+                               endTag = "</wyroznienie>";
+                       } else if (buttonName == "tytuł dzieła") {
+                               startTag = "<tytul_dziela>";
+                               endTag = "</tytul_dziela>";
+                       } else if(buttonName == "znak spec."){
+                           addSymbol();
+                           return false;
+                       }
+                       
+                       var myField = textAreaOpened;                   
+                        
+                       //IE support
+                       if (document.selection) {
+                           textAreaOpened.focus();
+                           sel = document.selection.createRange();
+                           sel.text = startTag + sel.text + endTag;
+                       }
+                       //MOZILLA/NETSCAPE support
+                       else if (textAreaOpened.selectionStart || textAreaOpened.selectionStart == '0') {
+                           var startPos = textAreaOpened.selectionStart;
+                           var endPos = textAreaOpened.selectionEnd;
+                           textAreaOpened.value = textAreaOpened.value.substring(0, startPos)
+                                 + startTag + textAreaOpened.value.substring(startPos, endPos) + endTag + textAreaOpened.value.substring(endPos, textAreaOpened.value.length);
+                       }
+               });
+
+                $('.accept-button', $overlay).click(function(){
+                    save();
+                    $(document).unbind('click.blur-overlay');
+                });
+
+                $(document).bind('click.blur-overlay', function(event){
+                    if ($(event.target).closest('.html-editarea, #specialCharsContainer').length > 0) {
+                        return;
+                    }
+                    save();
+                    $(document).unbind('click.blur-overlay');
+                });
+
+            },
+            error: function(text){
+                alert('Błąd! ' + text);
+            }
+        });
+    }
+
+    function VisualPerspective(options){
+
+        var old_callback = options.callback;
+
+        options.callback = function(){
+            var element = $("#html-view");
+            var button = $('<button class="edit-button">Edytuj</button>');
+
+            if (!CurrentDocument.readonly) {
+                $('#html-view').bind('mousemove', function(event){
+                    var editable = $(event.target).closest('*[x-editable]');
+                    $('.active', element).not(editable).removeClass('active').children('.edit-button').remove();
+
+                    if (!editable.hasClass('active')) {
+                        editable.addClass('active').append(button);
+                    }
+                    if (editable.is('.annotation-inline-box')) {
+                        $('*[x-annotation-box]', editable).css({
+                            position: 'absolute',
+                            left: event.clientX - editable.offset().left + 5,
+                            top: event.clientY - editable.offset().top + 5
+                        }).show();
+                    }
+                    else {
+                        $('*[x-annotation-box]').hide();
+                    }
+                });
+
+                $('#insert-annotation-button').click(function(){
+                    addAnnotation();
+                    return false;
+                });
+
+                $('#insert-theme-button').click(function(){
+                    addTheme();
+                    return false;
+                });            
+
+                $('.edit-button').live('click', function(event){
+                    event.preventDefault();
+                    openForEdit($(this).parent());
+                });
+
+            }
+
+            $('.motyw').live('click', function(){
+                selectTheme($(this).attr('theme-class'));
+            });
+
+            old_callback.call(this);
+        };
+
+        $.wiki.Perspective.call(this, options);
+    };
+
+    VisualPerspective.prototype = new $.wiki.Perspective();
+
+    VisualPerspective.prototype.freezeState = function(){
+
+    };
+
+    VisualPerspective.prototype.onEnter = function(success, failure){
+        $.wiki.Perspective.prototype.onEnter.call(this);
+
+        $.blockUI({
+            message: 'Uaktualnianie widoku...'
+        });
+
+        function _finalize(callback){
+            $.unblockUI();
+            if (callback)
+                callback();
+        }
+
+        xml2html({
+            xml: this.doc.text,
+            success: function(element){
+                var htmlView = $('#html-view');
+                htmlView.html(element);
+                htmlView.find('*[x-node]').dblclick(function(e) {
+                    if($(e.target).is('textarea'))
+                        return;
+                    var selection = window.getSelection();
+                    selection.collapseToStart();
+                    selection.modify('extend', 'forward', 'word');
+                    e.stopPropagation();
+                });
+                _finalize(success);
+            },
+            error: function(text, source){
+                err = '<p class="error">Wystąpił błąd:</p><p>'+text+'</p>';
+                if (source)
+                    err += '<pre>'+source.replace(/&/g, '&amp;').replace(/</g, '&lt;')+'</pre>'
+                $('#html-view').html(err);
+                _finalize(failure);
+            }
+        });
+    };
+
+    VisualPerspective.prototype.onExit = function(success, failure){
+        var self = this;
+
+        $.blockUI({
+            message: 'Zapisywanie widoku...'
+        });
+
+        function _finalize(callback){
+            $.unblockUI();
+            if (callback)
+                callback();
+        }
+
+        if ($('#html-view .error').length > 0)
+            return _finalize(failure);
+
+        html2text({
+            element: $('#html-view').get(0),
+            stripOuter: true,
+            success: function(text){
+                self.doc.setText(text);
+                _finalize(success);
+            },
+            error: function(text){
+                $('#source-editor').html('<p>Wystąpił błąd:</p><pre>' + text + '</pre>');
+                _finalize(failure);
+            }
+        });
+    };
+
+    $.wiki.VisualPerspective = VisualPerspective;
+
+})(jQuery);
diff --git a/src/redakcja/static/js/wiki/view_gallery.js b/src/redakcja/static/js/wiki/view_gallery.js
new file mode 100644 (file)
index 0000000..65a716a
--- /dev/null
@@ -0,0 +1,259 @@
+(function($){
+
+    function normalizeNumber(pageNumber, pageCount){
+        // Page number should be >= 1, <= pageCount; 0 if pageCount = 0
+        var pageNumber = parseInt(pageNumber, 10);
+
+        if (!pageCount)
+            return 0;
+
+        if (!pageNumber ||
+                isNaN(pageNumber) ||
+                pageNumber == Infinity ||
+                pageNumber == -Infinity ||
+                pageNumber < 1)
+            return 1;
+
+        if (pageNumber > pageCount)
+            return pageCount;
+
+        return pageNumber;
+    }
+
+    function bounds(galleryWidth, galleryHeight, imageWidth, imageHeight){
+        return {
+            maxX: 0,
+            maxY: 0,
+            minX: galleryWidth - imageWidth,
+            minY: galleryHeight - imageHeight
+        }
+    };
+
+    function normalizePosition(x, y, galleryWidth, galleryHeight, imageWidth, imageHeight){
+        var b = bounds(galleryWidth, galleryHeight, imageWidth, imageHeight);
+        return {
+            x: Math.min(b.maxX, Math.max(b.minX, x)),
+            y: Math.min(b.maxY, Math.max(b.minY, y))
+        }
+    };
+
+    function fixImageSize(){
+
+    }
+
+    /*
+     * Perspective
+     */
+    function ScanGalleryPerspective(options){
+        var old_callback = options.callback || function() { };
+
+               this.noupdate_hash_onenter = true;
+        this.vsplitbar = 'GALERIA';
+
+        options.callback = function(){
+            var self = this;
+
+            this.dimensions = {};
+            this.zoomFactor = 1;
+           if (this.config().page == undefined)
+               this.config().page = CurrentDocument.galleryStart;
+            this.$element = $("#side-gallery");
+            this.$numberInput = $('.page-number', this.$element);
+
+            // ...
+            var origin = {};
+            var imageOrigin = {};
+
+            this.$image = $('.gallery-image img', this.$element).attr('unselectable', 'on');
+
+            // button handlers
+            this.$numberInput.change(function(event){
+                event.preventDefault();
+                self.setPage($(this).val());
+            });
+                    
+           $('.start-page', this.$element).click(function(){
+               self.setPage(CurrentDocument.galleryStart);
+           });
+
+            $('.previous-page', this.$element).click(function(){
+                self.setPage(parseInt(self.$numberInput.val(),10) - 1);
+            });
+
+            $('.next-page', this.$element).click(function(){
+                self.setPage(parseInt(self.$numberInput.val(),10) + 1);
+            });
+
+            $('.zoom-in', this.$element).click(function(){
+                self.alterZoom(0.2);
+            });
+
+            $('.zoom-out', this.$element).click(function(){
+                self.alterZoom((-0.2));
+            });
+
+            $(window).resize(function(){
+                self.dimensions.galleryWidth = self.$image.parent().width();
+                self.dimensions.galleryHeight = self.$image.parent().height();
+            });
+
+            this.$image.load(function(){
+                console.log("Image loaded.")
+                self._resizeImage();
+            }).bind('mousedown', function() {
+                               self.imageMoveStart.apply(self, arguments);
+                       });
+
+
+
+                       old_callback.call(this);
+        };
+
+        $.wiki.Perspective.call(this, options);
+    };
+
+    ScanGalleryPerspective.prototype = new $.wiki.Perspective();
+
+    ScanGalleryPerspective.prototype._resizeImage = function(){
+        var $img = this.$image;
+
+        $img.css({
+            width: '',
+            height: ''
+        });
+
+        this.dimensions = {
+            width: $img.width() * this.zoomFactor,
+            height: $img.height() * this.zoomFactor,
+            originWidth: $img.width(),
+            originHeight: $img.height(),
+                   galleryWidth: $img.parent().width(),
+            galleryHeight: $img.parent().height()
+        };
+
+        if (!(this.dimensions.width && this.dimensions.height)) {
+            setTimeout(function(){
+                $img.load();
+            }, 100);
+        }
+
+        var position = normalizePosition($img.position().left, $img.position().top, this.dimensions.galleryWidth, this.dimensions.galleryHeight, this.dimensions.width, this.dimensions.height);
+
+        $img.css({
+            left: position.x,
+            top: position.y,
+            width: $img.width() * this.zoomFactor,
+            height: $img.height() * this.zoomFactor
+        });
+    };
+
+    ScanGalleryPerspective.prototype.setPage = function(newPage){
+        newPage = normalizeNumber(newPage, this.doc.galleryImages.length);
+        this.$numberInput.val(newPage);
+               this.config().page = newPage;
+        $('.gallery-image img', this.$element).attr('src', this.doc.galleryImages[newPage - 1]);
+    };
+
+    ScanGalleryPerspective.prototype.alterZoom = function(delta){
+        var zoomFactor = this.zoomFactor + delta;
+        if (zoomFactor < 0.2)
+            zoomFactor = 0.2;
+        if (zoomFactor > 2)
+            zoomFactor = 2;
+        this.setZoom(zoomFactor);
+    };
+
+    ScanGalleryPerspective.prototype.setZoom = function(factor){
+        this.zoomFactor = factor;
+
+        this.dimensions.width = this.dimensions.originWidth * this.zoomFactor;
+        this.dimensions.height = this.dimensions.originHeight * this.zoomFactor;
+
+        // var position = normalizePosition(this.$image.position().left, this.$image.position().top, this.dimensions.galleryWidth, this.dimensions.galleryHeight, this.dimensions.width, this.dimensions.height);
+
+               this._resizeImage();
+    };
+
+       /*
+        * Movement
+        */
+       ScanGalleryPerspective.prototype.imageMoved = function(event){
+               event.preventDefault();
+
+               // origin is where the drag started
+               // imageOrigin is where the drag started on the image
+
+               var newX = event.clientX - this.origin.x + this.imageOrigin.left;
+               var newY = event.clientY - this.origin.y + this.imageOrigin.top;
+
+               var position = normalizePosition(newX, newY, this.dimensions.galleryWidth, this.dimensions.galleryHeight, this.dimensions.width, this.dimensions.height);
+
+               this.$image.css({
+                       left: position.x,
+                       top: position.y,
+               });
+
+               return false;
+       };
+
+       ScanGalleryPerspective.prototype.imageMoveStart = function(event){
+               event.preventDefault();
+
+               var self = this;
+
+               this.origin = {
+                       x: event.clientX,
+                       y: event.clientY
+               };
+
+               this.imageOrigin = self.$image.position();
+               $(document).bind('mousemove.gallery', function(){
+                       self.imageMoved.apply(self, arguments);
+               }).bind('mouseup.gallery', function() {
+                       self.imageMoveStop.apply(self, arguments);
+               });
+
+               return false;
+       };
+
+       ScanGalleryPerspective.prototype.imageMoveStop = function(event){
+               $(document).unbind('mousemove.gallery').unbind('mouseup.gallery');
+       };
+
+    /*
+     * Loading gallery
+     */
+    ScanGalleryPerspective.prototype.onEnter = function(success, failure){
+        var self = this;
+
+        $.wiki.Perspective.prototype.onEnter.call(this);
+
+        $('.vsplitbar').not('.active').trigger('click');
+        $(".vsplitbar-title").html("&darr;&nbsp;GALERIA&nbsp;&darr;");        
+        
+        this.doc.refreshGallery({
+            success: function(doc, data){
+                self.$image.show();
+                               console.log("gconfig:", self.config().page );
+                               self.setPage( self.config().page );
+                $('#imagesCount').html("/" + doc.galleryImages.length);
+
+                $('.error_message', self.$element).hide();
+                if(success) success();
+            },
+            failure: function(doc, message){
+                self.$image.hide();
+                $('.error_message', self.$element).show().html(message);
+                if(failure) failure();
+            }
+        });
+
+    };
+
+       ScanGalleryPerspective.prototype.onExit = function(success, failure) {
+
+       };
+
+    $.wiki.ScanGalleryPerspective = ScanGalleryPerspective;
+
+})(jQuery);
diff --git a/src/redakcja/static/js/wiki/view_history.js b/src/redakcja/static/js/wiki/view_history.js
new file mode 100644 (file)
index 0000000..85adca0
--- /dev/null
@@ -0,0 +1,204 @@
+(function($){
+
+    function HistoryPerspective(options) {
+               var old_callback = options.callback || function() {};
+
+               options.callback = function() {
+                       var self = this;
+            if (CurrentDocument.diff) {
+                rev_from = CurrentDocument.diff[0];
+                rev_to = CurrentDocument.diff[1];
+                this.doc.fetchDiff({
+                    from: rev_from,
+                    to: rev_to,
+                    success: function(doc, data){
+                        var result = $.wiki.newTab(doc, ''+rev_from +' -> ' + rev_to, 'DiffPerspective');
+
+                        $(result.view).html(data);
+                        $.wiki.switchToTab(result.tab);
+                    }
+                });
+            }
+
+                       // first time page is rendered
+               $('#make-diff-button').click(function() {
+                               self.makeDiff();
+                       });
+
+                       $('#pubmark-changeset-button').click(function() {
+                               self.showPubmarkForm();
+                       });
+
+               $('#doc-revert-button').click(function() {
+                   self.revertDialog();
+               });
+
+                       $('#open-preview-button').click(function(event) {
+                               var selected = $('#changes-list .entry.selected');
+
+                               if (selected.length != 1) {
+                           window.alert("Wybierz dokładnie *jedną* wersję.");
+                       return;
+                       }
+
+                               var version = parseInt($("*[data-stub-value='version']", selected[0]).text());
+                               window.open($(this).attr('data-basehref') + "?revision=" + version);
+
+                               event.preventDefault();
+                       });
+
+               $('#changes-list .entry').live('click', function(){
+               var $this = $(this);
+
+               var selected_count = $("#changes-list .entry.selected").length;
+
+               if ($this.hasClass('selected')) {
+                       $this.removeClass('selected');
+                       selected_count -= 1;
+               }
+               else {
+                   if (selected_count  < 2) {
+                       $this.addClass('selected');
+                       selected_count += 1;
+                   };
+               };
+
+               $('#history-view-editor .toolbar button').attr('disabled', 'disabled').
+                   filter('*[data-enabled-when~=' + selected_count + '], *[data-enabled-when~=*]').
+                   attr('disabled', null);
+               });
+
+           $('#changes-list span.tag').live('click', function(event){
+                   return false;
+               });
+
+               old_callback.call(this);
+               }
+
+               $.wiki.Perspective.call(this, options);
+    };
+
+    HistoryPerspective.prototype = new $.wiki.Perspective();
+
+    HistoryPerspective.prototype.freezeState = function(){
+        // must
+    };
+
+    HistoryPerspective.prototype.onEnter = function(success, failure){
+        $.wiki.Perspective.prototype.onEnter.call(this);
+
+        $.blockUI({
+            message: 'Odświeżanie historii...'
+        });
+
+        function _finalize(s){
+            $.unblockUI();
+
+            if (s) {
+                if (success)
+                    success();
+            }
+            else {
+                if (failure)
+                    failure();
+            }
+        }
+
+        function _failure(doc, message){
+            $('#history-view .message-box').html('Nie udało się odświeżyć historii:' + message).show();
+            _finalize(false);
+        };
+
+        function _success(doc, data){
+            $('#history-view .message-box').hide();
+            var changes_list = $('#changes-list');
+            var $stub = $('#history-view .row-stub');
+            changes_list.html('');
+
+                       var tags = $('select#id_addtag-tag option');
+
+            $.each(data, function(){
+                $.wiki.renderStub({
+                                       container: changes_list,
+                                       stub: $stub,
+                                       data: this,
+                                       filters: {
+//                                             tag: function(value) {
+//                                                     return tags.filter("*[value='"+value+"']").text();
+//                                             }
+//                        description: function(value) {
+//                                                 return value.replace('\n', ');
+//                                             }
+                                       }
+                               });
+            });
+
+            _finalize(true);
+        };
+
+        return this.doc.fetchHistory({
+            success: _success,
+            failure: _failure
+        });
+    };
+
+       HistoryPerspective.prototype.showPubmarkForm = function(){
+               var selected = $('#changes-list .entry.selected');
+
+               if (selected.length != 1) {
+            window.alert("Musisz zaznaczyć dokładnie jedną wersję.");
+            return;
+        }
+
+               var version = parseInt($("*[data-stub-value='version']", selected[0]).text());
+               $.wiki.showDialog('#pubmark_dialog', {'revision': version});
+       };
+
+       HistoryPerspective.prototype.makeDiff = function() {
+        var changelist = $('#changes-list');
+        var selected = $('.entry.selected', changelist);
+
+        if (selected.length != 2) {
+            window.alert("Musisz zaznaczyć dokładnie dwie wersje do porównania.");
+            return;
+        }
+
+        $.blockUI({
+            message: 'Wczytywanie porównania...'
+        });
+
+               var rev_from = $("*[data-stub-value='version']", selected[1]).text();
+               var rev_to =  $("*[data-stub-value='version']", selected[0]).text();
+
+        return this.doc.fetchDiff({
+            from: rev_from,
+                       to: rev_to,
+            success: function(doc, data){
+                var result = $.wiki.newTab(doc, ''+rev_from +' -> ' + rev_to, 'DiffPerspective');
+
+                               $(result.view).html(data);
+                               $.wiki.switchToTab(result.tab);
+                               $.unblockUI();
+            },
+            failure: function(doc){
+                $.unblockUI();
+            }
+        });
+    };
+
+    HistoryPerspective.prototype.revertDialog = function(){
+        var self = this;
+        var selected = $('#changes-list .entry.selected');
+
+        if (selected.length != 1) {
+            window.alert("Musisz zaznaczyć dokładnie jedną wersję.");
+            return;
+        }
+
+        var version = parseInt($("*[data-stub-value='version']", selected[0]).text());
+        $.wiki.showDialog('#revert_dialog', {revision: version});
+    };
+
+    $.wiki.HistoryPerspective = HistoryPerspective;
+
+})(jQuery);
diff --git a/src/redakcja/static/js/wiki/view_search.js b/src/redakcja/static/js/wiki/view_search.js
new file mode 100644 (file)
index 0000000..b49671c
--- /dev/null
@@ -0,0 +1,117 @@
+(function($){
+
+    /*
+     * Perspective
+     */
+    function SearchPerspective(options){
+        var old_callback = options.callback || function() { };
+
+        this.noupdate_hash_onenter = true;
+        this.vsplitbar = 'ZNAJDŹ&nbsp;I&nbsp;ZAMIEŃ';
+
+        options.callback = function(){
+            var self = this;
+
+            this.editor = null;
+            this.$element = $("#side-search");
+            this.$searchInput = $('#search-input', this.$element);
+            this.$replaceInput = $('#replace-input', this.$element);
+            this.$searchButton = $('#search-button', this.$element);
+            this.$replaceButton = $('#replace-button', this.$element);
+
+            this.$replaceButton.attr("disabled","disabled");
+            this.options = Array();
+
+            // handlers
+            this.$searchInput.change(function(event){
+                self.searchCursor = null;
+            });
+            this.$replaceInput.change(function(event){
+                self.searchCursor = null;
+            });
+
+            $("#side-search input:checkbox").each(function() {
+                self.options[this.id] = this.checked;
+            }).change(function(){
+                self.options[this.id] = this.checked;
+                self.searchCursor = null;
+            });
+
+            this.$searchButton.click(function(){
+                if (!self.search())
+                    alert('Brak wyników.');
+            });
+
+            this.$replaceButton.click(function(){
+                self.replace();
+            });
+
+            old_callback.call(this);
+        };
+
+        $.wiki.Perspective.call(this, options);
+    };
+
+    SearchPerspective.prototype = new $.wiki.Perspective();
+
+    SearchPerspective.prototype.search = function(){
+        var self = this;
+        var query = self.$searchInput.val();
+
+        if (!self.editor)
+            self.editor = $.wiki.perspectiveForTab('#CodeMirrorPerspective').codemirror
+
+        if (!self.searchCursor) {
+            self.searchCursor = self.editor.getSearchCursor(
+                self.$searchInput.val(), 
+                self.options['search-from-cursor'], 
+                !self.options['search-case-sensitive']
+            );
+        }
+        if (self.searchCursor.findNext()) {
+            self.searchCursor.select();
+            self.$replaceButton.removeAttr("disabled");
+            return true;
+        }
+        else {
+            self.searchCursor = null;
+            this.$replaceButton.attr("disabled","disabled");
+            return false;
+        }
+    };
+
+    SearchPerspective.prototype.replace = function(){
+        var self = this;
+        var query = self.$replaceInput.val();
+
+        if (!self.searchCursor) {
+            self.search();
+        }
+        else {}
+        self.searchCursor.select();
+        self.searchCursor.replace(query);
+        if(self.search() && self.options['replace-all']) {
+            self.replace();
+        }
+    };
+
+    SearchPerspective.prototype.onEnter = function(success, failure){
+        var self = this;
+
+        $.wiki.Perspective.prototype.onEnter.call(this);
+        self.$searchCursor = null;
+
+        $('.vsplitbar').not('.active').trigger('click');
+        $(".vsplitbar-title").html("&darr;&nbsp;ZNAJDŹ&nbsp;I&nbsp;ZAMIEŃ&nbsp;&darr;");        
+        
+        if ($.wiki.activePerspective() != 'CodeMirrorPerspective')
+            $.wiki.switchToTab('#CodeMirrorPerspective');
+    };
+
+    SearchPerspective.prototype.onExit = function(success, failure) {
+
+    };
+
+    $.wiki.SearchPerspective = SearchPerspective;
+
+})(jQuery);
diff --git a/src/redakcja/static/js/wiki/view_summary.js b/src/redakcja/static/js/wiki/view_summary.js
new file mode 100644 (file)
index 0000000..099a0e8
--- /dev/null
@@ -0,0 +1,61 @@
+(function($){
+
+       function SummaryPerspective(options) {
+               var old_callback = options.callback || function() {};
+
+               options.callback = function() {
+                       var self = this;
+
+                       // first time page is rendered
+               $('#summary-cover-refresh').click(function() {
+                               self.refreshCover();
+                       });
+
+               old_callback.call(this);
+               }
+
+               $.wiki.Perspective.call(this, options);
+    };
+
+    SummaryPerspective.prototype = new $.wiki.Perspective();
+
+       SummaryPerspective.prototype.refreshCover = function() {
+               $('#summary-cover-refresh').attr('disabled', 'disabled');
+               this.doc.refreshCover({
+                       success: function(text) {
+                               $('#summary-cover').attr('src', text);
+                       $('#summary-cover-refresh').removeAttr('disabled');
+                       }
+               });
+       };
+
+    SummaryPerspective.prototype.showCharCount = function() {
+        var cc;
+        try {
+            cc = this.doc.getLength();
+            $('#charcount_untagged').hide();
+        }
+        catch (e) {
+            $('#charcount_untagged').show();
+            cc = this.doc.text.replace(/\s{2,}/g, ' ').length;
+        }
+        $('#charcount').html(cc);
+        $('#charcount_pages').html((Math.round(cc/18)/100).toLocaleString());
+    }
+
+    SummaryPerspective.prototype.freezeState = function(){
+        // must
+    };
+
+       SummaryPerspective.prototype.onEnter = function(success, failure){
+               $.wiki.Perspective.prototype.onEnter.call(this);
+               
+               this.showCharCount();
+
+               console.log("Entered summery view");
+       };
+
+       $.wiki.SummaryPerspective = SummaryPerspective;
+
+})(jQuery);
+
diff --git a/src/redakcja/static/js/wiki/wikiapi.js b/src/redakcja/static/js/wiki/wikiapi.js
new file mode 100644 (file)
index 0000000..8df3ef5
--- /dev/null
@@ -0,0 +1,434 @@
+(function($) {
+       $.wikiapi = {};
+       var noop = function() {
+       };
+       var noops = {
+               success: noop,
+               failure: noop
+       };
+       /*
+        * Return absolute reverse path of given named view. (at least he have it
+        * hard-coded in one place)
+        *
+        * TODO: think of a way, not to hard-code it here ;)
+        *
+        */
+       function reverse() {
+               var vname = arguments[0];
+               var base_path = "/editor";
+
+               if (vname == "ajax_document_text") {
+                       var path = "/text/" + arguments[1] + '/';
+
+               if (arguments[2] !== undefined)
+                               path += arguments[2] + '/';
+
+                       return base_path + path;
+               }
+
+        if (vname == "ajax_document_revert") {
+            return base_path + "/revert/" + arguments[1] + '/';
+        }
+
+
+               if (vname == "ajax_document_history") {
+
+                       return base_path + "/history/" + arguments[1] + '/';
+               }
+
+               if (vname == "ajax_document_gallery") {
+
+                       return base_path + "/gallery/" + arguments[1] + '/';
+               }
+
+               if (vname == "ajax_document_diff")
+                       return base_path + "/diff/" + arguments[1] + '/';
+
+        if (vname == "ajax_document_rev")
+            return base_path + "/rev/" + arguments[1] + '/';
+
+               if (vname == "ajax_document_pubmark")
+                       return base_path + "/pubmark/" + arguments[1] + '/';
+
+               if (vname == "ajax_cover_preview")
+                       return "/cover/preview/";
+
+               console.log("Couldn't reverse match:", vname);
+               return "/404.html";
+       };
+
+       /*
+        * Document Abstraction
+        */
+       function WikiDocument(element_id) {
+               var meta = $('#' + element_id);
+               this.id = meta.attr('data-chunk-id');
+
+               this.revision = $("*[data-key='revision']", meta).text();
+               this.readonly = !!$("*[data-key='readonly']", meta).text();
+
+               this.galleryLink = $("*[data-key='gallery']", meta).text();
+        this.galleryStart = parseInt($("*[data-key='gallery-start']", meta).text());
+
+        var diff = $("*[data-key='diff']", meta).text();
+        if (diff) {
+            diff = diff.split(',');
+            if (diff.length == 2 && diff[0] < diff[1])
+                this.diff = diff;
+            else if (diff.length == 1) {
+                diff = parseInt(diff);
+                if (diff != NaN)
+                    this.diff = [diff - 1, diff];
+            }
+        }
+
+               this.galleryImages = [];
+               this.text = null;
+               this.has_local_changes = false;
+               this._lock = -1;
+               this._context_lock = -1;
+               this._lock_count = 0;
+       };
+
+       WikiDocument.prototype.triggerDocumentChanged = function() {
+               $(document).trigger('wlapi_document_changed', this);
+       };
+       /*
+        * Fetch text of this document.
+        */
+       WikiDocument.prototype.fetch = function(params) {
+               params = $.extend({}, noops, params);
+               var self = this;
+               $.ajax({
+                       method: "GET",
+                       url: reverse("ajax_document_text", self.id),
+                       data: {"revision": self.revision},
+                       dataType: 'json',
+                       success: function(data) {
+                               var changed = false;
+
+                               if (self.text === null || self.revision !== data.revision) {
+                                       self.text = data.text;
+                                       self.revision = data.revision;
+                                       self.gallery = data.gallery;
+                                       changed = true;
+                                       self.triggerDocumentChanged();
+                               };
+
+                               self.has_local_changes = false;
+                               params['success'](self, changed);
+                       },
+                       error: function() {
+                               params['failure'](self, "Nie udało się wczytać treści dokumentu.");
+                       }
+               });
+       };
+       /*
+        * Fetch history of this document.
+        *
+        * from - First revision to fetch (default = 0) upto - Last revision to
+        * fetch (default = tip)
+        *
+        */
+       WikiDocument.prototype.fetchHistory = function(params) {
+               /* this doesn't modify anything, so no locks */
+               params = $.extend({}, noops, params);
+               var self = this;
+               $.ajax({
+                       method: "GET",
+                       url: reverse("ajax_document_history", self.id),
+                       dataType: 'json',
+                       data: {
+                               "from": params['from'],
+                               "upto": params['upto']
+                       },
+                       success: function(data) {
+                               params['success'](self, data);
+                       },
+                       error: function() {
+                               params['failure'](self, "Nie udało się wczytać historii dokumentu.");
+                       }
+               });
+       };
+       WikiDocument.prototype.fetchDiff = function(params) {
+               /* this doesn't modify anything, so no locks */
+               var self = this;
+               params = $.extend({
+                       'from': self.revision,
+                       'to': self.revision
+               }, noops, params);
+               $.ajax({
+                       method: "GET",
+                       url: reverse("ajax_document_diff", self.id),
+                       dataType: 'html',
+                       data: {
+                               "from": params['from'],
+                               "to": params['to']
+                       },
+                       success: function(data) {
+                               params['success'](self, data);
+                       },
+                       error: function() {
+                               params['failure'](self, "Nie udało się wczytać porównania wersji.");
+                       }
+               });
+       };
+
+    WikiDocument.prototype.checkRevision = function(params) {
+        /* this doesn't modify anything, so no locks */
+        var self = this;
+        $.ajax({
+            method: "GET",
+            url: reverse("ajax_document_rev", self.id),
+            dataType: 'text',
+            success: function(data) {
+                if (data == '') {
+                    if (params.error)
+                        params.error();
+                }
+                else if (data != self.revision)
+                    params.outdated();
+            }
+        });
+    };
+
+       /*
+        * Fetch gallery
+        */
+       WikiDocument.prototype.refreshGallery = function(params) {
+               params = $.extend({}, noops, params);
+               var self = this;
+               $.ajax({
+                       method: "GET",
+                       url: reverse("ajax_document_gallery", self.galleryLink),
+                       dataType: 'json',
+                       // data: {},
+                       success: function(data) {
+                               self.galleryImages = data;
+                               params['success'](self, data);
+                       },
+                       error: function(xhr) {
+                switch (xhr.status) {
+                    case 403:
+                        var msg = 'Galerie dostępne tylko dla zalogowanych użytkowników.';
+                        break;
+                    case 404:
+                        var msg = "Nie znaleziono galerii o nazwie: '" + self.galleryLink + "'.";
+                    default:
+                        var msg = "Nie udało się wczytać galerii o nazwie: '" + self.galleryLink + "'.";
+                }
+                               self.galleryImages = [];
+                               params['failure'](self, "<p>" + msg + "</p>");
+                       }
+               });
+       };
+
+       /*
+        * Set document's text
+        */
+       WikiDocument.prototype.setText = function(text) {
+               return this.setDocumentProperty('text', text);
+       };
+
+       /*
+        * Set document's gallery link
+        */
+       WikiDocument.prototype.setGalleryLink = function(gallery) {
+               return this.setDocumentProperty('galleryLink', gallery);
+       };
+
+       /*
+        * Set document's property
+        */
+       WikiDocument.prototype.setDocumentProperty = function(property, value) {
+               if(this[property] !== value) {
+                       this[property] = value;
+                       this.has_local_changes = true;
+               }
+       };
+
+       /*
+        * Save text back to the server
+        */
+       WikiDocument.prototype.save = function(params) {
+               params = $.extend({}, noops, params);
+               var self = this;
+
+               if (!self.has_local_changes) {
+                       console.log("Abort: no changes.");
+                       return params['success'](self, false, "Nie ma zmian do zapisania.");
+               };
+
+               // Serialize form to dictionary
+               var data = {};
+               $.each(params['form'].serializeArray(), function() {
+                       data[this.name] = this.value;
+               });
+
+               data['textsave-text'] = self.text;
+
+               $.ajax({
+                       url: reverse("ajax_document_text", self.id),
+                       type: "POST",
+                       dataType: "json",
+                       data: data,
+                       success: function(data) {
+                               var changed = false;
+
+                $('#header').removeClass('saving');
+
+                               if (data.text) {
+                                       self.text = data.text;
+                                       self.revision = data.revision;
+                                       self.gallery = data.gallery;
+                                       changed = true;
+                                       self.triggerDocumentChanged();
+                               };
+
+                               params['success'](self, changed, ((changed && "Udało się zapisać :)") || "Twoja wersja i serwera jest identyczna"));
+                       },
+                       error: function(xhr) {
+                if ($('#header').hasClass('saving')) {
+                    $('#header').removeClass('saving');
+                    $.blockUI({
+                        message: "<p>Nie udało się zapisać zmian. <br/><button onclick='$.unblockUI()'>OK</button></p>"
+                    })
+                }
+                else {
+                    try {
+                        params['failure'](self, $.parseJSON(xhr.responseText));
+                    }
+                    catch (e) {
+                        params['failure'](self, {
+                            "__message": "<p>Nie udało się zapisać - błąd serwera.</p>"
+                        });
+                    };
+                }
+
+                       }
+               });
+
+        $('#save-hide').click(function(){
+            $('#header').addClass('saving');
+            $.unblockUI();
+            $.wiki.blocking.unblock();
+        });
+       }; /* end of save() */
+
+    WikiDocument.prototype.revertToVersion = function(params) {
+        var self = this;
+        params = $.extend({}, noops, params);
+
+        if (params.revision >= this.revision) {
+            params.failure(self, 'Proszę wybrać rewizję starszą niż aktualna.');
+            return;
+        }
+
+        // Serialize form to dictionary
+        var data = {};
+        $.each(params['form'].serializeArray(), function() {
+            data[this.name] = this.value;
+        });
+
+        $.ajax({
+            url: reverse("ajax_document_revert", self.id),
+            type: "POST",
+            dataType: "json",
+            data: data,
+            success: function(data) {
+                if (data.text) {
+                    self.text = data.text;
+                    self.revision = data.revision;
+                    self.gallery = data.gallery;
+                    self.triggerDocumentChanged();
+
+                    params.success(self, "Udało się przywrócić wersję :)");
+                }
+                else {
+                    params.failure(self, "Przywracana wersja identyczna z aktualną. Anulowano przywracanie.");
+                }
+            },
+            error: function(xhr) {
+                params.failure(self, "Nie udało się przywrócić wersji - błąd serwera.");
+            }
+        });
+    };
+
+       WikiDocument.prototype.pubmark = function(params) {
+               params = $.extend({}, noops, params);
+               var self = this;
+               var data = {
+                       "pubmark-id": self.id,
+               };
+
+               /* unpack form */
+               $.each(params.form.serializeArray(), function() {
+                       data[this.name] = this.value;
+               });
+
+               $.ajax({
+                       url: reverse("ajax_document_pubmark", self.id),
+                       type: "POST",
+                       dataType: "json",
+                       data: data,
+                       success: function(data) {
+                               params.success(self, data.message);
+                       },
+                       error: function(xhr) {
+                               if (xhr.status == 403 || xhr.status == 401) {
+                                       params.failure(self, {
+                                               "__all__": ["Nie masz uprawnień lub nie jesteś zalogowany."]
+                                       });
+                               }
+                               else {
+                                       try {
+                                               params.failure(self, $.parseJSON(xhr.responseText));
+                                       }
+                                       catch (e) {
+                                               params.failure(self, {
+                                                       "__all__": ["Nie udało się - błąd serwera."]
+                                               });
+                                       };
+                               };
+                       }
+               });
+       };
+
+       WikiDocument.prototype.refreshCover = function(params) {
+        var self = this;
+               var data = {
+                       xml: self.text // TODO: send just DC
+               };
+        $.ajax({
+            url: reverse("ajax_cover_preview"),
+            type: "POST",
+            data: data,
+            success: function(data) {
+                params.success(data);
+            },
+            error: function(xhr) {
+                // params.failure("Nie udało się odświeżyć okładki - błąd serwera.");
+            }
+        });
+       };
+
+
+    WikiDocument.prototype.getLength = function(params) {
+        var xml = this.text.replace(/\/(\s+)/g, '<br />$1');
+        var parser = new DOMParser();
+        var doc = parser.parseFromString(xml, 'text/xml');
+        var error = $('parsererror', doc);
+
+        if (error.length > 0) {
+            throw "Not an XML document.";
+        }
+        $.xmlns["rdf"] = "http://www.w3.org/1999/02/22-rdf-syntax-ns#"; 
+        $('rdf|RDF, motyw, pa, pe, pr, pt', doc).remove();
+        var text = $(doc).text();
+        text = $.trim(text.replace(/\s{2,}/g, ' '));
+        return text.length;
+    }
+
+
+       $.wikiapi.WikiDocument = WikiDocument;
+})(jQuery);
diff --git a/src/redakcja/static/js/wiki/xslt.js b/src/redakcja/static/js/wiki/xslt.js
new file mode 100644 (file)
index 0000000..ab90e0c
--- /dev/null
@@ -0,0 +1,393 @@
+/*
+ *
+ * XSLT STUFF
+ *
+ */
+function createXSLT(xsl) {
+    var p = new XSLTProcessor();
+    p.importStylesheet(xsl);
+    return p;
+}
+
+var xml2htmlStylesheet = null;
+
+// Wykonuje block z załadowanymi arkuszami stylów
+function withStylesheets(code_block, onError)
+{
+    if (!xml2htmlStylesheet) {
+       $.blockUI({message: 'Ładowanie arkuszy stylów...'});
+       $.ajax({
+               url: STATIC_URL + 'xsl/wl2html_client.xsl?20171106',
+               dataType: 'xml',
+               timeout: 10000,
+               success: function(data) {
+               xml2htmlStylesheet = createXSLT(data);
+                $.unblockUI();
+                               code_block();
+
+            },
+                       error: onError
+        })
+    }
+       else {
+               code_block();
+       }
+}
+
+
+// Wykonuje block z załadowanymi kanonicznymi motywami
+function withThemes(code_block, onError)
+{
+    if (typeof withThemes.canon == 'undefined') {
+        $.ajax({
+            url: '/editor/themes',
+            dataType: 'text',
+            success: function(data) {
+                withThemes.canon = data.split('\n');
+                code_block(withThemes.canon);
+            },
+            error: function() {
+                withThemes.canon = null;
+                code_block(withThemes.canon);
+            }
+        })
+    }
+    else {
+        code_block(withThemes.canon);
+    }
+}
+
+
+function xml2html(options) {
+    withStylesheets(function() {
+        var xml = options.xml.replace(/\/(\s+)/g, '<br />$1');
+        xml = xml.replace(/([^a-zA-Z0-9ąćęłńóśźżĄĆĘŁŃÓŚŹŻ\s<>«»\\*_!,:;?&%."'=#()\/-]+)/g, '<alien>$1</alien>');
+        var parser = new DOMParser();
+        var serializer = new XMLSerializer();
+        var doc = parser.parseFromString(xml, 'text/xml');
+        var error = $('parsererror', doc);
+
+        if (error.length == 0) {
+            doc = xml2htmlStylesheet.transformToFragment(doc, document);
+            console.log(doc.firstChild);
+
+        if(doc.firstChild === null) {
+            options.error("Błąd w przetwarzaniu XML.");
+                return;
+            }
+
+            error = $('parsererror', doc);
+        }
+
+        if (error.length > 0 && options.error) {
+            source = $('sourcetext', doc);
+            source_text = source.text();
+            source.text('');
+            options.error(error.text(), source_text);
+        } else {
+            options.success(doc.childNodes);
+
+            withThemes(function(canonThemes) {
+                if (canonThemes != null) {
+                    $('.theme-text-list').addClass('canon').each(function(){
+                        var themes = $(this).html().split(',');
+                        for (i in themes) {
+                            themes[i] = $.trim(themes[i]);
+                            if (canonThemes.indexOf(themes[i]) == -1)
+                                themes[i] = '<span x-pass-thru="true" class="noncanon">' + themes[i] + "</span>"
+                        }
+                        $(this).html(themes.join(', '));
+                    });
+                }
+            });
+        }
+    }, function() { options.error && options.error('Nie udało się załadować XSLT'); });
+}
+
+/* USEFULL CONSTANTS */
+const ELEMENT_NODE                                      = 1;
+const ATTRIBUTE_NODE                 = 2;
+const TEXT_NODE                      = 3;
+const CDATA_SECTION_NODE             = 4;
+const ENTITY_REFERENCE_NODE          = 5;
+const ENTITY_NODE                    = 6;
+const PROCESSING_INSTRUCTION_NODE    = 7;
+const COMMENT_NODE                   = 8;
+const DOCUMENT_NODE                  = 9;
+const DOCUMENT_TYPE_NODE             = 10;
+const DOCUMENT_FRAGMENT_NODE         = 11;
+const NOTATION_NODE                  = 12;
+const XATTR_RE = /^x-attr-name-(.*)$/;
+
+const ELEM_START = 1;
+const ELEM_END = 2;
+const NS_END = 3;
+
+const NAMESPACES = {
+       // namespaces not listed here will be assigned random names
+       "http://www.w3.org/1999/02/22-rdf-syntax-ns#": "rdf",
+       "http://purl.org/dc/elements/1.1/": "dc",
+       "http://www.w3.org/XML/1998/namespace": "xml"
+};
+
+function HTMLSerializer() {
+       // empty constructor
+}
+
+
+
+HTMLSerializer.prototype._prepare = function() {
+       this.stack = [];
+
+       // XML namespace is implicit
+       this.nsMap = {"http://www.w3.org/XML/1998/namespace": "xml"};
+
+       this.result = "";
+       this.nsCounter = 1;
+}
+
+HTMLSerializer.prototype._pushElement = function(element) {
+       this.stack.push({
+               "type": ELEM_START,
+               "node": element
+       });
+}
+
+HTMLSerializer.prototype._pushChildren = function(element) {
+       for(var i = element.childNodes.length-1; i >= 0; i--)
+               this._pushElement(element.childNodes.item(i));
+}
+
+HTMLSerializer.prototype._pushTagEnd = function(tagName) {
+       this.stack.push({
+               "type": ELEM_END,
+               "tagName": tagName
+       });
+}
+
+HTMLSerializer.prototype._verseBefore = function(node) {
+    /* true if previous element is a previous verse of a stanza */
+    var parent = node.parentNode;
+    if (!parent || !parent.hasAttribute('x-node') || parent.getAttribute('x-node') != 'strofa')
+        return false;
+
+       var prev = node.previousSibling;
+
+       while((prev !== null) && (prev.nodeType != ELEMENT_NODE)) {
+               prev = prev.previousSibling;
+       }
+
+       return (prev !== null) && prev.hasAttribute('x-verse');
+}
+
+HTMLSerializer.prototype._nodeIgnored = function(node) {
+    return node.getAttribute('x-node') == 'wers';
+}
+
+HTMLSerializer.prototype._ignoredWithWhitespace = function(node) {
+    while (node.nodeType == ELEMENT_NODE && this._nodeIgnored(node) && node.childNodes.length > 0)
+        node = node.childNodes[0];
+    if (node.nodeType == TEXT_NODE)
+        return node.nodeValue.match(/^\s/)
+    else return false;
+}
+
+
+HTMLSerializer.prototype.serialize = function(rootElement, stripOuter)
+{
+       var self = this;
+       self._prepare();
+
+       if(!stripOuter)
+               self._pushElement(rootElement);
+       else
+               self._pushChildren(rootElement);
+
+    var text_buffer = '';
+
+       while(self.stack.length > 0) {
+               var token = self.stack.pop();
+
+        if(token.type === ELEM_END) {
+            self.result += text_buffer;
+            text_buffer = '';
+            if (token.tagName != '')
+                self.result += "</" + token.tagName + ">";
+            continue;
+        };
+
+               if(token.type === NS_END) {
+                       self._unassignNamespace(token.namespace);
+                       continue;
+               }
+
+
+               switch(token.node.nodeType) {
+                       case ELEMENT_NODE:
+                               if(token.node.hasAttribute('x-pass-thru')
+                                || token.node.hasAttribute('data-pass-thru')) {
+                                       self._pushChildren(token.node);
+                                       break;
+                               }
+
+                               if(!token.node.hasAttribute('x-node'))
+                                       break;
+
+                               var xnode = token.node.getAttribute('x-node');
+
+                               if(xnode === 'out-of-flow-text') {
+                                       self._pushChildren(token.node);
+                                       break;
+                               }
+
+                if(token.node.hasAttribute('x-verse') && self._verseBefore(token.node)) {
+                    self.result += '/';
+                    // add whitespace if there's none
+                    if (!(text_buffer.match(/^\s/) || self._ignoredWithWhitespace(token.node)))
+                        self.result += ' ';
+                }
+
+                self.result += text_buffer;
+                text_buffer = '';
+                               self._serializeElement(token.node);
+                               break;
+                       case TEXT_NODE:
+                               self.result += text_buffer;
+                               text_buffer = token.node.nodeValue.replace(/&/g, '&amp;').replace(/</g, '&lt;');
+                               break;
+            case COMMENT_NODE:
+                self.result += text_buffer;
+                text_buffer = '';
+                self.result += '<!--' + token.node.nodeValue + '-->';
+                break;
+               };
+       };
+    self.result += text_buffer;
+
+       return this.result;
+}
+
+/*
+ * TODO: this doesn't support prefix redefinitions
+ */
+HTMLSerializer.prototype._unassignNamespace = function(nsData) {
+       this.nsMap[nsData.uri] = undefined;
+};
+
+HTMLSerializer.prototype._assignNamespace = function(uri) {
+       if(uri === null) {
+               // default namespace
+               return ({"prefix": "", "uri": "", "fresh": false});
+       }
+
+       if(this.nsMap[uri] === undefined) {
+               // this prefix hasn't been defined yet in current context
+               var prefix = NAMESPACES[uri];
+
+               if (prefix === undefined) { // not predefined
+                       prefix = "ns" + this.nsCounter;
+                       this.nsCounter += 1;
+               }
+
+               this.nsMap[uri] = prefix;
+               return ({
+                       "prefix": prefix,
+                       "uri": uri,
+                       "fresh": true
+               });
+       }
+
+       return ({"prefix": this.nsMap[uri], "uri": uri, "fresh": false});
+};
+
+HTMLSerializer.prototype._join = function(prefix, name) {
+       if(!!prefix)
+               return prefix + ":" + name;
+       return name;
+};
+
+HTMLSerializer.prototype._rjoin = function(prefix, name) {
+       if(!!name)
+               return prefix + ":" + name;
+       return prefix;
+};
+
+HTMLSerializer.prototype._serializeElement = function(node) {
+    var self = this;
+
+    if (self._nodeIgnored(node)) {
+        self._pushTagEnd('');
+        self._pushChildren(node);
+    }
+    else {
+       var ns = node.getAttribute('x-ns');
+       var nsPrefix = null;
+       var newNamespaces = [];
+
+       var nsData = self._assignNamespace(node.getAttribute('x-ns'));
+
+       if(nsData.fresh) {
+               newNamespaces.push(nsData);
+               self.stack.push({
+                       "type": NS_END,
+                       "namespace": nsData
+               });
+       }
+
+       var tagName = self._join(nsData.prefix, node.getAttribute('x-node'));
+
+       /* retrieve attributes */
+       var attributeIDs = [];
+       for (var i = 0; i < node.attributes.length; i++) {
+               var attr = node.attributes.item(i);
+
+               // check if name starts with "x-attr-name"
+               var m = attr.name.match(XATTR_RE);
+               if (m !== null)
+                       attributeIDs.push(m[1]);
+       };
+
+       /* print out */
+
+       self.result += '<' + tagName;
+
+       $.each(attributeIDs, function() {
+               var nsData = self._assignNamespace(node.getAttribute('x-attr-ns-'+this));
+
+               if(nsData.fresh) {
+                       newNamespaces.push(nsData);
+                       self.stack.push({
+                               "type": NS_END,
+                               "namespace": nsData
+                       });
+               };
+
+               self.result += ' ' + self._join(nsData.prefix, node.getAttribute('x-attr-name-'+this));
+               self.result += '="'+node.getAttribute('x-attr-value-'+this) +'"';
+       });
+
+       /* print new namespace declarations */
+       $.each(newNamespaces, function() {
+               self.result += " " + self._rjoin("xmlns", this.prefix);
+               self.result += '="' + this.uri + '"';
+       });
+
+       if (node.childNodes.length > 0) {
+               self.result += ">";
+               self._pushTagEnd(tagName);
+               self._pushChildren(node);
+       }
+       else {
+               self.result += "/>";
+       };
+    }
+};
+
+function html2text(params) {
+       try {
+               var s = new HTMLSerializer();
+               params.success( s.serialize(params.element, params.stripOuter) );
+       } catch(e) {
+               params.error("Nie udało się zserializować tekstu:" + e)
+       }
+}
diff --git a/src/redakcja/static/js/wiki_img/base.js b/src/redakcja/static/js/wiki_img/base.js
new file mode 100644 (file)
index 0000000..ffe5a01
--- /dev/null
@@ -0,0 +1,329 @@
+(function($)
+{
+       var noop = function() { };
+
+       $.wiki = {
+               perspectives: {},
+               cls: {},
+               state: {
+                       "version": 1,
+                       "perspectives": {
+                "CodeMirrorPerspective": {}
+                       }
+               }
+       };
+
+       $.wiki.loadConfig = function() {
+               if(!window.localStorage)
+                       return;
+
+               try {
+                       var value = window.localStorage.getItem(CurrentDocument.id) || "{}";
+                       var config = JSON.parse(value);
+
+                       if (config.version == $.wiki.state.version) {
+                               $.wiki.state.perspectives = $.extend($.wiki.state.perspectives, config.perspectives);
+                       }
+               } catch(e) {
+                       console.log("Failed to load config, using default.");
+               }
+
+               console.log("Loaded:", $.wiki.state, $.wiki.state.version);
+       };
+
+       $(window).bind('unload', function() {
+               if(window.localStorage)
+                       window.localStorage.setItem(CurrentDocument.id, JSON.stringify($.wiki.state));
+       })
+
+
+       $.wiki.activePerspective = function() {
+               return this.perspectives[$("#tabs li.active").attr('id')];
+       };
+
+       $.wiki.exitContext = function() {
+               var ap = this.activePerspective();
+               if(ap) ap.onExit();
+               return ap;
+       };
+
+       $.wiki.enterContext = function(ap) {
+               if(ap) ap.onEnter();
+       };
+
+       $.wiki.isDirty = function() {
+               var ap = this.activePerspective();
+               return (!!CurrentDocument && CurrentDocument.has_local_changes) || ap.dirty();
+       };
+
+       $.wiki.newTab = function(doc, title, klass) {
+               var base_id = 'id' + Math.floor(Math.random()* 5000000000);
+               var id = (''+klass)+'_' + base_id;
+               var $tab = $('<li id="'+id+'" data-ui-related="'+base_id+'" data-ui-jsclass="'+klass+'" >'
+                               + title + '<img src="'+STATIC_URL+'icons/close.png" class="tabclose"></li>');
+               var $view = $('<div class="editor '+klass+'" id="'+base_id+'"> </div>');
+
+               this.perspectives[id] = new $.wiki[klass]({
+                       doc: doc,
+                       id: id,
+                       base_id: base_id,
+               });
+
+               $('#tabs').append($tab);
+               $view.hide().appendTo('#editor');
+               return {
+                       tab: $tab[0],
+                       view: $view[0],
+               };
+       };
+
+       $.wiki.initTab = function(options) {
+               var klass = $(options.tab).attr('data-ui-jsclass');
+
+               return new $.wiki[klass]({
+                       doc: options.doc,
+                       id: $(options.tab).attr('id'),
+                       callback: function() {
+                               $.wiki.perspectives[this.perspective_id] = this;
+                               if(options.callback)
+                                       options.callback.call(this);
+                       }
+               });
+       };
+
+       $.wiki.perspectiveForTab = function(tab) { // element or id
+               return this.perspectives[ $(tab).attr('id')];
+       }
+
+       $.wiki.switchToTab = function(tab){
+               var self = this;
+               var $tab = $(tab);
+
+               if($tab.length != 1)
+                       $tab = $(DEFAULT_PERSPECTIVE);
+
+               var $old = $tab.closest('.tabs').find('.active');
+
+               $old.each(function(){
+                       $(this).removeClass('active');
+                       self.perspectives[$(this).attr('id')].onExit();
+                       $('#' + $(this).attr('data-ui-related')).hide();
+               });
+
+               /* show new */
+               $tab.addClass('active');
+               $('#' + $tab.attr('data-ui-related')).show();
+
+               console.log($tab);
+               console.log($.wiki.perspectives);
+
+               $.wiki.perspectives[$tab.attr('id')].onEnter();
+       };
+
+       /*
+        * Basic perspective.
+        */
+       $.wiki.Perspective = function(options) {
+               if(!options) return;
+
+               this.doc = options.doc;
+               if (options.id) {
+                       this.perspective_id = options.id;
+               }
+               else {
+                       this.perspective_id = '';
+               }
+
+               if(options.callback)
+                       options.callback.call(this);
+       };
+
+       $.wiki.Perspective.prototype.config = function() {
+               return $.wiki.state.perspectives[this.perspective_id];
+       }
+
+       $.wiki.Perspective.prototype.toString = function() {
+               return this.perspective_id;
+       };
+
+       $.wiki.Perspective.prototype.dirty = function() {
+               return true;
+       };
+
+       $.wiki.Perspective.prototype.onEnter = function () {
+               // called when perspective in initialized
+               if (!this.noupdate_hash_onenter) {
+                       document.location.hash = '#' + this.perspective_id;
+               }
+       };
+
+       $.wiki.Perspective.prototype.onExit = function () {
+               // called when user switches to another perspective
+               if (!this.noupdate_hash_onenter) {
+                       document.location.hash = '';
+               }
+       };
+
+       $.wiki.Perspective.prototype.destroy = function() {
+               // pass
+       };
+
+       $.wiki.Perspective.prototype.freezeState = function () {
+               // free UI state (don't store data here)
+       };
+
+       $.wiki.Perspective.prototype.unfreezeState = function (frozenState) {
+               // restore UI state
+       };
+
+       /*
+        * Stub rendering (used in generating history)
+        */
+       $.wiki.renderStub = function(params)
+       {
+               params = $.extend({ 'filters': {} }, params);
+               var $elem = params.stub.clone();
+               $elem.removeClass('row-stub');
+               params.container.append($elem);
+
+               $('*[data-stub-value]', $elem).each(function() {
+                       var $this = $(this);
+                       var field = $this.attr('data-stub-value');
+
+                       var value = params.data[field];
+
+                       if(params.filters[field])
+                               value = params.filters[field](value);
+
+                       if(value === null || value === undefined) return;
+
+                       if(!$this.attr('data-stub-target')) {
+                               $this.text(value);
+                       }
+                       else {
+                               $this.attr($this.attr('data-stub-target'), value);
+                               $this.removeAttr('data-stub-target');
+                               $this.removeAttr('data-stub-value');
+                       }
+               });
+
+               $elem.show();
+               return $elem;
+       };
+
+       /*
+        * Dialogs
+        */
+       function GenericDialog(element) {
+               if(!element) return;
+
+               var self = this;
+
+               self.$elem = $(element);
+
+               if(!self.$elem.attr('data-ui-initialized')) {
+                       console.log("Initializing dialog", this);
+                       self.initialize();
+                       self.$elem.attr('data-ui-initialized', true);
+               }
+
+               self.show();
+       };
+
+       GenericDialog.prototype = {
+
+               /*
+               * Steps to follow when the dialog in first loaded on page.
+               */
+               initialize: function(){
+                       var self = this;
+
+                       /* bind buttons */
+                       $('button[data-ui-action]', self.$elem).click(function(event) {
+                               event.preventDefault();
+
+                               var action = $(this).attr('data-ui-action');
+                               console.log("Button pressed, action: ", action);
+
+                               try {
+                                       self[action + "Action"].call(self);
+                               } catch(e) {
+                                       console.log("Action failed:", e);
+                                       // always hide on cancel
+                                       if(action == 'cancel')
+                                               self.hide();
+                               }
+                       });
+               },
+
+               /*
+                * Prepare dialog for user. Clear any unnessary data.
+               */
+               show: function() {
+                       $.blockUI({
+                               message: this.$elem,
+                               css: {
+                                       'top': '25%',
+                                       'left': '25%',
+                                       'width': '50%'
+                               }
+                       });
+               },
+
+               hide: function(){
+                       $.unblockUI();
+               },
+
+               cancelAction: function() {
+                       this.hide();
+               },
+
+               doneAction: function() {
+                       this.hide();
+               },
+
+               clearForm: function() {
+                       $("*[data-ui-error-for]", this.$elem).text('');
+               },
+
+               reportErrors: function(errors) {
+                       var global = $("*[data-ui-error-for='__all__']", this.$elem);
+                       var unassigned = [];
+
+                       for (var field_name in errors)
+                       {
+                               var span = $("*[data-ui-error-for='"+field_name+"']", this.$elem);
+
+                               if(!span.length) {
+                                       unassigned.push(field_name);
+                                       continue;
+                               }
+
+                               span.text(errors[field_name].join(' '));
+                       }
+
+                       if(unassigned.length > 0)
+                               global.text( global.text() + 'W formularzu wystąpiły błędy');
+               }
+       };
+
+       $.wiki.cls.GenericDialog = GenericDialog;
+
+       $.wiki.showDialog = function(selector, options) {
+               var elem = $(selector);
+
+               if(elem.length != 1) {
+                       console.log("Failed to show dialog:", selector, elem);
+                       return false;
+               }
+
+               try {
+                   var klass = elem.attr('data-ui-jsclass');
+                       return new $.wiki.cls[klass](elem, options);
+               } catch(e) {
+                       console.log("Failed to show dialog", selector, klass, e);
+                       return false;
+               }
+       };
+
+})(jQuery);
diff --git a/src/redakcja/static/js/wiki_img/loader.js b/src/redakcja/static/js/wiki_img/loader.js
new file mode 100644 (file)
index 0000000..c3fe03e
--- /dev/null
@@ -0,0 +1,137 @@
+if (!window.console) {
+    window.console = {
+        log: function(){
+        }
+    }
+}
+
+DEFAULT_PERSPECTIVE = "#MotifsPerspective";
+
+$(function()
+{
+       var tabs = $('ol#tabs li');
+       var gallery = null;
+       CurrentDocument = new $.wikiapi.WikiDocument("document-meta");
+
+       $.blockUI.defaults.baseZ = 10000;
+
+    function initialize()
+       {
+               $(document).keydown(function(event) {
+                       console.log("Received key:", event);
+               });
+
+               /* The save button */
+        $('#save-button').click(function(event){
+            event.preventDefault();
+                       $.wiki.showDialog('#save_dialog');
+        });
+
+               $('.editor').hide();
+
+               /*
+                * TABS
+                */
+        $('.tabs li').live('click', function(event, callback) {
+            event.preventDefault();
+                       $.wiki.switchToTab(this);
+        });
+
+               $('#tabs li > .tabclose').live('click', function(event, callback) {
+                       var $tab = $(this).parent();
+
+                       if($tab.is('.active'))
+                               $.wiki.switchToTab(DEFAULT_PERSPECTIVE);
+
+                       var p = $.wiki.perspectiveForTab($tab);
+                       p.destroy();
+
+                       return false;
+        });
+
+
+        /*$(window).resize(function(){
+            $('iframe').height($(window).height() - $('#tabs').outerHeight() - $('#source-editor .toolbar').outerHeight());
+        });
+
+        $(window).resize();*/
+
+        /*$('.vsplitbar').toggle(
+                       function() {
+                               $.wiki.state.perspectives.ScanGalleryPerspective.show = true;
+                               $('#sidebar').show();
+                               $('.vsplitbar').css('right', 480).addClass('active');
+                               $('#editor .editor').css('right', 510);
+                               $(window).resize();
+                               $.wiki.perspectiveForTab('#tabs-right .active').onEnter();
+                       },
+                       function() {
+                           var active_right = $.wiki.perspectiveForTab('#tabs-right .active');
+                               $.wiki.state.perspectives.ScanGalleryPerspective.show = false;
+                               $('#sidebar').hide();
+                               $('.vsplitbar').css('right', 0).removeClass('active');
+                               $(".vsplitbar-title").html("&uarr;&nbsp;" + active_right.vsplitbar + "&nbsp;&uarr;");
+                               $('#editor .editor').css('right', 30);
+                               $(window).resize();
+                               active_right.onExit();
+                       }
+               );*/
+
+        window.onbeforeunload = function(e) {
+            if($.wiki.isDirty()) {
+                               e.returnValue = "Na stronie mogą być nie zapisane zmiany.";
+                               return "Na stronie mogą być nie zapisane zmiany.";
+                       };
+        };
+
+               console.log("Fetching document's text");
+
+               $(document).bind('wlapi_document_changed', function(event, doc) {
+                       try {
+                               $('#document-revision').text(doc.revision);
+                       } catch(e) {
+                               console.log("Failed handler", e);
+                       }
+               });
+
+               CurrentDocument.fetch({
+                       success: function(){
+                               console.log("Fetch success");
+                               $('#loading-overlay').fadeOut();
+                               var active_tab = document.location.hash || DEFAULT_PERSPECTIVE;
+
+                               console.log("Initial tab is:", active_tab)
+                               $.wiki.switchToTab(active_tab);
+                       },
+                       failure: function() {
+                               $('#loading-overlay').fadeOut();
+                               alert("FAILURE");
+                       }
+               });
+    }; /* end of initialize() */
+
+
+       /* Load configuration */
+       $.wiki.loadConfig();
+
+       var initAll = function(a, f) {
+               if (a.length == 0) return f();
+
+               $.wiki.initTab({
+                       tab: a.pop(),
+                       doc: CurrentDocument,
+                       callback: function(){
+                               initAll(a, f);
+                       }
+               });
+       };
+
+
+       /*
+        * Initialize all perspectives
+        */
+       initAll( $.makeArray($('.tabs li')), initialize);
+       console.log(location.hash);
+});
+
+
diff --git a/src/redakcja/static/js/wiki_img/loader_readonly.js b/src/redakcja/static/js/wiki_img/loader_readonly.js
new file mode 100755 (executable)
index 0000000..99e5ad0
--- /dev/null
@@ -0,0 +1,93 @@
+if (!window.console) {
+    window.console = {
+        log: function(){
+        }
+    }
+}
+
+
+DEFAULT_PERSPECTIVE = "#MotifsPerspective";
+
+
+$(function()
+{
+       var tabs = $('ol#tabs li');
+       var gallery = null;
+
+       CurrentDocument = new $.wikiapi.WikiDocument("document-meta");
+       $.blockUI.defaults.baseZ = 10000;
+
+       function initialize()
+       {
+               $('.editor').hide();
+
+               /*
+                * TABS
+                */
+        $('#tabs li').live('click', function(event, callback) {
+            event.preventDefault();
+                       $.wiki.switchToTab(this);
+        });
+
+               $('#tabs li > .tabclose').live('click', function(event, callback) {
+                       var $tab = $(this).parent();
+
+                       if($tab.is('.active'))
+                               $.wiki.switchToTab(DEFAULT_PERSPECTIVE);
+
+                       var p = $.wiki.perspectiveForTab($tab);
+                       p.destroy();
+                       return false;
+        });
+/*
+        $(window).resize(function(){
+            $('iframe').height($(window).height() - $('#tabs').outerHeight() - $('#source-editor .toolbar').outerHeight());
+        });
+        */
+
+               $(document).bind('wlapi_document_changed', function(event, doc) {
+                       try {
+                               $('#document-revision').text(doc.revision);
+                       } catch(e) {
+                               console.log("Failed handler", e);
+                       }
+               });
+
+               CurrentDocument.fetch({
+                       success: function(){
+                               console.log("Fetch success");
+                               $('#loading-overlay').fadeOut();
+                               var active_tab = document.location.hash || DEFAULT_PERSPECTIVE;
+
+                               console.log("Initial tab is:", active_tab)
+                               $.wiki.switchToTab(active_tab);
+                       },
+                       failure: function() {
+                               $('#loading-overlay').fadeOut();
+                               alert("FAILURE");
+                       }
+               });
+    }; /* end of initialize() */
+
+       /* Load configuration */
+       $.wiki.loadConfig();
+
+       var initAll = function(a, f) {
+               if (a.length == 0) return f();
+
+               $.wiki.initTab({
+                       tab: a.pop(),
+                       doc: CurrentDocument,
+                       callback: function(){
+                               initAll(a, f);
+                       }
+               });
+       };
+
+
+       /*
+        * Initialize all perspectives
+        */
+       initAll( $.makeArray($('ol#tabs li')), initialize);
+       console.log(location.hash);
+});
diff --git a/src/redakcja/static/js/wiki_img/view_editor_motifs.js b/src/redakcja/static/js/wiki_img/view_editor_motifs.js
new file mode 100644 (file)
index 0000000..ad60c22
--- /dev/null
@@ -0,0 +1,158 @@
+(function($){
+
+    function MotifsPerspective(options){
+
+        var old_callback = options.callback;
+
+        options.callback = function(){
+            var self = this;
+
+            self.$tag_name = $('#motifs-editor .tag-name');
+            self.$toolbar = $('#motifs-editor .toolbar');
+            self.$scrolled = $('#motifs-editor .scrolled');
+            withThemes(function(canonThemes){
+                self.$tag_name.autocomplete(canonThemes, {
+                    autoFill: true,
+                    multiple: true,
+                    selectFirst: true,
+                    highlight: false
+                });
+            })
+
+            self.$objects_list = $('#motifs-editor .objects-list');
+
+            self.x1 = null;
+            self.x2 = null;
+            self.y1 = null;
+            self.y2 = null;
+
+            if (!CurrentDocument.readonly) {
+                self.ias = $('#motifs-editor img.area-selectable').imgAreaSelect({ handles: true, onSelectEnd: self._fillCoords(self), instance: true });
+                $('#motifs-editor .add').click(self._addObject(self));
+
+                $('.delete', self.$objects_list).live('click', function() {
+                    $(this).prev().trigger('click');
+                    if (window.confirm("Czy na pewno chcesz usunąć ten motyw?")) {
+                        $(this).prev().remove();
+                        $(this).remove();
+                        self._refreshLayout();
+                    }
+                    self._resetSelection();
+                    return false;
+                });
+            }
+
+            $('.image-object', self.$objects_list).live('click', function(){
+                $('.active', self.$objects_list).removeClass('active');
+                $(this).addClass('active');
+                var coords = $(this).data('coords');
+                if (coords) {
+                    self.ias.setSelection.apply(self.ias, coords);
+                    self.ias.setOptions({ show: true });
+                }
+                else {
+                    self._resetSelection();
+                }
+            });
+
+            old_callback.call(this);
+        };
+
+        $.wiki.Perspective.call(this, options);
+    };
+
+    MotifsPerspective.prototype = new $.wiki.Perspective();
+
+    MotifsPerspective.prototype.freezeState = function(){
+
+    };
+
+    MotifsPerspective.prototype._resetSelection = function() {
+        var self = this;
+        self.x1 = self.x2 = self.y1 = self.y2 = null;
+        self.ias.setOptions({ hide: true });
+    }
+
+    MotifsPerspective.prototype._refreshLayout = function() {
+        this.$scrolled.css({top: this.$toolbar.height()});
+    };
+
+    MotifsPerspective.prototype._push = function(name, x1, y1, x2, y2) {
+        var $e = $('<span class="image-object"></span>')
+        $e.text(name);
+        if (x1 !== null)
+            $e.data('coords', [x1, y1, x2, y2]);
+        this.$objects_list.append($e);
+        this.$objects_list.append('<span class="delete">(x) </span>');
+        this._refreshLayout();
+    }
+
+
+    MotifsPerspective.prototype._addObject = function(self) {
+        return function() {
+            outputs = [];
+            chunks = self.$tag_name.val().split(',');
+            for (i in chunks) {
+                item = chunks[i].trim();
+                if (item == '')
+                    continue;
+                outputs.push(item.trim());
+            }
+            output = outputs.join(', ');
+
+            self._push(output, self.x1, self.y1, self.x2, self.y2);
+            self._resetSelection();
+        }
+    }
+
+    MotifsPerspective.prototype._fillCoords = function(self) {
+        return function(img, selection) {
+            $('.active', self.$objects_list).removeClass('active');
+            if (selection.x1 != selection.x2 && selection.y1 != selection.y2) {
+                self.x1 = selection.x1;
+                self.x2 = selection.x2;
+                self.y1 = selection.y1;
+                self.y2 = selection.y2;
+            }
+            else {
+                self.x1 = self.x2 = self.y1 = self.y2 = null;
+            }
+        }
+    }
+
+    MotifsPerspective.prototype.onEnter = function(success, failure){
+        var self = this;
+        this.$objects_list.children().remove();
+
+        $.each(this.doc.getImageItems('theme'), function(i, e) {
+            self._push.apply(self, e);
+        });
+
+        if (this.x1 !== null)
+            this.ias.setOptions({enable: true, show: true});
+        else
+            this.ias.setOptions({enable: true});
+
+        $.wiki.Perspective.prototype.onEnter.call(this);
+
+    };
+
+    MotifsPerspective.prototype.onExit = function(success, failure){
+        var self = this;
+        var motifs = [];
+        this.$objects_list.children(".image-object").each(function(i, e) {
+            var args = $(e).data('coords');
+            if (!args)
+                args = [null, null, null, null];
+            args.unshift($(e).text());
+            motifs.push(args);
+        })
+        self.doc.setImageItems('theme', motifs);
+
+        this.ias.setOptions({disable: true, hide: true});
+
+    };
+
+    $.wiki.MotifsPerspective = MotifsPerspective;
+
+})(jQuery);
diff --git a/src/redakcja/static/js/wiki_img/view_editor_objects.js b/src/redakcja/static/js/wiki_img/view_editor_objects.js
new file mode 100644 (file)
index 0000000..b0cf2a9
--- /dev/null
@@ -0,0 +1,149 @@
+(function($){
+
+    function ObjectsPerspective(options){
+
+        var old_callback = options.callback;
+
+        options.callback = function(){
+            var self = this;
+
+            self.$tag_name = $('#objects-editor .tag-name');
+            self.$toolbar = $('#objects-editor .toolbar');
+            self.$scrolled = $('#objects-editor .scrolled');
+            self.$objects_list = $('#objects-editor .objects-list');
+
+            self.x1 = null;
+            self.x2 = null;
+            self.y1 = null;
+            self.y2 = null;
+
+            if (!CurrentDocument.readonly) {
+                self.ias = $('#objects-editor img.area-selectable').imgAreaSelect({ handles: true, onSelectEnd: self._fillCoords(self), instance: true });
+                $('#objects-editor .add').click(self._addObject(self));
+
+                $('.delete', self.$objects_list).live('click', function() {
+                    $(this).prev().trigger('click');
+                    if (window.confirm("Czy na pewno chcesz usunąć ten obiekt?")) {
+                        $(this).prev().remove();
+                        $(this).remove();
+                        self._refreshLayout();
+                    }
+                    self._resetSelection();
+                    return false;
+                });
+            }
+
+            $('.image-object', self.$objects_list).live('click', function(){
+                $('.active', self.$objects_list).removeClass('active');
+                $(this).addClass('active');
+                var coords = $(this).data('coords');
+                if (coords) {
+                    self.ias.setSelection.apply(self.ias, coords);
+                    self.ias.setOptions({ show: true });
+                }
+                else {
+                    self._resetSelection();
+                }
+            });
+
+            old_callback.call(this);
+        };
+
+        $.wiki.Perspective.call(this, options);
+    };
+
+    ObjectsPerspective.prototype = new $.wiki.Perspective();
+
+    ObjectsPerspective.prototype.freezeState = function(){
+
+    };
+
+    ObjectsPerspective.prototype._resetSelection = function() {
+        var self = this;
+        self.x1 = self.x2 = self.y1 = self.y2 = null;
+        self.ias.setOptions({ hide: true });
+    }
+
+    ObjectsPerspective.prototype._refreshLayout = function() {
+        this.$scrolled.css({top: this.$toolbar.height()});
+    };
+
+    ObjectsPerspective.prototype._push = function(name, x1, y1, x2, y2) {
+        var $e = $('<span class="image-object"></span>')
+        $e.text(name);
+        if (x1 !== null)
+            $e.data('coords', [x1, y1, x2, y2]);
+        this.$objects_list.append($e);
+        this.$objects_list.append('<span class="delete">(x) </span>');
+        this._refreshLayout();
+    }
+
+
+    ObjectsPerspective.prototype._addObject = function(self) {
+        return function() {
+            outputs = [];
+            chunks = self.$tag_name.val().split(',');
+            for (i in chunks) {
+                item = chunks[i].trim();
+                if (item == '')
+                    continue;
+                outputs.push(item.trim());
+            }
+            output = outputs.join(', ');
+
+            self._push(output, self.x1, self.y1, self.x2, self.y2);
+            self._resetSelection();
+        }
+    }
+
+    ObjectsPerspective.prototype._fillCoords = function(self) {
+        return function(img, selection) {
+            $('.active', self.$objects_list).removeClass('active');
+            if (selection.x1 != selection.x2 && selection.y1 != selection.y2) {
+                self.x1 = selection.x1;
+                self.x2 = selection.x2;
+                self.y1 = selection.y1;
+                self.y2 = selection.y2;
+            }
+            else {
+                self.x1 = self.x2 = self.y1 = self.y2 = null;
+            }
+        }
+    }
+
+    ObjectsPerspective.prototype.onEnter = function(success, failure){
+        var self = this;
+        this.$objects_list.children().remove();
+
+        $.each(this.doc.getImageItems('object'), function(i, e) {
+            self._push.apply(self, e);
+        });
+
+        if (this.x1 !== null)
+            this.ias.setOptions({enable: true, show: true});
+        else
+            this.ias.setOptions({enable: true});
+
+        $.wiki.Perspective.prototype.onEnter.call(this);
+
+    };
+
+    ObjectsPerspective.prototype.onExit = function(success, failure){
+        var self = this;
+        var objects = [];
+        this.$objects_list.children(".image-object").each(function(i, e) {
+            var args = $(e).data('coords');
+            if (!args)
+                args = [null, null, null, null];
+            args.unshift($(e).text());
+            objects.push(args);
+        })
+        self.doc.setImageItems('object', objects);
+
+        this.ias.setOptions({disable: true, hide: true});
+
+    };
+
+    $.wiki.ObjectsPerspective = ObjectsPerspective;
+
+})(jQuery);
diff --git a/src/redakcja/static/js/wiki_img/wikiapi.js b/src/redakcja/static/js/wiki_img/wikiapi.js
new file mode 100644 (file)
index 0000000..377e4f9
--- /dev/null
@@ -0,0 +1,418 @@
+(function($) {
+       $.wikiapi = {};
+       var noop = function() {
+       };
+       var noops = {
+               success: noop,
+               failure: noop
+       };
+       /*
+        * Return absolute reverse path of given named view. (at least he have it
+        * hard-coded in one place)
+        *
+        * TODO: think of a way, not to hard-code it here ;)
+        *
+        */
+       function reverse() {
+               var vname = arguments[0];
+               var base_path = "/images";
+
+               if (vname == "ajax_document_text")
+                   return base_path + "/text/" + arguments[1] + "/";
+
+
+        if (vname == "ajax_document_revert") {
+            return base_path + "/revert/" + arguments[1] + '/';
+        }
+
+               if (vname == "ajax_document_history") {
+                       return base_path + "/history/" + arguments[1] + '/';
+               }
+
+               if (vname == "ajax_document_diff")
+                       return base_path + "/diff/" + arguments[1] + '/';
+
+               if (vname == "ajax_document_pubmark")
+                       return base_path + "/pubmark/" + arguments[1] + '/';
+
+               console.log("Couldn't reverse match:", vname);
+               return "/404.html";
+       };
+
+       /*
+        * Document Abstraction
+        */
+       function WikiDocument(element_id) {
+               var meta = $('#' + element_id);
+               this.id = meta.attr('data-object-id');
+
+               this.revision = $("*[data-key='revision']", meta).text();
+               this.readonly = !!$("*[data-key='readonly']", meta).text();
+
+        var diff = $("*[data-key='diff']", meta).text();
+        if (diff) {
+            diff = diff.split(',');
+            if (diff.length == 2 && diff[0] < diff[1])
+                this.diff = diff;
+            else if (diff.length == 1) {
+                diff = parseInt(diff);
+                if (diff != NaN)
+                    this.diff = [diff - 1, diff];
+            }
+        }
+
+               this.text = null;
+               this.has_local_changes = false;
+               this._lock = -1;
+               this._context_lock = -1;
+               this._lock_count = 0;
+       };
+
+       WikiDocument.prototype.triggerDocumentChanged = function() {
+               $(document).trigger('wlapi_document_changed', this);
+       };
+       /*
+        * Fetch text of this document.
+        */
+       WikiDocument.prototype.fetch = function(params) {
+               params = $.extend({}, noops, params);
+               var self = this;
+               $.ajax({
+                       method: "GET",
+                       url: reverse("ajax_document_text", self.id),
+                       data: {"revision": self.revision},
+                       dataType: 'json',
+                       success: function(data) {
+                               var changed = false;
+
+                               if (self.text === null || self.revision !== data.revision) {
+                                       self.text = data.text;
+                                       if (self.text === '') {
+                                           self.text = '<picture></picture>';
+                                       }
+                                       self.revision = data.revision;
+//                    self.commit = data.commit;
+                                       changed = true;
+                                       self.triggerDocumentChanged();
+                               };
+
+                               self.has_local_changes = false;
+                               params['success'](self, changed);
+                       },
+                       error: function() {
+                               params['failure'](self, "Nie udało się wczytać treści dokumentu.");
+                       }
+               });
+       };
+       /*
+        * Fetch history of this document.
+        *
+        * from - First revision to fetch (default = 0) upto - Last revision to
+        * fetch (default = tip)
+        *
+        */
+       WikiDocument.prototype.fetchHistory = function(params) {
+               /* this doesn't modify anything, so no locks */
+               params = $.extend({}, noops, params);
+               var self = this;
+               $.ajax({
+                       method: "GET",
+                       url: reverse("ajax_document_history", self.id),
+                       dataType: 'json',
+                       data: {
+                               "from": params['from'],
+                               "upto": params['upto']
+                       },
+                       success: function(data) {
+                               params['success'](self, data);
+                       },
+                       error: function() {
+                               params['failure'](self, "Nie udało się wczytać historii dokumentu.");
+                       }
+               });
+       };
+       WikiDocument.prototype.fetchDiff = function(params) {
+               /* this doesn't modify anything, so no locks */
+               var self = this;
+               params = $.extend({
+                       'from': self.revision,
+                       'to': self.revision
+               }, noops, params);
+               $.ajax({
+                       method: "GET",
+                       url: reverse("ajax_document_diff", self.id),
+                       dataType: 'html',
+                       data: {
+                               "from": params['from'],
+                               "to": params['to']
+                       },
+                       success: function(data) {
+                               params['success'](self, data);
+                       },
+                       error: function() {
+                               params['failure'](self, "Nie udało się wczytać porównania wersji.");
+                       }
+               });
+       };
+
+       /*
+        * Set document's text
+        */
+       WikiDocument.prototype.setText = function(text) {
+               this.text = text;
+               this.has_local_changes = true;
+       };
+
+       /*
+        * Save text back to the server
+        */
+       WikiDocument.prototype.save = function(params) {
+               params = $.extend({}, noops, params);
+               var self = this;
+
+               if (!self.has_local_changes) {
+                       console.log("Abort: no changes.");
+                       return params['success'](self, false, "Nie ma zmian do zapisania.");
+               };
+
+               // Serialize form to dictionary
+               var data = {};
+               $.each(params['form'].serializeArray(), function() {
+                       data[this.name] = this.value;
+               });
+
+               data['textsave-text'] = self.text;
+
+               $.ajax({
+                       url: reverse("ajax_document_text", self.id),
+                       type: "POST",
+                       dataType: "json",
+                       data: data,
+                       success: function(data) {
+                               var changed = false;
+
+                $('#header').removeClass('saving');
+
+                               if (data.text) {
+                                       self.text = data.text;
+                                       self.revision = data.revision;
+//                    self.commit = data.commit;
+                                       changed = true;
+                                       self.triggerDocumentChanged();
+                               };
+
+                               params['success'](self, changed, ((changed && "Udało się zapisać :)") || "Twoja wersja i serwera jest identyczna"));
+                       },
+                       error: function(xhr) {
+                if ($('#header').hasClass('saving')) {
+                    $('#header').removeClass('saving');
+                    $.blockUI({
+                        message: "<p>Nie udało się zapisać zmian. <br/><button onclick='$.unblockUI()'>OK</button></p>"
+                    })
+                }
+                else {
+                    try {
+                        params['failure'](self, $.parseJSON(xhr.responseText));
+                    }
+                    catch (e) {
+                        params['failure'](self, {
+                            "__message": "<p>Nie udało się zapisać - błąd serwera.</p>"
+                        });
+                    };
+                }
+
+                       }
+               });
+
+        $('#save-hide').click(function(){
+            $('#header').addClass('saving');
+            $.unblockUI();
+            $.wiki.blocking.unblock();
+        });
+       }; /* end of save() */
+
+    WikiDocument.prototype.revertToVersion = function(params) {
+        var self = this;
+        params = $.extend({}, noops, params);
+
+        if (params.revision >= this.revision) {
+            params.failure(self, 'Proszę wybrać rewizję starszą niż aktualna.');
+            return;
+        }
+
+        // Serialize form to dictionary
+        var data = {};
+        $.each(params['form'].serializeArray(), function() {
+            data[this.name] = this.value;
+        });
+
+        $.ajax({
+            url: reverse("ajax_document_revert", self.id),
+            type: "POST",
+            dataType: "json",
+            data: data,
+            success: function(data) {
+                if (data.text) {
+                    self.text = data.text;
+                    self.revision = data.revision;
+                    self.gallery = data.gallery;
+                    self.triggerDocumentChanged();
+
+                    params.success(self, "Udało się przywrócić wersję :)");
+                }
+                else {
+                    params.failure(self, "Przywracana wersja identyczna z aktualną. Anulowano przywracanie.");
+                }
+            },
+            error: function(xhr) {
+                params.failure(self, "Nie udało się przywrócić wersji - błąd serwera.");
+            }
+        });
+    };
+
+       WikiDocument.prototype.pubmark = function(params) {
+               params = $.extend({}, noops, params);
+               var self = this;
+               var data = {
+                       "pubmark-id": self.id,
+               };
+
+               /* unpack form */
+               $.each(params.form.serializeArray(), function() {
+                       data[this.name] = this.value;
+               });
+
+               $.ajax({
+                       url: reverse("ajax_document_pubmark", self.id),
+                       type: "POST",
+                       dataType: "json",
+                       data: data,
+                       success: function(data) {
+                               params.success(self, data.message);
+                       },
+                       error: function(xhr) {
+                               if (xhr.status == 403 || xhr.status == 401) {
+                                       params.failure(self, {
+                                               "__all__": ["Nie masz uprawnień lub nie jesteś zalogowany."]
+                                       });
+                               }
+                               else {
+                                       try {
+                                               params.failure(self, $.parseJSON(xhr.responseText));
+                                       }
+                                       catch (e) {
+                                               params.failure(self, {
+                                                       "__all__": ["Nie udało się - błąd serwera."]
+                                               });
+                                       };
+                               };
+                       }
+               });
+       };
+
+
+
+    WikiDocument.prototype.getImageItems = function(tag) {
+        var self = this;
+
+        var parser = new DOMParser();
+        var doc = parser.parseFromString(self.text, 'text/xml');
+        var error = $('parsererror', doc);
+
+        if (error.length != 0) {
+            return null;
+        }
+
+        var a = [];
+        $('sem[type="'+tag+'"]', doc).each(function(i, e) {
+            var $e = $(e);
+            var $div = $e.children().first()
+            var value = $e.attr(tag);
+            $e.find('div').each(function(i, div) {
+                var $div = $(div);
+                switch ($div.attr('type')) {
+                    case 'rect':
+                        a.push([
+                            value,
+                            $div.attr('x1'),
+                            $div.attr('y1'),
+                            $div.attr('x2'),
+                            $div.attr('y2')
+                        ]);
+                        break;
+                    case 'whole':
+                        a.push([
+                            value,
+                            null, null, null, null
+                        ]);
+                        break
+                }
+            });
+        });
+
+        return a;
+    }
+
+    WikiDocument.prototype.setImageItems = function(tag, items) {
+        var self = this;
+
+        var parser = new DOMParser();
+        var doc = parser.parseFromString(self.text, 'text/xml');
+        var serializer = new XMLSerializer();
+        var error = $('parsererror', doc);
+
+        if (error.length != 0) {
+            return null;
+        }
+
+        $('sem[type="'+tag+'"]', doc).remove();
+        $root = $(doc.firstChild);
+        $.each(items, function(i, e) {
+            var $sem = $(doc.createElement("sem"));
+            $sem.attr('type', tag);
+            $sem.attr(tag, e[0]);
+            $div = $(doc.createElement("div"));
+            if (e[1]) {
+                $div.attr('type', 'rect');
+                $div.attr('x1', e[1]);
+                $div.attr('y1', e[2]);
+                $div.attr('x2', e[3]);
+                $div.attr('y2', e[4]);
+            }
+            else {
+                $div.attr('type', 'whole');
+            }
+            $sem.append($div);
+            $root.append($sem);
+        });
+        self.setText(serializer.serializeToString(doc));
+    }
+
+
+       $.wikiapi.WikiDocument = WikiDocument;
+})(jQuery);
+
+
+
+// Wykonuje block z załadowanymi kanonicznymi motywami
+function withThemes(code_block, onError)
+{
+    if (typeof withThemes.canon == 'undefined') {
+        $.ajax({
+            url: '/editor/themes',
+            dataType: 'text',
+            success: function(data) {
+                withThemes.canon = data.split('\n');
+                code_block(withThemes.canon);
+            },
+            error: function() {
+                withThemes.canon = null;
+                code_block(withThemes.canon);
+            }
+        })
+    }
+    else {
+        code_block(withThemes.canon);
+    }
+}
+
diff --git a/src/redakcja/static/xsl/wl2html_client.xsl b/src/redakcja/static/xsl/wl2html_client.xsl
new file mode 100644 (file)
index 0000000..784acb7
--- /dev/null
@@ -0,0 +1,867 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xsl:stylesheet version="1.0"    
+    xmlns="http://www.w3.org/1999/xhtml"    
+    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+
+    <!--
+        Dokument ten opisuje jednoznaczne przekształcenie WLML 0.1 -> XHTML.
+    -->        
+    <xsl:output method="html" omit-xml-declaration="yes" encoding="utf-8" indent="no" />
+
+    <!--
+        Base tag for rendering a fragment of text
+    -->
+    <xsl:template match="chunk">        
+        <xsl:apply-templates select="child::node()">            
+            <xsl:with-param name="mixed" select="true()" />
+        </xsl:apply-templates>        
+    </xsl:template>
+    
+    <!--
+        Base tag for rendering the whole text 
+    -->
+
+    <xsl:template match="utwor">
+        <div>
+            <xsl:call-template name="standard-attributes" />
+            <xsl:apply-templates select="child::node()">
+                <xsl:with-param name="mixed" select="false()" />
+            </xsl:apply-templates>
+        </div>
+    </xsl:template>    
+    
+    <!-- 
+        Przekształcenia poszczególnych elementów zgodnie z:            
+        http://wiki.wolnepodreczniki.pl/Lektury:Sk%C5%82ad/Tagi_sk%C5%82adu
+    -->
+
+    <!-- TAGI MASTERÓW STYLÓW
+    
+        Tagi rozpoczynające i kończące tekst utworu lirycznego o standardowej szerokości łamu:
+    -->
+
+    <xsl:template match="opowiadanie|powiesc">
+        <xsl:param name="mixed" />
+        <div>
+            <xsl:call-template name="standard-attributes" />
+            <xsl:apply-templates select="child::node()">
+                <xsl:with-param name="mixed" select="false()" />
+            </xsl:apply-templates>
+        </div>
+    </xsl:template>
+
+    <xsl:template match="liryka_l|liryka_lp">
+        <xsl:param name="mixed" />
+        <div>
+            <xsl:call-template name="standard-attributes" />
+            <xsl:apply-templates select="child::node()">
+                <xsl:with-param name="mixed" select="false()" />
+            </xsl:apply-templates>
+        </div>
+    </xsl:template>
+
+    <xsl:template match="dramat_wierszowany_l|dramat_wierszowany_lp|dramat_wspolczesny">
+        <xsl:param name="mixed" />
+        <div>
+            <xsl:call-template name="standard-attributes" />
+            <xsl:apply-templates select="child::node()">
+                <xsl:with-param name="mixed" select="false()" />
+            </xsl:apply-templates>
+        </div>
+    </xsl:template>
+
+    <xsl:template match="wywiad">
+        <xsl:param name="mixed" />
+        <div>
+            <xsl:call-template name="standard-attributes" />
+            <xsl:apply-templates select="child::node()">
+                <xsl:with-param name="mixed" select="false()" />
+            </xsl:apply-templates>
+        </div>
+    </xsl:template>
+
+    <!-- 
+        *****************************
+        ELEMENTY POZA TEKSTEM GŁÓWNYM
+        *****************************
+    -->
+
+    <!--
+        Autor składanego utworu
+
+        Element strony tytułowej - lub odpowiadającej jej przestrzeni,
+        np. na stronie internetowej) :
+
+        <autor_utworu> imiona-itd.-autora-składanego-utworu </autor_utworu>
+    -->
+    <xsl:template match="autor_utworu">
+        <xsl:param name="mixed" />
+        <h2 x-editable="true">
+            <xsl:call-template name="standard-attributes" />
+            <xsl:call-template name="context-menu" />
+            <xsl:apply-templates select="child::node()">
+                <xsl:with-param name="mixed" select="true()" />
+            </xsl:apply-templates>
+        </h2>
+    </xsl:template>
+
+    <!-- 
+        Nazwa składanego utworu
+
+        Element strony tytułowej - lub odpowiadającej jej przestrzeni,
+        np. na stronie internetowej
+
+        <nazwa_utworu> tytuł-składanego-utworu </nazwa_utworu>
+    -->
+    <xsl:template match="nazwa_utworu">
+        <xsl:param name="mixed" />
+        <h1 x-editable="true">
+            <xsl:call-template name="standard-attributes" />
+            <xsl:call-template name="context-menu" />
+            <xsl:apply-templates select="child::node()">
+                <xsl:with-param name="mixed" select="true()" />
+            </xsl:apply-templates>
+        </h1>
+    </xsl:template>
+
+    <!--
+        Nazwa utworu nadrzędnego, w którego skład wchodzi dany utwór
+        <dzielo_nadrzedne> tytuł-dzieła-nadrzędnego </dzielo_nadrzedne>
+
+         Przykład:
+
+         <utwor>
+         <liryka_l>
+            <autor_utworu>Bruno Jasieński</autor_utworu>
+            <dzielo_nadrzedne>But w butonierce</dzielo_nadrzedne>
+            <nazwa_utworu>Deszcz</nazwa_utworu>
+            ....
+    -->
+    <xsl:template match="dzielo_nadrzedne">
+        <xsl:param name="mixed" />
+        <h2 x-editable="true">
+            <xsl:call-template name="standard-attributes" />
+            <xsl:call-template name="context-menu" />
+            <xsl:apply-templates select="child::node()">
+                <xsl:with-param name="mixed" select="true()" />
+            </xsl:apply-templates>
+        </h2>
+    </xsl:template>
+
+    <!--
+        Podtytuł, czyli wszystkie dopiski do tytułu
+
+        Element strony tytułowej - lub odpowiadającej jej przestrzeni.
+        <podtytul> podtytuł-składanego-utworu </podtytul>
+
+        Przykład:
+        <utwor>
+            <powiesc>
+            <autor_utworu>Daniel Defoe</autor_utworu>
+            <nazwa_utworu>Robinson Crusoe</nazwa_utworu>
+            <podtytul>Jego życia losy, doświadczenia i przypadki</podtytul>
+            ...
+    -->
+    <xsl:template match="podtytul">
+        <xsl:param name="mixed" />
+        <h3 x-editable="true">
+            <xsl:call-template name="standard-attributes" />
+            <xsl:call-template name="context-menu" />
+            <xsl:apply-templates select="child::node()">
+                <xsl:with-param name="mixed" select="true()" />
+            </xsl:apply-templates>
+        </h3>
+    </xsl:template>
+
+    <!--
+       Tagi obejmujące tekst noty poprzedzającej tekst główny (styl wieloakapitowy):
+
+        <nota><akap> tekst-noty </akap></nota> (styl wieloakapitowy)
+    -->
+
+    <xsl:template match="nota">
+        <xsl:param name="mixed" />
+        <div>
+            <xsl:call-template name="standard-attributes" />
+            <xsl:apply-templates select="child::node()">
+                <xsl:with-param name="mixed" select="false()" />
+            </xsl:apply-templates>
+        </div>
+    </xsl:template>
+
+    <xsl:template match="nota_red">
+        <xsl:param name="mixed" />
+        <div>
+            <xsl:call-template name="standard-attributes" />
+            <xsl:apply-templates select="child::node()">
+                <xsl:with-param name="mixed" select="false()" />
+            </xsl:apply-templates>
+        </div>
+    </xsl:template>
+
+    <!--
+        Tagi obejmujące tekst dedykacji (styl wieloakapitowy bądź wielostrofowy):
+        <dedykacja> tekst-dedykacji </dedykacja>
+    -->
+    <xsl:template match="dedykacja">
+        <xsl:param name="mixed" />
+        <div>
+            <xsl:call-template name="standard-attributes" />
+            <xsl:apply-templates select="child::node()">
+                <xsl:with-param name="mixed" select="false()" />
+            </xsl:apply-templates>
+        </div>
+    </xsl:template>
+
+    <!--
+        Tagi obejmujące tekst w ramce (styl wieloakapitowy bądź wielostrofowy):
+        <ramka> tekst-w-ramce </ramka>
+    -->
+    <xsl:template match="ramka">
+        <xsl:param name="mixed" />
+        <div>
+            <xsl:call-template name="standard-attributes" />
+            <xsl:apply-templates select="child::node()">
+                <xsl:with-param name="mixed" select="false()" />
+            </xsl:apply-templates>
+        </div>
+    </xsl:template>
+
+    <!--
+        Tagi obejmujące tekst motta (styl wieloakapitowy bądź wielostrofowy):
+        <motto> tekst-motta </motto>
+    -->
+    <xsl:template match="motto">
+        <xsl:param name="mixed" />
+        <div class="motto_container" data-pass-thru="true">
+        <div x-editable="true">
+            <xsl:call-template name="standard-attributes" />
+            <xsl:apply-templates select="child::node()">
+                <xsl:with-param name="mixed" select="true()" />
+            </xsl:apply-templates>            
+        </div>
+        <xsl:apply-templates select="following-sibling::*[1][self::motto_podpis]" mode="motto">
+            <xsl:with-param name="mixed" select="true()" />
+        </xsl:apply-templates>
+        </div>
+    </xsl:template>
+
+    <!--  
+        Catch stand-alone "motto_podpis" and make then render properly.
+        If not, editing will fail :(.
+        
+        TODO: Make "motto" & "motto_podpis" one block in edit mode (like strofa) 
+    -->
+    <xsl:template match="motto_podpis[not(preceding-sibling::*[1][self::motto])]">
+       <xsl:apply-templates select="." mode="motto">
+            <xsl:with-param name="mixed" select="true()" />
+        </xsl:apply-templates>
+    </xsl:template>        
+        
+    <xsl:template match="motto_podpis" mode="motto">
+        <xsl:param name="mixed" />
+        <p x-editable="true">
+            <xsl:call-template name="standard-attributes" />
+            <xsl:call-template name="context-menu" />
+            <xsl:apply-templates select="child::node()">
+                <xsl:with-param name="mixed" select="true()" />
+            </xsl:apply-templates>
+        </p>
+    </xsl:template>    
+    
+    <xsl:template match="motto_podpis" />
+    
+    
+
+    <!--
+        Tagi obejmujące listę osób poprzedzającą tekst dramatu
+          (zwykle składaną na osobnej stronie; to odmiana stylu listy):
+
+        <lista_osob> osoby </lista_osob>
+    -->
+    <xsl:template match="lista_osob">
+        <xsl:param name="mixed" />
+        <div>
+            <xsl:call-template name="standard-attributes" />
+            <xsl:apply-templates select="child::node()">
+                <xsl:with-param name="mixed" select="false()" />
+            </xsl:apply-templates>
+        </div>
+    </xsl:template>
+
+    <xsl:template match="naglowek_listy">
+        <xsl:param name="mixed" />
+        <p x-editable="true">
+            <xsl:call-template name="standard-attributes" />
+            <xsl:call-template name="context-menu" />
+            <xsl:apply-templates select="child::node()">
+                <xsl:with-param name="mixed" select="true()" />
+            </xsl:apply-templates>
+        </p>
+    </xsl:template>
+
+    <xsl:template match="lista_osoba">
+        <xsl:param name="mixed" />
+        <p x-editable="true">
+            <xsl:call-template name="standard-attributes" />
+            <xsl:call-template name="context-menu" />
+            <xsl:apply-templates select="child::node()">
+                <xsl:with-param name="mixed" select="true()" />
+            </xsl:apply-templates>
+        </p>
+    </xsl:template>
+
+    <!--  Tagi obejmujące inne komentarze wprowadzające
+        przed tekstem dramatu (składane razem z listą osób):
+
+        <miejsce_czas> komentarze-wprowadzające </miejsce_czas>
+    -->
+    <xsl:template match="miejsce_czas">
+        <xsl:param name="mixed" />
+        <p x-editable="true">
+            <xsl:call-template name="standard-attributes" />
+            <xsl:call-template name="context-menu" />
+            <xsl:apply-templates select="child::node()">
+                <xsl:with-param name="mixed" select="true()" />
+            </xsl:apply-templates>
+        </p>
+    </xsl:template>
+
+
+    <!--
+        ***************************
+        TAGI STYLÓW TEKSTU GŁÓWNEGO
+        ***************************
+    -->
+
+    <!--
+      Tagi nagłówka części/księgi:
+      <naglowek_czesc> nagłówek-części-lub-księgi </naglowek_czesc>
+    -->
+    <xsl:template match="naglowek_czesc">
+        <xsl:param name="mixed" />
+        <h2 x-editable="true">
+            <xsl:call-template name="standard-attributes" />
+            <xsl:call-template name="context-menu" />
+            <xsl:apply-templates select="child::node()">
+                <xsl:with-param name="mixed" select="true()" />
+            </xsl:apply-templates>
+        </h2>
+    </xsl:template>
+
+    <!--
+      Tagi tytułu rozdziału:
+      <naglowek_rozdzial> nr-i/lub-tytuł </naglowek_rozdzial>
+    -->
+    <xsl:template match="naglowek_rozdzial">
+        <xsl:param name="mixed" />
+        <h3 x-editable="true">
+            <xsl:call-template name="standard-attributes" />
+            <xsl:call-template name="context-menu" />
+            <xsl:apply-templates select="child::node()">
+                <xsl:with-param name="mixed" select="true()" />
+            </xsl:apply-templates>
+        </h3>
+    </xsl:template>
+
+    <!--
+      Tagi tytułu podrozdziału:
+      <naglowek_podrozdzial> nr-i/lub-podtytuł </naglowek_podrozdzial>
+    -->
+    <xsl:template match="naglowek_podrozdzial">
+        <xsl:param name="mixed" />
+        <h4 x-editable="true">
+            <xsl:call-template name="standard-attributes" />
+            <xsl:call-template name="context-menu" />
+            <xsl:apply-templates select="child::node()">
+                <xsl:with-param name="mixed" select="true()" />
+            </xsl:apply-templates>
+        </h4>
+    </xsl:template>
+
+    <!--
+       Tagi sródtytułu:
+       <srodtytul> śródtytuł </srodtytul>
+
+       Tagi nagłówków aktów:
+       <naglowek_akt> nagłówek-aktu </naglowek_akt>
+    -->
+
+    <xsl:template match="srodtytul">
+        <xsl:param name="mixed" />
+        <h2 x-editable="true">
+            <xsl:call-template name="standard-attributes" />
+            <xsl:call-template name="context-menu" />
+            <xsl:apply-templates select="child::node()">
+                <xsl:with-param name="mixed" select="true()" />
+            </xsl:apply-templates>
+        </h2>
+    </xsl:template>
+
+    <xsl:template match="naglowek_akt">
+        <xsl:param name="mixed" />
+        <h2 x-editable="true">
+            <xsl:call-template name="standard-attributes" />
+            <xsl:call-template name="context-menu" />
+            <xsl:apply-templates select="child::node()">
+                <xsl:with-param name="mixed" select="true()" />
+            </xsl:apply-templates>
+        </h2>
+    </xsl:template>
+
+    <!--
+      Tagi nagłówków scen:
+      <naglowek_scena> nagłówek-sceny </naglowek_scena>
+    -->
+
+    <xsl:template match="naglowek_scena">
+        <xsl:param name="mixed" />
+        <h3 x-editable="true">
+            <xsl:call-template name="standard-attributes" />
+            <xsl:call-template name="context-menu" />
+            <xsl:apply-templates select="child::node()">
+                <xsl:with-param name="mixed" select="true()" />
+            </xsl:apply-templates>
+        </h3>
+    </xsl:template>
+    
+    <xsl:template match="naglowek_osoba">
+        <xsl:param name="mixed" />
+        <h4 x-editable="true">
+            <xsl:call-template name="standard-attributes" />
+            <xsl:call-template name="context-menu" />
+            <xsl:apply-templates select="child::node()">
+                <xsl:with-param name="mixed" select="true()" />
+            </xsl:apply-templates>
+        </h4>
+    </xsl:template>
+
+    <!--
+       ************************
+       Bloki w tekście głównym
+       ************************
+    -->
+    
+    <xsl:template match="dlugi_cytat">
+        <xsl:param name="mixed" />
+        <div>
+            <xsl:call-template name="standard-attributes" />
+            <xsl:apply-templates select="child::node()">
+                <xsl:with-param name="mixed" select="false()" />
+            </xsl:apply-templates>
+        </div>
+    </xsl:template>
+    
+    <xsl:template match="poezja_cyt">
+        <xsl:param name="mixed" />
+        <div>
+            <xsl:call-template name="standard-attributes" />
+            <xsl:apply-templates select="child::node()">
+                <xsl:with-param name="mixed" select="false()" />
+            </xsl:apply-templates>
+        </div>
+    </xsl:template>
+
+    <xsl:template match="kwestia">
+        <xsl:param name="mixed" />
+        <div>
+            <xsl:call-template name="standard-attributes" />
+            <xsl:apply-templates select="child::node()">
+                <xsl:with-param name="mixed" select="false()" />
+            </xsl:apply-templates>
+        </div>
+    </xsl:template>
+
+    <xsl:template match="didaskalia">
+        <xsl:param name="mixed" />
+        <div x-editable="true">
+            <xsl:call-template name="standard-attributes" />
+            <xsl:apply-templates select="child::node()">
+                <xsl:with-param name="mixed" select="true()" />
+            </xsl:apply-templates>
+        </div>
+    </xsl:template>
+
+    <xsl:template match="wywiad_pyt">
+        <xsl:param name="mixed" />
+        <div>
+            <xsl:call-template name="standard-attributes" />
+            <xsl:apply-templates select="child::node()">
+                <xsl:with-param name="mixed" select="false()" />
+            </xsl:apply-templates>
+        </div>
+    </xsl:template>
+
+    <xsl:template match="wywiad_odp">
+        <xsl:param name="mixed" />
+        <div>
+            <xsl:call-template name="standard-attributes" />
+            <xsl:apply-templates select="child::node()">
+                <xsl:with-param name="mixed" select="false()" />
+            </xsl:apply-templates>
+        </div>
+    </xsl:template>
+
+    <!--
+        ***********************************
+        Style akapitowe oraz strofy i wersy
+        ***********************************
+    -->
+
+    <xsl:template match="akap">
+        <xsl:param name="mixed" />
+        <p x-editable="true">
+            <xsl:call-template name="standard-attributes" />
+            <xsl:call-template name="context-menu" />
+            <xsl:apply-templates select="child::node()">
+                <xsl:with-param name="mixed" select="true()" />
+            </xsl:apply-templates>
+        </p>
+    </xsl:template>
+
+    <xsl:template match="akap_cd">
+        <xsl:param name="mixed" />
+        <p x-editable="true">
+            <xsl:call-template name="standard-attributes" />
+            <xsl:call-template name="context-menu" />
+            <xsl:apply-templates select="child::node()">
+                <xsl:with-param name="mixed" select="true()" />
+            </xsl:apply-templates>
+        </p>
+    </xsl:template>
+
+    <xsl:template match="akap_dialog">
+        <xsl:param name="mixed" />
+        <p x-editable="true">
+            <xsl:call-template name="standard-attributes" />
+            <xsl:call-template name="context-menu" />
+            <xsl:apply-templates select="child::node()">
+                <xsl:with-param name="mixed" select="true()" />
+            </xsl:apply-templates>
+        </p>
+    </xsl:template>
+
+    <!--
+        ********
+        STROFA
+        ********
+    -->
+    <xsl:template match="strofa">
+        <div x-editable="true">
+            <xsl:call-template name="standard-attributes" />
+            <xsl:call-template name="context-menu" />
+         
+            <xsl:choose>
+                <xsl:when test="count(br) > 0">
+                    <xsl:variable name="first-verse" select="br[1]/preceding-sibling::node()" />                    
+                    <xsl:call-template name="verse">
+                        <xsl:with-param name="verse-content" select="$first-verse" />                        
+                    </xsl:call-template>
+                    <xsl:for-each select="br">
+                        <xsl:variable name="lnum" select="count(preceding-sibling::br)" />
+                        <!-- select all nodes up to the next br or end of stanza -->
+                        <xsl:variable name="current-verse"
+                            select="following-sibling::node()[count(preceding-sibling::br) = $lnum+1]" />                        
+                        <xsl:call-template name="verse">
+                            <xsl:with-param name="verse-content" select="$current-verse" />                            
+                        </xsl:call-template>
+                    </xsl:for-each>
+                </xsl:when>
+                <xsl:otherwise>
+                    <xsl:call-template name="verse">
+                        <xsl:with-param name="verse-content" select="child::node()" />                        
+                    </xsl:call-template>
+                </xsl:otherwise>
+            </xsl:choose>
+        </div>
+    </xsl:template>
+
+    <xsl:template name="verse">
+        <!-- the verse contents including the last br (if any) -->
+        <xsl:param name="verse-content" />
+        <xsl:variable name="first-tag-name" select="name($verse-content/self::*)" />
+        <!-- name of text nodes is '' == false -->
+
+        <!-- THIS IS A HORROR!!! -->
+        <!-- Possible variants: -->
+        <xsl:choose>
+            <!-- Simple verse == not wers_ tags anywhere until the ending br -->
+            <xsl:when test="not($verse-content[starts-with(name(), 'wers_')])">
+                <p class="wers" x-node="wers" x-verse="true">
+                <xsl:apply-templates select="$verse-content[local-name(.) != 'br']">
+                    <xsl:with-param name="mixed" select="true()" />
+                </xsl:apply-templates>
+                </p>
+            </xsl:when>
+
+            <xsl:otherwise>
+            <xsl:apply-templates select="$verse-content[local-name(.) != 'br']">
+                <xsl:with-param name="mixed" select="false()" />
+            </xsl:apply-templates>
+            </xsl:otherwise>
+        </xsl:choose>
+    </xsl:template>
+
+    <xsl:template match="wers_cd|wers_akap|wers_wciety|wers_do_prawej">
+        <xsl:param name="mixed" />
+               <!-- <xsl:choose>
+               <xsl:when test="ancestor::*[0]/self::strofa"> -->
+               <p x-verse="true">
+               <xsl:call-template name="standard-attributes" />
+               <xsl:apply-templates select="child::node()">
+                       <xsl:with-param name="mixed" select="true()" />
+               </xsl:apply-templates>
+               </p>
+               <!-- </xsl:when> 
+               <xsl:choose> -->
+    </xsl:template>
+
+    <xsl:template match="br"><xsl:text>/</xsl:text></xsl:template>
+
+
+    <!--
+        *************
+        STYLE ZNAKOWE
+        *************
+    -->
+
+    <!--
+        Tagi obejmujące tytuł dzieła, np. książki, filmu, piosenki,
+        modlitwy, przedstawienia teatr. itd.:
+
+        <tytul_dziela> tytuł-dzieła </tytul_dziela>
+    -->
+    <xsl:template match="tytul_dziela">
+        <xsl:param name="mixed" />
+        <em>
+            <xsl:call-template name="standard-attributes" />
+            <xsl:apply-templates select="child::node()">
+                <xsl:with-param name="mixed" select="true()" />
+            </xsl:apply-templates>
+        </em>
+    </xsl:template>
+
+    <xsl:template match="wyroznienie|slowo_obce|mat|didask_tekst|osoba|wyp_osoba|www|wieksze_odstepy">
+        <xsl:param name="mixed" />
+        <em>
+            <xsl:call-template name="standard-attributes" />
+            <xsl:apply-templates select="child::node()">
+                <xsl:with-param name="mixed" select="true()" />
+            </xsl:apply-templates>
+        </em>
+    </xsl:template>
+
+    <xsl:template match="indeks_dolny">
+        <xsl:param name="mixed" />
+        <sub>
+            <xsl:call-template name="standard-attributes" />
+            <xsl:apply-templates select="child::node()">
+                <xsl:with-param name="mixed" select="true()" />
+            </xsl:apply-templates>
+        </sub>
+    </xsl:template>
+
+    <!--
+        **********
+        SEPARATORY
+        **********
+    -->
+    <xsl:template match="sekcja_swiatlo">
+        <xsl:param name="mixed" />
+        <hr><xsl:call-template name="standard-attributes" /></hr>
+    </xsl:template>
+
+    <xsl:template match="sekcja_asterysk">
+        <xsl:param name="mixed" />
+        <p><xsl:call-template name="standard-attributes" /></p>
+    </xsl:template>
+
+    <xsl:template match="separator_linia">
+        <xsl:param name="mixed" />
+        <hr><xsl:call-template name="standard-attributes" /></hr>
+    </xsl:template>
+
+    <xsl:template match="zastepnik_wersu">
+        <xsl:param name="mixed" />
+        <span>
+            <xsl:call-template name="standard-attributes" />
+            <xsl:apply-templates select="child::node()">
+                <xsl:with-param name="mixed" select="true()" />
+            </xsl:apply-templates>
+        </span>
+    </xsl:template>
+
+    <!--
+        *************
+           PRZYPISY
+        *************
+    -->
+
+    <!--
+        Przypisy i motywy
+    -->
+    <xsl:template match="pr|pa|pe|pt">       
+        <span x-editable="true" x-common="common">
+            <xsl:call-template name="standard-attributes">
+                <xsl:with-param name="extra-class" select="'annotation-inline-box'" />
+            </xsl:call-template>
+            <a name="anchor-{generate-id(.)}" />
+            <!-- the link to the non-inline version -->
+            <a href="#annotation-{generate-id(.)}" class="annotation"></a>
+            <!-- inline contents -->
+            <span x-annotation-box="true" x-pass-thru="true">
+                <xsl:call-template name="context-menu" />
+                <xsl:apply-templates select="node()">
+                    <xsl:with-param name="mixed" select="true()" />
+                </xsl:apply-templates>
+            </span>
+        </span>
+    </xsl:template>
+
+    <xsl:template match="begin">        
+        <span>
+            <xsl:call-template name="standard-attributes" />
+            <xsl:attribute name="theme-class">
+                <xsl:value-of select="substring-after(@id, 'b')" />
+            </xsl:attribute>
+        </span>
+    </xsl:template>
+
+    <xsl:template match="extra|uwaga">
+        <span x-common="common">
+            <xsl:call-template name="standard-attributes" />
+            <xsl:apply-templates select="child::node()">
+                <xsl:with-param name="mixed" select="true()" />
+            </xsl:apply-templates>
+        </span>
+    </xsl:template>
+
+    <xsl:template match="motyw">
+        <span x-editable="true" x-common="common">
+            <xsl:call-template name="standard-attributes" />
+            <xsl:attribute name="theme-class">
+                <xsl:value-of select="substring-after(@id, 'm')" />
+            </xsl:attribute>
+            <xsl:call-template name="context-menu" />
+            <span x-pass-thru="true" class="theme-text-list"><xsl:value-of select=".|node()" /></span>
+        </span>
+    </xsl:template>
+
+    <xsl:template match="end">
+        <span>
+            <xsl:call-template name="standard-attributes" />
+            <xsl:attribute name="theme-class">
+                <xsl:value-of select="substring-after(@id, 'e')" />
+            </xsl:attribute>
+        </span>
+    </xsl:template>
+
+
+    <!--
+        ****************
+         TEKST WŁAŚCIWY
+        ****************
+    -->
+
+    <xsl:template match="text()">
+        <!-- <xsl:value-of select="." /> -->
+        <xsl:param name="mixed" />
+        <xsl:choose>
+            <xsl:when test="normalize-space(.) = ''">
+                <xsl:value-of select="." />
+            </xsl:when>
+            <xsl:when test="not($mixed)">
+               <span x-node="out-of-flow-text" class="out-of-flow-text" x-editable="true">
+                       <xsl:value-of select="." />
+                               </span>
+                       </xsl:when>
+            <xsl:otherwise><xsl:value-of select="." /></xsl:otherwise>
+        </xsl:choose>
+    </xsl:template>
+
+    <xsl:template match="comment()">
+        <xsl:comment><xsl:value-of select="." /></xsl:comment>
+    </xsl:template>
+
+    <xsl:template match="*[name() != local-name()]">
+        <span>
+            <xsl:call-template name="standard-attributes" />
+            <xsl:apply-templates select="child::node()">
+                <xsl:with-param name="mixed" select="true()" />
+            </xsl:apply-templates>
+        </span>
+    </xsl:template>
+       
+       <!-- 
+               Earlier versions of html2wl introduced a BUG, that 'causes "out-of-flow-text" tag to appear.
+               Instead of marking it as "unknown", just pass thru it.
+               Keep a pass-thru span for out-of-flow box editing
+       -->
+    <xsl:template match="out-of-flow-text">
+        <span data-pass-thru="true">
+            <xsl:apply-templates select="child::node()">
+                <xsl:with-param name="mixed" select="false()" />
+            </xsl:apply-templates>        
+        </span>
+    </xsl:template>
+
+    <xsl:template match="*">
+        <span>
+            <xsl:call-template name="standard-attributes">
+                <xsl:with-param name="extra-class">unknown-tag</xsl:with-param>
+            </xsl:call-template>
+            <xsl:apply-templates select="child::node()">
+                <xsl:with-param name="mixed" select="true()" />
+            </xsl:apply-templates>        
+        </span>
+    </xsl:template>
+
+    <xsl:template name="context-menu">
+        <!-- <span class="default-menu context-menu"> -->
+        <!-- <button class="edit-button">Edytuj</button> -->
+            <!-- <span class="delete-button">Delete</span> -->
+        <!-- </span> -->
+        <!-- <span class="edit-menu context-menu">
+            <span class="accept-button">Accept</span>
+            <span class="reject-button">Close</span>
+        </span> -->
+    </xsl:template>
+
+    <xsl:template name="standard-attributes">
+        <xsl:param name="extra-class" />
+        <xsl:attribute name="class"><xsl:value-of select="local-name()" /><xsl:text>&#x0020;</xsl:text><xsl:value-of select="$extra-class" /></xsl:attribute>
+
+        <xsl:attribute name="x-node"><xsl:value-of select="local-name()" /></xsl:attribute>
+
+        <xsl:if test="local-name() != name()">
+            <xsl:attribute name="x-ns"><xsl:value-of select="namespace-uri()" /></xsl:attribute>
+        </xsl:if>
+
+        <xsl:for-each select="@*">
+            <xsl:variable name="id" select="generate-id()" />
+            <xsl:attribute name="x-attr-value-{$id}"><xsl:value-of select="."/></xsl:attribute>
+            <xsl:attribute name="x-attr-name-{$id}"><xsl:value-of select="local-name()"/></xsl:attribute>
+            <xsl:choose>
+               <xsl:when test="namespace-uri()">
+                <xsl:attribute name="x-attr-ns-{$id}"><xsl:value-of select="namespace-uri()"/></xsl:attribute>
+                               </xsl:when>
+                               <!-- if the element belongs to default namespace and attribut has no namespace -->
+               <xsl:when test="not(namespace-uri(.))">
+                               <xsl:attribute name="data-wlf-{local-name()}"><xsl:value-of select="."/></xsl:attribute>
+                               </xsl:when>
+                       </xsl:choose>               
+        </xsl:for-each>
+    </xsl:template>
+
+    <xsl:template match="alien">
+            <span class="alien" x-pass-thru="true">
+                <xsl:apply-templates select="node()">
+                    <xsl:with-param name="mixed" select="true()" />
+                </xsl:apply-templates>
+            </span>
+    </xsl:template>
+
+    <xsl:template match="comment()">
+        <xsl:comment><xsl:value-of select="."/></xsl:comment>
+    </xsl:template>
+</xsl:stylesheet>
diff --git a/src/redakcja/templates/404.html b/src/redakcja/templates/404.html
new file mode 100644 (file)
index 0000000..11edc9d
--- /dev/null
@@ -0,0 +1,29 @@
+{% extends "catalogue/base.html" %}
+{% load i18n %}
+
+{% block titleextra %}{% trans "Page not found" %}{% endblock %}
+
+{% block content %}
+
+<h1>{% trans "Page not found" %}</h1>
+
+{% blocktrans with p=request.build_absolute_uri %}The page you're trying
+to reach (<tt>{{p}}</tt>) could not be found. If it's a document, try
+looking for it on the <a href="/">document list</a>.{% endblocktrans %}
+
+<p>
+{% blocktrans with m="mailto:radoslaw.czajka@nowoczesnapolska.org.pl" %}If you
+still can't find what you're looking for, please
+<a href="{{m}}">contact the administrator</a>.{% endblocktrans %}
+</p>
+
+{% url "catalogue_user" as m %}
+<p>
+{% blocktrans %}If you're coming from Redmine, please note that
+work is no longer managed there. 
+Go to the <a href="/">document list</a>
+or to <a href="{{m}}">your page</a> instead.{% endblocktrans %}
+</p>
+
+
+{% endblock content %}
diff --git a/src/redakcja/templates/500.html b/src/redakcja/templates/500.html
new file mode 100644 (file)
index 0000000..bd7404f
--- /dev/null
@@ -0,0 +1,13 @@
+{% extends "error_base.html" %}
+
+{% block "content" %}
+
+<h1>Błąd po stronie serwera.</h1>
+
+<p>Niestety nasz serwer WWW nie był w stanie dostarczyć Ci strony o którą prosisz.</p>
+<p><b>Serdecznie przepraszamy.</b></p>
+<p>Administrator został już powiadomiony o błędzie, ale jeśli chciałbyś
+przekazać nam więcej informacji na temat błędu, napisz na <a href="mailto:radoslaw.czajka@nowoczesnapolska.org.pl">nasz adres</a>.</p>
+</div>
+
+{% endblock %}
diff --git a/src/redakcja/templates/503.html b/src/redakcja/templates/503.html
new file mode 100644 (file)
index 0000000..1c1d39a
--- /dev/null
@@ -0,0 +1,14 @@
+{% extends "error_base.html" %}
+
+{% block "content" %}
+
+<h1>Serwis tymczasowo niedostępny</h1>
+
+<p>
+Platfroma redakcyjna serwisu <a href="http://wolnelektury.pl/">Wolne Lektury</a> jest
+tymczasowo niedostępna z powodu prac administracyjnych.
+</p>
+
+<p>Prosimy o wyrozumiałość i ponowne odwiedziny.</p>
+
+{% endblock "content" %}
diff --git a/src/redakcja/templates/base.html b/src/redakcja/templates/base.html
new file mode 100644 (file)
index 0000000..fc60995
--- /dev/null
@@ -0,0 +1,28 @@
+<!DOCTYPE html >
+{% load i18n %}
+<html>
+    <head lang="{{ LANGUAGE_CODE }}">
+        <meta charset="utf-8" />
+        <link rel="icon" href="{{ STATIC_URL }}img/pr-icon.png" type="image/png" />
+        <title>{% block title %}{% block titleextra %}{% endblock titleextra %} ::
+            {% trans "Platforma Redakcyjna" %}{% endblock title %}</title>
+        {% block extrahead %}
+        {% endblock %}
+    </head>
+    <body id="{% block bodyid %}base{% endblock %}">
+
+    <div id="loading-overlay">
+       <div id="loading-message">
+               <img src="{{STATIC_URL}}img/spinner.gif" alt="Loading" />
+               <p>{% trans "Loading" %}</p>
+       </div>
+       </div>
+
+       <div id="body-wrap">
+               <!-- version: {{ APP_VERSION }} -->
+        <div id="content">{% block maincontent %} {% endblock %}</div>
+        </div>
+    <script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js"></script>
+    {% block extrabody %}{% endblock %}
+    </body>
+</html>
diff --git a/src/redakcja/templates/error_base.html b/src/redakcja/templates/error_base.html
new file mode 100755 (executable)
index 0000000..58784dc
--- /dev/null
@@ -0,0 +1,66 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
+    <style type="text/css">
+
+body {
+    margin: 0;
+    font-family: verdana, sans-serif;
+    font-size: 10px;
+}
+
+img {
+    border: 0;
+}
+
+.clr {
+    clear: both;
+}
+
+#tabs-nav {
+    padding: 5px 5px 0 10px;
+    background: #ffdfbf;
+    border-bottom: 1px solid #ff8000;
+    position: relative;
+    height: 25px;
+}
+
+#logo {
+    position: absolute;
+    bottom: 0;
+}
+
+#content {
+    padding: 10px;
+}
+
+a, a:visited, a:active {
+       color: #bf6000;
+       text-decoration: none;
+}
+
+a:hover {
+       text-decoration: underline;
+}
+
+    </style>
+    <title>Platforma Redakcyjna</title>
+</head>
+<body>
+
+<div id="tabs-nav">
+
+<img id="logo" src='data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAC8AAAAcCAYAAADxyeavAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAD7AAAA+wBSobVeQAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAATYSURBVFiFvdhbjN1VFcfxzzpTR8tUYisJViJWJCCIVKUNYCGdaRPUMBYvqNFSYkyol4iNsalpaOfPf4bLKBiR8kTQCCG0tWMBa4WEgLY0lJBga61FiGKRahPqgxamUGjP8uF/Mr0wlzMHpzvZL2ft9Vvfvfb6r71zIjOd8FHGe/GgMKAnf9iqTO3/iNTcuDHeh02YJS1+K1InFr6MGQ7ZjDbcgDOUcW6rcicOvowzVBk/bJK52t2Ow+huVfLEwB8NTqfr8kXLcx+24DOtyk48fBkn4zeom6RLkXuGbOF+XOzmeHcr0hMLX0YN92EqulyXLxwX/WG0eV1XK/ITnfmb8Ul8UZHPv8m6Ip/FP2kNftJbQhttlPElLMMSRW4ZZeWjmNdKiInJfBkz8HPco8jbx1j9KD6kjPeMN8zEwIcL0GGy7zSx+rGGz7hLpzX4MmrKmDaK6g5w0IwxtcLZqGNkvRHDtDYW43llnDes9bC/4YC6D4+q0hcfkX6Fp53kF+OFaA3+7dbi39jgpjj1TfYi69jJKPA3xmnqNuIl7S63NAeVcW7je2lqjN5tyngXOjG/MUPNVVbm08pYgC3esFUZCxS58zjvHRj+3fKTmOyQDWjXZq7luU9vLMRduAffaAb+uMxH6IvZyuhTxlOq7N6PL2M76uoeU0anIndpMxuv4AlldB4rZbuRMr/fz3A2uq3Iv+uNa6R78XuTLW0GHCKv1y50SVdgAU7Da3hceER6ROGPZOqPqQ7aiI/hK4p8wK3RYdAGXIwvKPK3oIxZeNI0Ha7Ng0MRy1imurw+p8hf64sL1W3GeixS5KHxwG/DR/Gy6g2yXoeHLM3BYT3KOAkDqptziSLvaPy2EXOEhXpynTvjbfbajwsVuaPh+6nGuu8r8jZlnKV6sL2ATkW+1iw4Vdk8XG3DckV+VZEDI4JDkQdMd4WqPlcp4xaFV3XoxlZptTK6Lc438AfRKJ0yTsG9WK/I29wQH1T1+P1YMF5wiMykjDvwLeEqPbm6ae8yvo2fYgO+psNhgx5SldWlWIQDilypjLsxBx9X9fTNCMxR5D/GC34EXoTSXbhaVcsDTSv0xVx1A/gvrsRf8Tu0q1ms7gPYg8cxGy+pSmUKLlXkX1oBZ6jbZOIarMFqZVzZtMLK3GSSWXgRW/FdYQ3OV7dXkWtU38duHFKVSjvmjwhexjvcFNPHCh3H/HuwLtrsch8+j4WK/GXTm6iCfh23qt7vT5hmnmvzYOOFubax6k/oHiqVMk4RZkszMRPnq9pooF9hRSO5Y8BXYjX8GEuEUo/ekZxH2EA7zsJzinx96Pfe6JImq9krfUK6CBfhTNWJPIc/q27mncL7pVvwoKqFHhgb/gjEN7FK9TFerchXjrNPUd2g56kuo2qGpXpyreFG1T43YV8DsJp1zx6z0SMxLlOV8m5VR9pztHlkeOiN+dI67BZWSeccBXo6ErvwpGjMulOFmdJaRf7rKJB5+I/CtnGe5Jmq7E/FZxX5VHPwR5wHcI7qWLcPzcm2WZYvN9a1q3r/IgziGUXObiThB1J/Q3GLNoutyGea3sCP4p1etREX4NOK3NwcfAVWM11b4+IZzn4yHsAlqnZ7Or6nyOkN+wZMUbNMXT8uEa7Xo7/pU6ieIdUGai6zMrf+DykEtvGDDlIUAAAAAElFTkSuQmCC' />
+
+    <div class='clr' ></div>
+</div>
+
+<div id="content">
+{% block "content" %}
+{% endblock %}
+</div>
+
+
+</body>
+</html>
diff --git a/src/redakcja/templates/pagination/pagination.html b/src/redakcja/templates/pagination/pagination.html
new file mode 100755 (executable)
index 0000000..fe566a8
--- /dev/null
@@ -0,0 +1,26 @@
+{% if is_paginated %}
+{% load i18n %}
+<div class="pagination">
+    {% if page_obj.has_previous %}
+        <a href="?page={{ page_obj.previous_page_number }}{{ getvars }}{{ hashtag }}" class="prev">&lsaquo;&lsaquo; {% trans "previous" %}</a>
+    {% else %}
+        <span class="disabled prev">&lsaquo;&lsaquo; {% trans "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 }}{{ hashtag }}" class="page">{{ page }}</a>
+            {% endifequal %}
+        {% else %}
+            ...
+        {% endif %}
+    {% endfor %}
+    {% if page_obj.has_next %}
+        <a href="?page={{ page_obj.next_page_number }}{{ getvars }}{{ hashtag }}" class="next">{% trans "next" %} &rsaquo;&rsaquo;</a>
+    {% else %}
+        <span class="disabled next">{% trans "next" %} &rsaquo;&rsaquo;</span>
+    {% endif %}
+</div>
+{% endif %}
diff --git a/src/redakcja/templates/registration/head_login.html b/src/redakcja/templates/registration/head_login.html
new file mode 100644 (file)
index 0000000..2a8fd3b
--- /dev/null
@@ -0,0 +1,16 @@
+{% load i18n %}
+
+{% if user.is_authenticated %}
+<span class="user_name">{{ user.username }}</span> |
+
+{% if user.is_staff %}
+    <a href="{% url 'admin:index' %}">{% trans "Admin" %}</a> |
+{% endif %}
+
+<a href='{% url "logout" %}{% if logout_to %}?next={{ logout_to }}{% endif %}'>{% trans "Log Out" %}</a>
+{% else %}
+{% url "login" as login_url %}
+{% ifnotequal login_url request.path %}
+    <a href='{{ login_url }}'>{% trans "Log In" %}</a>
+{% endifnotequal %}
+{% endif %}
diff --git a/src/redakcja/templates/registration/login.html b/src/redakcja/templates/registration/login.html
new file mode 100644 (file)
index 0000000..adbef3c
--- /dev/null
@@ -0,0 +1,17 @@
+{% extends "catalogue/base.html" %}
+
+{% block titleextra %}Logowanie{% endblock %}
+{% block subtitle %} - Logowanie {% endblock subtitle %}
+
+{% block content %}
+
+<div class="isection">
+<form method="POST" action="{% url 'login' %}">
+{% csrf_token %}
+{{ form.as_p }}
+<p><input type="submit" value="Login" /></p>
+<input type="hidden" name="next" value="{{ next|urlencode }}" />
+</form>
+</div>
+
+{% endblock content %}
diff --git a/src/redakcja/urls.py b/src/redakcja/urls.py
new file mode 100644 (file)
index 0000000..c0629fa
--- /dev/null
@@ -0,0 +1,36 @@
+# -*- coding: utf-8 -*-
+
+from django.conf.urls import include, patterns, url
+from django.contrib import admin
+from django.conf import settings
+from django.conf.urls.static import static
+from django.contrib.staticfiles.urls import staticfiles_urlpatterns
+from django.views.generic import RedirectView
+
+
+admin.autodiscover()
+
+urlpatterns = patterns('',
+    # Auth
+    url(r'^accounts/login/$', 'django_cas.views.login', name='login'),
+    url(r'^accounts/logout/$', 'django_cas.views.logout', name='logout'),
+    url(r'^admin/login/$', 'django_cas.views.login', name='login'),
+    url(r'^admin/logout/$', 'django_cas.views.logout', name='logout'),
+
+    # Admin panel
+    url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
+    (r'^admin/', include(admin.site.urls)),
+
+    (r'^comments/', include('django.contrib.comments.urls')),
+
+    url(r'^$', RedirectView.as_view(url= '/documents/')),
+    url(r'^documents/', include('catalogue.urls')),
+    url(r'^apiclient/', include('apiclient.urls')),
+    url(r'^editor/', include('wiki.urls')),
+    url(r'^images/', include('wiki_img.urls')),
+    url(r'^cover/', include('cover.urls')),
+)
+
+if settings.DEBUG:
+    urlpatterns += staticfiles_urlpatterns()
+    urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
diff --git a/src/redakcja/wsgi.py b/src/redakcja/wsgi.py
new file mode 100755 (executable)
index 0000000..b0097e6
--- /dev/null
@@ -0,0 +1,9 @@
+import os
+
+
+os.environ.setdefault("DJANGO_SETTINGS_MODULE", "redakcja.settings")
+
+# This application object is used by the development server
+# as well as any WSGI server configured to use this file.
+from django.core.wsgi import get_wsgi_application
+application = get_wsgi_application()
diff --git a/src/toolbar/__init__.py b/src/toolbar/__init__.py
new file mode 100644 (file)
index 0000000..c53f0e7
--- /dev/null
@@ -0,0 +1 @@
+  # pragma: no cover
diff --git a/src/toolbar/admin.py b/src/toolbar/admin.py
new file mode 100644 (file)
index 0000000..654480c
--- /dev/null
@@ -0,0 +1,31 @@
+from django.contrib import admin
+from django.utils.translation import ugettext_lazy as _
+from django import forms
+import json
+
+from toolbar import models
+
+
+class ButtonAdminForm(forms.ModelForm):
+    class Meta:
+        model = models.Button
+        exclude = []
+
+    def clean_params(self):
+        value = self.cleaned_data['params']
+        try:
+            return json.dumps(json.loads(value))
+        except ValueError, e:
+            raise forms.ValidationError(e)
+
+
+class ButtonAdmin(admin.ModelAdmin):
+    form = ButtonAdminForm
+    list_display = ('slug', 'label', 'tooltip', 'accesskey')
+    list_display_links = ('slug',)
+    list_editable = ('label', 'tooltip', 'accesskey')
+    prepopulated_fields = {'slug': ('label',)}
+
+admin.site.register(models.Button, ButtonAdmin)
+admin.site.register(models.ButtonGroup)
+admin.site.register(models.Scriptlet)
diff --git a/src/toolbar/fixtures/initial_toolbar.yaml b/src/toolbar/fixtures/initial_toolbar.yaml
new file mode 100644 (file)
index 0000000..be8c339
--- /dev/null
@@ -0,0 +1,1067 @@
+-   fields: {name: "Nag\u0142\xF3wki", position: 0, slug: naglowki}
+    model: toolbar.buttongroup
+    pk: 1
+-   fields: {name: Autokorekta, position: 0, slug: autokorekta}
+    model: toolbar.buttongroup
+    pk: 2
+-   fields: {name: Mastery, position: 0, slug: mastery}
+    model: toolbar.buttongroup
+    pk: 11
+-   fields: {name: 'Dramat ', position: 0, slug: dramat}
+    model: toolbar.buttongroup
+    pk: 12
+-   fields: {name: "Elementy pocz\u0105tkowe", position: 0, slug: elementy-poczatkowe}
+    model: toolbar.buttongroup
+    pk: 13
+-   fields: {name: Akapity, position: 0, slug: akapity}
+    model: toolbar.buttongroup
+    pk: 14
+-   fields: {name: Style znakowe, position: 0, slug: style-znakowe}
+    model: toolbar.buttongroup
+    pk: 15
+-   fields: {name: Separatory, position: 0, slug: separatory}
+    model: toolbar.buttongroup
+    pk: 16
+-   fields: {name: Wersy, position: 0, slug: wersy}
+    model: toolbar.buttongroup
+    pk: 17
+-   fields: {name: Bloki, position: 0, slug: bloki}
+    model: toolbar.buttongroup
+    pk: 21
+-   fields: {name: "Pocz\u0105tek dramatu", position: 0, slug: poczatek-dramatu}
+    model: toolbar.buttongroup
+    pk: 22
+-   fields: {name: Przypisy, position: 0, slug: przypisy}
+    model: toolbar.buttongroup
+    pk: 26
+-   fields: {name: Autotagowanie, position: 0, slug: autotagowanie}
+    model: toolbar.buttongroup
+    pk: 28
+-   fields: {name: Uwaga, position: 0, slug: uwaga}
+    model: toolbar.buttongroup
+    pk: 29
+-   fields:
+        accesskey: ''
+        group: [2]
+        label: ",,\u2026\" na \xAB\u2026\xBB"
+        link: ''
+        params: '{"exprs": [[",,", "\u00ab"], ["\"", "\u00bb"]]}'
+        scriptlet: fulltextregexp
+        slug: na-francuskie
+        tooltip: "Zamienia cudzys\u0142owy podw\xF3jne na francuskie"
+    model: toolbar.button
+    pk: 2
+-   fields:
+        accesskey: ''
+        group: [2]
+        label: ",,\u2026\" na \xBB\u2026\xAB"
+        link: ''
+        params: '{"exprs": [[",,", "\u00bb"], ["\"", "\u00ab"]]}'
+        scriptlet: fulltextregexp
+        slug: na-niemieckie
+        tooltip: "Zamienia cudzys\u0142owy podw\xF3jne na niemieckie"
+    model: toolbar.button
+    pk: 3
+-   fields:
+        accesskey: ''
+        group: [2]
+        label: Podstawowa
+        link: ''
+        params: '[["fulltextregexp", {"exprs": [["\ufeff", ""], ["$[\\s]*\\d+[\\s]*^",
+            ""], ["-\\s*^", ""], ["\\,\\.\\.|\\.\\,\\.|\\.\\.\\,", "..."], ["<(/?)P([aert])",
+            "<$1p$2"], ["[\u2014\u2013\u2010-]{2,}|[\u2014\u2013\u2010]+", "---"],
+            ["(\\s)-([^-])", "$1---$2"], ["([^-])-(\\s)", "$1---$2"], ["(\\d)-+(\\d)",
+            "$1--$2"], ["---(\\S)", "--- $1"], ["(\\S)---", "$1 ---"], ["<akap_dialog>\\s*-+\\s*",
+            "<akap_dialog>--- "]]}], ["lineregexp", {"exprs": [["^\\s+|\\s+$", ""],
+            ["\\s+", " "], ["(,,)\\s+", "$1"], ["\\s+(\")", "$1"], ["([^\\.])(\\s*)</p",
+            "$1.$2</p"], ["([\\.:;!\\?])([^\\s\\\\])", "$1 $2"], ["([^\\s])\\s+([\\.:;!\\?])",
+            "$1$2"], ["\\s+,([^,])", ",$1"], ["([^,]),([^\\s\\\\,])", "$1, $2"], ["([A-Za-z])0",
+            "$1o"], ["(\\. )0([A-Za-z])", "$1O$2"], [" 0([A-Za-z])", " o$1"], ["([A-Za-z])1",
+            "$1l"], ["(\\. )1([A-Za-z])", "$1L$2"], [" 1([A-Za-z])", " l$1"]]}]]'
+        scriptlet: macro
+        slug: basic_correction
+        tooltip: "Wykonuj\u0119 podstawow\u0105 korekt\u0119 tekstu."
+    model: toolbar.button
+    pk: 4
+-   fields:
+        accesskey: ''
+        group: [1]
+        label: "cz\u0119\u015B\u0107/ksi\u0119ga"
+        link: ''
+        params: '{"padding_top": 1, "padding_bottom": 4, "tag": "naglowek_czesc"}'
+        scriptlet: insert_tag
+        slug: czesc
+        tooltip: ''
+    model: toolbar.button
+    pk: 10
+-   fields:
+        accesskey: ''
+        group: [1]
+        label: "rozdzia\u0142"
+        link: ''
+        params: '{"padding_top": 1, "padding_bottom": 4, "tag": "naglowek_rozdzial"}'
+        scriptlet: insert_tag
+        slug: rozdzial
+        tooltip: ''
+    model: toolbar.button
+    pk: 11
+-   fields:
+        accesskey: ''
+        group: [1]
+        label: "podrozdzia\u0142"
+        link: ''
+        params: '{"tag": "naglowek_podrozdzial"}'
+        scriptlet: insert_tag
+        slug: podrozdzial
+        tooltip: ''
+    model: toolbar.button
+    pk: 12
+-   fields:
+        accesskey: ''
+        group: [1]
+        label: "\u015Br\xF3dtytu\u0142"
+        link: ''
+        params: '{"tag": "srodtytul"}'
+        scriptlet: insert_tag
+        slug: srodtytul
+        tooltip: ''
+    model: toolbar.button
+    pk: 13
+-   fields:
+        accesskey: ''
+        group: [1]
+        label: akt
+        link: ''
+        params: '{"padding_top": 1, "padding_bottom": 4, "tag": "naglowek_akt"}'
+        scriptlet: insert_tag
+        slug: akt
+        tooltip: ''
+    model: toolbar.button
+    pk: 14
+-   fields:
+        accesskey: ''
+        group: [1]
+        label: scena
+        link: ''
+        params: '{"tag": "naglowek_scena"}'
+        scriptlet: insert_tag
+        slug: scena
+        tooltip: ''
+    model: toolbar.button
+    pk: 15
+-   fields:
+        accesskey: ''
+        group: [12]
+        label: "nag\u0142\xF3wek kwestii"
+        link: ''
+        params: '{"padding_top": 1, "padding_bottom": 4, "tag": "naglowek_osoba"}'
+        scriptlet: insert_tag
+        slug: naglowek-kwestii
+        tooltip: "nag\u0142\xF3wek kwestii - nazwa osoby"
+    model: toolbar.button
+    pk: 16
+-   fields:
+        accesskey: ''
+        group: [11]
+        label: "tag g\u0142\xF3wny"
+        link: ''
+        params: '{"tag": "utwor"}'
+        scriptlet: insert_tag
+        slug: tag-glowny
+        tooltip: ''
+    model: toolbar.button
+    pk: 17
+-   fields:
+        accesskey: ''
+        group: [11]
+        label: opowiadanie
+        link: ''
+        params: '{"padding_top": 1, "padding_bottom": 4, "tag": "opowiadanie"}'
+        scriptlet: insert_tag
+        slug: opowiadanie
+        tooltip: ''
+    model: toolbar.button
+    pk: 18
+-   fields:
+        accesskey: ''
+        group: [11]
+        label: "powie\u015B\u0107"
+        link: ''
+        params: '{"padding_top": 1, "padding_bottom": 4, "tag": "powiesc"}'
+        scriptlet: insert_tag
+        slug: powiesc
+        tooltip: ''
+    model: toolbar.button
+    pk: 19
+-   fields:
+        accesskey: ''
+        group: [11]
+        label: dramat wiersz.
+        link: ''
+        params: '{"padding_top": 1, "padding_bottom": 4, "tag": "dramat_wierszowany_l"}'
+        scriptlet: insert_tag
+        slug: dramat-wiersz
+        tooltip: ''
+    model: toolbar.button
+    pk: 20
+-   fields:
+        accesskey: ''
+        group: [11]
+        label: "dramat wsp\xF3\u0142czesny"
+        link: ''
+        params: '{"padding_top": 1, "padding_bottom": 4, "tag": "dramat_wspolczesny"}'
+        scriptlet: insert_tag
+        slug: dramat-wspolczesny
+        tooltip: "dramat wsp\xF3\u0142czesny (proz\u0105)"
+    model: toolbar.button
+    pk: 21
+-   fields:
+        accesskey: ''
+        group: [11]
+        label: "dramat wiersz./w. \u0142am"
+        link: ''
+        params: '{"padding_top": 1, "padding_bottom": 4, "tag": "dramat_wierszowany_lp"}'
+        scriptlet: insert_tag
+        slug: dramat-wiersz-w-lam
+        tooltip: "dramat wierszowany o zw\u0119\u017Conej szeroko\u015Bci \u0142amu"
+    model: toolbar.button
+    pk: 22
+-   fields:
+        accesskey: ''
+        group: [11]
+        label: liryka
+        link: ''
+        params: '{"padding_top": 1, "padding_bottom": 4, "tag": "liryka_l"}'
+        scriptlet: insert_tag
+        slug: liryka
+        tooltip: ''
+    model: toolbar.button
+    pk: 23
+-   fields:
+        accesskey: ''
+        group: [11]
+        label: "liryka/w. \u0142am"
+        link: ''
+        params: '{"padding_top": 1, "padding_bottom": 4, "tag": "liryka_lp"}'
+        scriptlet: insert_tag
+        slug: liryka-w-lam
+        tooltip: "utw\xF3r liryczny o zw\u0119\u017Conej szeroko\u015Bci \u0142amu"
+    model: toolbar.button
+    pk: 24
+-   fields:
+        accesskey: ''
+        group: [11]
+        label: wywiad
+        link: ''
+        params: '{"padding_top": 1, "padding_bottom": 4, "tag": "wywiad"}'
+        scriptlet: insert_tag
+        slug: wywiad
+        tooltip: ''
+    model: toolbar.button
+    pk: 25
+-   fields:
+        accesskey: ''
+        group: [13]
+        label: autor
+        link: ''
+        params: '{"padding_top": 1, "padding_bottom": 2, "tag": "autor_utworu"}'
+        scriptlet: insert_tag
+        slug: autor
+        tooltip: ''
+    model: toolbar.button
+    pk: 32
+-   fields:
+        accesskey: ''
+        group: [13]
+        label: nazwa utworu
+        link: ''
+        params: '{"padding_top": 1, "padding_bottom": 2, "tag": "nazwa_utworu"}'
+        scriptlet: insert_tag
+        slug: nazwa-utworu
+        tooltip: ''
+    model: toolbar.button
+    pk: 33
+-   fields:
+        accesskey: ''
+        group: [1]
+        label: "podtytu\u0142"
+        link: ''
+        params: '{"tag": "podtytul"}'
+        scriptlet: insert_tag
+        slug: podtytul
+        tooltip: ''
+    model: toolbar.button
+    pk: 34
+-   fields:
+        accesskey: v
+        group: [13]
+        label: nota
+        link: ''
+        params: '{"tag": "nota"}'
+        scriptlet: insert_tag
+        slug: nota
+        tooltip: ''
+    model: toolbar.button
+    pk: 35
+-   fields:
+        accesskey: ''
+        group: [13, 22]
+        label: motto podpis
+        link: ''
+        params: '{"padding_top": 1, "padding_bottom": 2, "tag": "motto_podpis"}'
+        scriptlet: insert_tag
+        slug: motto-podpis
+        tooltip: ''
+    model: toolbar.button
+    pk: 37
+-   fields:
+        accesskey: ''
+        group: [13]
+        label: "dzie\u0142o nadrz\u0119dne"
+        link: ''
+        params: '{"padding_top": 1, "padding_bottom": 2, "tag": "dzielo_nadrzedne"}'
+        scriptlet: insert_tag
+        slug: dzielo-nadrzedne
+        tooltip: ''
+    model: toolbar.button
+    pk: 38
+-   fields:
+        accesskey: a
+        group: [14, 12]
+        label: akapit
+        link: ''
+        params: '{"padding_top": 1, "padding_bottom": 3, "tag": "akap"}'
+        scriptlet: insert_tag
+        slug: akapit
+        tooltip: wstawia akapit
+    model: toolbar.button
+    pk: 39
+-   fields:
+        accesskey: ''
+        group: [14]
+        label: akapit cd.
+        link: ''
+        params: '{"padding_top": 1, "padding_bottom": 3, "tag": "akap_cd"}'
+        scriptlet: insert_tag
+        slug: akapit-cd
+        tooltip: "ci\u0105g dalszy akapitu po wewn\u0105trzakapitowym wtr\u0105ceniu"
+    model: toolbar.button
+    pk: 40
+-   fields:
+        accesskey: d
+        group: [14]
+        label: akapit dialogowy
+        link: ''
+        params: '{"padding_top": 1, "padding_bottom": 3, "tag": "akap_dialog"}'
+        scriptlet: insert_tag
+        slug: akapit-dialogowy
+        tooltip: wstawia akapit dialogowy
+    model: toolbar.button
+    pk: 41
+-   fields:
+        accesskey: ''
+        group: [21]
+        label: "d\u0142ugi cytat"
+        link: ''
+        params: '{"tag": "dlugi_cytat"}'
+        scriptlet: insert_tag
+        slug: dlugi-cytat
+        tooltip: "d\u0142ugi cytat wyr\xF3\u017Cniony sk\u0142adem"
+    model: toolbar.button
+    pk: 42
+-   fields:
+        accesskey: ''
+        group: [21]
+        label: "ramka"
+        link: ''
+        params: '{"tag": "ramka"}'
+        scriptlet: insert_tag
+        slug: ramka
+        tooltip: "tekst otoczony ramką"
+    model: toolbar.button
+    pk: 43
+-   fields:
+        accesskey: f
+        group: [12, 15]
+        label: "wyr\xF3\u017Cnienie"
+        link: ''
+        params: '{"tag": "wyroznienie"}'
+        scriptlet: insert_tag
+        slug: wyroznienie
+        tooltip: "wyr\xF3\u017Cnienie autorskie"
+    model: toolbar.button
+    pk: 44
+-   fields:
+        accesskey: ''
+        group: [15]
+        label: "tytu\u0142 dzie\u0142a typ 1"
+        link: ''
+        params: '{"tag": "tytul_dziela", "attrs": {"typ": "1"}}'
+        scriptlet: insert_tag
+        slug: tytul-dziela-typ
+        tooltip: "tytu\u0142 dzie\u0142a w cytowanym tytule dzie\u0142a"
+    model: toolbar.button
+    pk: 45
+-   fields:
+        accesskey: e
+        group: [15]
+        label: "s\u0142owo obce"
+        link: ''
+        params: '{"tag": "slowo_obce"}'
+        scriptlet: insert_tag
+        slug: slowo-obce
+        tooltip: "frazy w j\u0119zykach innych ni\u017C polski/definiendum w przypisie"
+    model: toolbar.button
+    pk: 46
+-   fields:
+        accesskey: ''
+        group: [15]
+        label: matemat.
+        link: ''
+        params: '{"tag": "mat"}'
+        scriptlet: insert_tag
+        slug: matemat
+        tooltip: "wyra\u017Cenia matematyczne lub zmienne symboliczne"
+    model: toolbar.button
+    pk: 47
+-   fields:
+        accesskey: ''
+        group: [15]
+        label: www
+        link: ''
+        params: '{"tag": "www"}'
+        scriptlet: insert_tag
+        slug: www
+        tooltip: ''
+    model: toolbar.button
+    pk: 48
+-   fields:
+        accesskey: ''
+        group: [15]
+        label: indeks dolny
+        link: ''
+        params: '{"tag": "indeks_dolny"}'
+        scriptlet: insert_tag
+        slug: indeks-dolny
+        tooltip: ''
+    model: toolbar.button
+    pk: 49
+-   fields:
+        accesskey: ''
+        group: [15]
+        label: "wi\u0119ksze odst\u0119py"
+        link: ''
+        params: '{"tag": "wieksze_odstepy"}'
+        scriptlet: insert_tag
+        slug: wieksze-odstepy
+        tooltip: "wi\u0119ksze odst\u0119py mi\u0119dzy s\u0142owami"
+    model: toolbar.button
+    pk: 50
+-   fields:
+        accesskey: ''
+        group: [29]
+        label: uwaga
+        link: ''
+        params: '{"tag": "uwaga"}'
+        scriptlet: insert_tag
+        slug: uwaga
+        tooltip: 'uwagi redaktorsko-korektorskie '
+    model: toolbar.button
+    pk: 51
+-   fields:
+        accesskey: ''
+        group: [16]
+        label: "\u015Bwiat\u0142o"
+        link: ''
+        params: '{"nocontent": "true", "tag": "sekcja_swiatlo"}'
+        scriptlet: insert_tag
+        slug: sep-swiatlo
+        tooltip: "\u015Bwiat\u0142o rozdzielaj\u0105ce sekcje tekstu"
+    model: toolbar.button
+    pk: 53
+-   fields:
+        accesskey: ''
+        group: [16]
+        label: asterysk
+        link: ''
+        params: '{"nocontent": "true", "tag": "sekcja_asterysk"}'
+        scriptlet: insert_tag
+        slug: sep-asterysk
+        tooltip: rozdzielenie partii tekstu asteryskiem
+    model: toolbar.button
+    pk: 54
+-   fields:
+        accesskey: ''
+        group: [16]
+        label: linia
+        link: ''
+        params: '{"nocontent": "true", "tag": "separator_linia"}'
+        scriptlet: insert_tag
+        slug: sep-linia
+        tooltip: "rozdzielenie partii tekstu pozioma lini\u0105"
+    model: toolbar.button
+    pk: 55
+-   fields:
+        accesskey: ''
+        group: [16]
+        label: "zast\u0119pnik wersu"
+        link: ''
+        params: '{"tag": "zastepnik_wersu"}'
+        scriptlet: insert_tag
+        slug: zastepnik-wersu
+        tooltip: wykropkowanie wersu
+    model: toolbar.button
+    pk: 56
+-   fields:
+        accesskey: ''
+        group: [12]
+        label: didaskalia
+        link: ''
+        params: '{"padding_top": 1, "padding_bottom": 2, "tag": "didaskalia"}'
+        scriptlet: insert_tag
+        slug: didaskalia
+        tooltip: ''
+    model: toolbar.button
+    pk: 62
+-   fields:
+        accesskey: ''
+        group: [12]
+        label: didaskalia tekstowe
+        link: ''
+        params: '{"tag": "didask_tekst"}'
+        scriptlet: insert_tag
+        slug: didaskalia-tekstowe
+        tooltip: "didaskalia umieszczone w obr\u0119bie innego tekstu"
+    model: toolbar.button
+    pk: 63
+-   fields:
+        accesskey: b
+        group: [12]
+        label: osoba
+        link: ''
+        params: '{"tag": "osoba"}'
+        scriptlet: insert_tag
+        slug: osoba
+        tooltip: "wstawia nazw\u0119 osoby w didaskaliach"
+    model: toolbar.button
+    pk: 64
+-   fields:
+        accesskey: ''
+        group: [21]
+        label: "d\u0142ugi cyt. poet."
+        link: ''
+        params: '{"tag": "poezja_cyt"}'
+        scriptlet: insert_tag
+        slug: dlugi-cyt-poet
+        tooltip: "d\u0142ugi cytat wierszowany wyr\xF3\u017Cniony sk\u0142adem"
+    model: toolbar.button
+    pk: 67
+-   fields:
+        accesskey: ''
+        group: [26]
+        label: przypis autorski
+        link: ''
+        params: '{"tag": "pa"}'
+        scriptlet: insert_tag
+        slug: przypis-autorski
+        tooltip: ''
+    model: toolbar.button
+    pk: 68
+-   fields:
+        accesskey: ''
+        group: [26]
+        label: "przypis t\u0142umacza"
+        link: ''
+        params: '{"tag": "pt"}'
+        scriptlet: insert_tag
+        slug: przypis-tlumacza
+        tooltip: ''
+    model: toolbar.button
+    pk: 69
+-   fields:
+        accesskey: ''
+        group: [26]
+        label: przypis redaktorski
+        link: ''
+        params: '{"tag": "pr"}'
+        scriptlet: insert_tag
+        slug: przypis-redaktorski
+        tooltip: ''
+    model: toolbar.button
+    pk: 70
+-   fields:
+        accesskey: ''
+        group: [26]
+        label: przypis edytorski
+        link: ''
+        params: '{"tag": "pe"}'
+        scriptlet: insert_tag
+        slug: przypis-edytorski
+        tooltip: ''
+    model: toolbar.button
+    pk: 71
+-   fields:
+        accesskey: ''
+        group: [21]
+        label: wywiad pytanie
+        link: ''
+        params: '{"tag": "wywiad_pyt"}'
+        scriptlet: insert_tag
+        slug: wywiad-pytanie
+        tooltip: ''
+    model: toolbar.button
+    pk: 72
+-   fields:
+        accesskey: ''
+        group: [21]
+        label: "wywiad odpowied\u017A"
+        link: ''
+        params: '{"tag": "wywiad_odp"}'
+        scriptlet: insert_tag
+        slug: wywiad-odpowiedz
+        tooltip: ''
+    model: toolbar.button
+    pk: 73
+-   fields:
+        accesskey: ''
+        group: [13, 22]
+        label: dedykacja
+        link: ''
+        params: '{"tag": "dedykacja"}'
+        scriptlet: insert_tag
+        slug: dedykacja
+        tooltip: ''
+    model: toolbar.button
+    pk: 74
+-   fields:
+        accesskey: ''
+        group: [13, 22]
+        label: motto
+        link: ''
+        params: '{"padding_top": 1, "padding_bottom": 3, "tag": "motto"}'
+        scriptlet: insert_tag
+        slug: motto
+        tooltip: ''
+    model: toolbar.button
+    pk: 75
+-   fields:
+        accesskey: u
+        group: [2]
+        label: "A<sup>\u2193</sup>"
+        link: ''
+        params: '[]'
+        scriptlet: lowercase
+        slug: tolowercase
+        tooltip: "Zamie\u0144 wielkie litery na ma\u0142e"
+    model: toolbar.button
+    pk: 76
+-   fields:
+        accesskey: ''
+        group: [2]
+        label: "zamiana cudzys\u0142ow\xF3w 2"
+        link: ''
+        params: '{"exprs": [["\u00bb|\u201e", ",,"], ["\u00ab", "\""], ["([^=])\"([\u0104\u0118\u00d3\u0141\u017b\u0179\u0106\u0143\u0105\u017c\u017a\u015b\u0144\u00f3\u0142\u0107\\w])",
+            "$1,,$2"], ["^\"([\u0104\u0118\u00d3\u0141\u017b\u0179\u0106\u0143\u0105\u017c\u017a\u015b\u0144\u00f3\u0142\u0107\\w])",
+            ",,$1"], ["(,,)\\s+|\\s+(\")", "$1"]]}'
+        scriptlet: lineregexp
+        slug: cudzyslow-niemiecki
+        tooltip: "zamienia \" na ,, oraz \xBBa\xAB na ,,a\""
+    model: toolbar.button
+    pk: 77
+-   fields:
+        accesskey: ''
+        group: [22]
+        label: 'typ osoby '
+        link: ''
+        params: '{"padding_top": 1, "padding_bottom": 1, "tag": "lista_osoba", "attrs":
+            {"typ": ""}}'
+        scriptlet: insert_tag
+        slug: lista-osob-pole
+        tooltip: osoby z takim samym opisem
+    model: toolbar.button
+    pk: 78
+-   fields:
+        accesskey: ''
+        group: [22]
+        label: "didaskalia pocz\u0105tkowe"
+        link: ''
+        params: '{"padding_top": 1, "padding_bottom": 3, "tag": "miejsce_czas"}'
+        scriptlet: insert_tag
+        slug: didaskalia-poczatkowe
+        tooltip: "komentarze wprowadzaj\u0105ce przed tekstem dramatu"
+    model: toolbar.button
+    pk: 79
+-   fields:
+        accesskey: s
+        group: [12, 17]
+        label: strofa
+        link: ''
+        params: '{"padding_top": 1, "padding_bottom": 3, "tag": "strofa"}'
+        scriptlet: insert_stanza
+        slug: strofa
+        tooltip: "wstawia strof\u0119"
+    model: toolbar.button
+    pk: 81
+-   fields:
+        accesskey: k
+        group: [12]
+        label: kwestia
+        link: ''
+        params: '{"padding_top": 1, "padding_bottom": 1, "tag": "kwestia"}'
+        scriptlet: insert_tag
+        slug: kwestia
+        tooltip: "wstawia kwesti\u0119"
+    model: toolbar.button
+    pk: 82
+-   fields:
+        accesskey: w
+        group: [12, 17]
+        label: "wers mocno wci\u0119ty"
+        link: ''
+        params: '{"tag": "wers_wciety", "attrs": {"typ": ""}}'
+        scriptlet: insert_tag
+        slug: wers-mocno-wciety
+        tooltip: "argumenty wersu wci\u0119tego: od 2 do 6"
+    model: toolbar.button
+    pk: 84
+-   fields:
+        accesskey: ''
+        group: [12, 17]
+        label: wers cd.
+        link: ''
+        params: '{"tag": "wers_cd"}'
+        scriptlet: insert_tag
+        slug: wers-cd
+        tooltip: "cz\u0119\u015B\u0107 wersu przeniesiona do innego wiersza"
+    model: toolbar.button
+    pk: 85
+-   fields:
+        accesskey: ''
+        group: [12, 17]
+        label: wers do prawej
+        link: ''
+        params: '{"tag": "wers_do_prawej"}'
+        scriptlet: insert_tag
+        slug: wers-do-prawej
+        tooltip: "wers wyr\xf3wnany do prawej"
+    model: toolbar.button
+    pk: 109
+-   fields:
+        accesskey: ''
+        group: []
+        label: Wydrukuj
+        link: print/xml
+        params: '[]'
+        scriptlet: insert_tag
+        slug: print-xml
+        tooltip: ''
+    model: toolbar.button
+    pk: 86
+-   fields:
+        accesskey: ''
+        group: []
+        label: Wydrukuj
+        link: print/html
+        params: '[]'
+        scriptlet: insert_tag
+        slug: htmleditor-print
+        tooltip: ''
+    model: toolbar.button
+    pk: 87
+-   fields:
+        accesskey: ''
+        group: [2]
+        label: "zamiana cudzys\u0142ow\xF3w 1"
+        link: ''
+        params: '{"exprs": [["\u00ab|\u201e", ",,"], ["\u00bb", "\""], ["([^=])\"([\u0104\u0118\u00d3\u0141\u017b\u0179\u0106\u0143\u0105\u017c\u017a\u015b\u0144\u00f3\u0142\u0107\\w])",
+            "$1,,$2"], ["^\"([\u0104\u0118\u00d3\u0141\u017b\u0179\u0106\u0143\u0105\u017c\u017a\u015b\u0144\u00f3\u0142\u0107\\w])",
+            ",,$1"], ["(,,)\\s+|\\s+(\")", "$1"]]}'
+        scriptlet: lineregexp
+        slug: cudzyslow-francuski
+        tooltip: "zamiana \" na ,, oraz  \xABa\xBB na ,,a\""
+    model: toolbar.button
+    pk: 89
+-   fields:
+        accesskey: q
+        group: [12, 17]
+        label: "wers wci\u0119ty"
+        link: ''
+        params: '{"tag": "wers_wciety", "attrs": {"typ": "1"}}'
+        scriptlet: insert_tag
+        slug: wers-wciety
+        tooltip: "wstawia wers wci\u0119ty"
+    model: toolbar.button
+    pk: 91
+-   fields:
+        accesskey: r
+        group: [15]
+        label: "tytu\u0142 dzie\u0142a"
+        link: ''
+        params: '{"tag": "tytul_dziela"}'
+        scriptlet: insert_tag
+        slug: tytul-dziela
+        tooltip: ''
+    model: toolbar.button
+    pk: 92
+-   fields:
+        accesskey: ''
+        group: [22]
+        label: "lista os\xF3b"
+        link: ''
+        params: '{"padding_top": 1, "padding_bottom": 4, "tag": "lista_osob"}'
+        scriptlet: insert_tag
+        slug: lista-osob
+        tooltip: "lista os\xF3b poprzedzaj\u0105ca tekst dramatu"
+    model: toolbar.button
+    pk: 93
+-   fields:
+        accesskey: ''
+        group: [22]
+        label: "nag\u0142\xF3wek listy"
+        link: ''
+        params: '{"padding_top": 1, "padding_bottom": 2, "tag": "naglowek_listy"}'
+        scriptlet: insert_tag
+        slug: naglowek-listy
+        tooltip: "nag\u0142\xF3wek listy os\xF3b"
+    model: toolbar.button
+    pk: 94
+-   fields:
+        accesskey: ''
+        group: [22]
+        label: osoba na liscie
+        link: ''
+        params: '{"padding_top": 1, "padding_bottom": 1, "tag": "lista_osoba"}'
+        scriptlet: insert_tag
+        slug: osoba-na-liscie
+        tooltip: "nazwa osoby na liscie os\xF3b"
+    model: toolbar.button
+    pk: 95
+-   fields:
+        accesskey: ''
+        group: []
+        label: extra
+        link: ''
+        params: '{"tag": "extra"}'
+        scriptlet: insert_tag
+        slug: extra
+        tooltip: "uwagi dotycz\u0105ce sk\u0142adu"
+    model: toolbar.button
+    pk: 96
+-   fields:
+        accesskey: ''
+        group: [28]
+        label: akapity
+        link: ''
+        params: '{"tag": "akap"}'
+        scriptlet: autotag
+        slug: akapity
+        tooltip: "autotagowanie akapit\xF3w"
+    model: toolbar.button
+    pk: 97
+-   fields:
+        accesskey: ''
+        group: [28]
+        label: strofy
+        link: ''
+        params: '{"tag": "strofa"}'
+        scriptlet: autotag
+        slug: strofy
+        tooltip: autotagowanie strof
+    model: toolbar.button
+    pk: 99
+-   fields:
+        accesskey: ''
+        group: [28]
+        label: "wersy wci\u0119te"
+        link: ''
+        params: '{"padding": 1, "tag": "wers_wciety", "split": 1}'
+        scriptlet: autotag
+        slug: wersy-wciete
+        tooltip: "autotagowanie wers\xF3w wci\u0119tych"
+    model: toolbar.button
+    pk: 100
+-   fields:
+        accesskey: g
+        group: [12]
+        label: kwestioakapit
+        link: ''
+        params: '[["insert_tag", {"tag": "akap"}], ["insert_tag", {"padding_top":
+            1, "padding_bottom": 1, "tag": "kwestia"}]]'
+        scriptlet: macro
+        slug: kwestioakapit
+        tooltip: ''
+    model: toolbar.button
+    pk: 101
+-   fields:
+        accesskey: ''
+        group: [12]
+        label: kwestiostrofa
+        link: ''
+        params: '[["insert_stanza", {"tag": "strofa"}], ["insert_tag", {"padding_top":
+            1, "padding_bottom": 1, "tag": "kwestia"}]]'
+        scriptlet: macro
+        slug: kwestiostrofa
+        tooltip: ''
+    model: toolbar.button
+    pk: 102
+-   fields:
+        accesskey: ''
+        group: [28]
+        label: "nag\u0142. dramatu"
+        link: ''
+        params: '{"exprs": [["^AKT(\\s\\w*)$", "<naglowek_akt>AKT$1</naglowek_akt>"],
+            ["^SCENA(\\s\\w*)$", "<naglowek_scena>SCENA$1</naglowek_scena>"], ["([A-Z\u0104\u0106\u0118\u0141\u0143\u00d3\u015a\u017b\u0179]{2}[A-Z\u0104\u0106\u0118\u0141\u0143\u00d3\u015a\u017b\u0179\\s]+)$",
+            "<naglowek_osoba>$1</naglowek_osoba>"]]}'
+        scriptlet: lineregexp
+        slug: nagl-dramatu
+        tooltip: "autotagowanie akt\xF3w, scen, nag\u0142\xF3wk\xF3w os\xF3b"
+    model: toolbar.button
+    pk: 103
+-   fields:
+        accesskey: x
+        group: [13]
+        label: nota red.
+        link: ''
+        params: '{"padding_top": 1, "padding_bottom": 3, "tag": "nota_red"}'
+        scriptlet: insert_tag
+        slug: nota-red
+        tooltip: nota redakcyjna
+    model: toolbar.button
+    pk: 104
+-   fields:
+        accesskey: ''
+        group: [2]
+        label: slug
+        link: ''
+        params: '[]'
+        scriptlet: slugify
+        slug: slug
+        tooltip: slugifikacja
+    model: toolbar.button
+    pk: 105
+-   fields:
+        accesskey: ''
+        group: [11]
+        label: trim begin
+        link: ''
+        params: '{"text": "\n<!-- TRIM_BEGIN -->\n"}'
+        scriptlet: insert_text
+        slug: trim-begin
+        tooltip: "Wstawia pocz\u0105tkowy znacznik ci\u0119cia cz\u0119\u015Bci"
+    model: toolbar.button
+    pk: 106
+-   fields:
+        accesskey: ''
+        group: [11]
+        label: trim end
+        link: ''
+        params: '{"text": "\n<!-- TRIM_END -->\n"}'
+        scriptlet: insert_text
+        slug: trim-end
+        tooltip: "Wstawia ko\u0144cowy znacznik ci\u0119cia cz\u0119\u015Bci"
+    model: toolbar.button
+    pk: 107
+-   fields:
+        accesskey: ''
+        group: [11]
+        label: etap
+        link: ''
+        params: '{"tag": "developmentStage"}'
+        scriptlet: insert_tag
+        slug: etap
+        tooltip: "wymaga uwsp\xF3\u0142czesnienia: 0.3"
+    model: toolbar.button
+    pk: 108
+-   fields: {code: '-'}
+    model: toolbar.scriptlet
+    pk: autotag
+-   fields: {code: "editor.showPopup('generic-info', 'Przetwarzanie zaznaczonego tekstu...',\
+            \ '', -1);\n$.log(editor, panel, params);\nvar cm = panel.texteditor;\n\
+            var exprs = $.map(params.exprs, function(expr) {\n    var opts = \"mg\"\
+            ;\n    if(expr.length > 2)\n        opts = expr[2];\n\n    return {rx:\
+            \ new RegExp(expr[0], opts), repl: expr[1]};\n});\n\nvar partial = true;\n\
+            var text = cm.selection();\n\nif(!text) {\n    var cpos = cm.cursorPosition();\n\
+            \    cpos.line = cm.lineNumber(cpos.line)\n    cm.selectLines(cm.firstLine(),\
+            \ 0, cm.lastLine(), 0);\n\n    text = cm.selection();\n    partial = false;\n\
+            }\n\nvar original = text;\n$(exprs).each(function() { \n    text = text.replace(this.rx,\
+            \ this.repl);\n});\n\nif( original != text) \n{    \n    cm.replaceSelection(text);\n\
+            \    panel.fireEvent('contentChanged');\n    editor.showPopup('generic-yes',\
+            \ 'Zmieniono tekst' );\n    editor.advancePopupQueue();\n}\nelse {\n \
+            \   editor.showPopup('generic-info', 'Brak zmian w tek\u015Bcie.');\n\
+            \    editor.advancePopupQueue();\n}\n\nif(!partial) {\n    cm.selectLines(\
+            \ cm.nthLine(cpos.line), cpos.character );\n}"}
+    model: toolbar.scriptlet
+    pk: fulltextregexp
+-   fields: {code: "var texteditor = panel.texteditor;\r\nvar text = texteditor.selection();\r\
+            \n\r\nif(text) {\r\n  var verses = text.split('\\n');\r\n  var text =\
+            \ ''; var buf = ''; var ebuf = '';\r\n  var first = true;\r\n\r\n  for(var\
+            \ i=0;  i < verses.length; i++) {\r\n    verse = verses[i].replace(/^\\\
+            s+/, \"\").replace(/\\s+$/, \"\");   \r\n    if(verse) {\r\n      text\
+            \ += (buf ? buf + '/\\n' : '') + ebuf;\r\n      buf = (first ? '<strofa>\\\
+            n' : '') + verses[i];\r\n      ebuf = '';\r\n      first = false;\r\n\
+            \    } else {    \r\n      ebuf += '\\n' + verses[i];\r\n    }\r\n  };\r\
+            \n  text = text + buf + '\\n</strofa>' + ebuf; \r\n  texteditor.replaceSelection(text);\r\
+            \n}\r\n\r\nif (!text) {\r\n    var pos = texteditor.cursorPosition();\r\
+            \n    texteditor.selectLines(pos.line, pos.character + 6 + 2);\r\n}\r\n\
+            \r\n\r\n\r\n\r\n\r\n\r\n\r\npanel.fireEvent('contentChanged');"}
+    model: toolbar.scriptlet
+    pk: insert_stanza
+-   fields: {code: "var texteditor = panel.texteditor;\nvar text = texteditor.selection();\n\
+            var start_tag = '<'+params.tag;\nfor (var attr in params.attrs) {\n  \
+            \  start_tag += ' '+attr+'=\"' + params.attrs[attr] + '\"';\n};\nstart_tag\
+            \ += '>';\nvar end_tag = '</'+params.tag+'>';\n\nif(text.length > 0) {\n\
+            // tokenize\nvar output = ''\nvar token = ''\nfor(var index=0; index <\
+            \ text.length; index++)\n{\n    if (text[index].match(/\\s/)) { // whitespace\n\
+            \        token += text[index];\n    }\n    else { // character\n     \
+            \   output += token;\n        if(output == token) output += start_tag;\n\
+            \        token = ''\n        output += text[index];\n    }\n}\n\nif( output[output.length-1]\
+            \ == '\\\\' ) {\n    output = output.substr(0, output.length-1) + end_tag\
+            \ + '\\\\';\n} else {\n    output += end_tag;\n}\noutput += token;\n}\n\
+            else {\n output = start_tag + end_tag;\n}\n\ntexteditor.replaceSelection(output);\n\
+            \nif (text.length == 0) {\n    var pos = texteditor.cursorPosition();\n\
+            \    texteditor.selectLines(pos.line, pos.character + params.tag.length\
+            \ + 2);\n}\n\npanel.fireEvent('contentChanged');"}
+    model: toolbar.scriptlet
+    pk: insert_tag
+-   fields: {code: '-'}
+    model: toolbar.scriptlet
+    pk: insert_text
+-   fields: {code: "editor.showPopup('generic-info', 'Przetwarzanie zaznaczonego tekstu...',\
+            \ '', -1);\n\nvar cm = panel.texteditor;\nvar exprs = $.map(params.exprs,\
+            \ function(expr) {\n\n    var opts = \"g\";\n\n    if(expr.length > 2)\n\
+            \n        opts = expr[2];\n\n    return {rx: new RegExp(expr[0], opts),\
+            \ repl: expr[1]};\n\n});\n\n\n\nvar partial = true;\n\nvar text = cm.selection();\n\
+            \n\n\nif(!text) {\n\n    var cpos = cm.cursorPosition();\n\n    cpos.line\
+            \ = cm.lineNumber(cpos.line)\n\n    cm.selectLines(cm.firstLine(), 0,\
+            \ cm.lastLine(), 0);\n\n    text = cm.selection();\n\n    partial = false;\n\
+            \n}\n\n\n\nvar changed = 0;\nvar lines = text.split('\\n');\nvar lines\
+            \ = $.map(lines, function(line) { \n    var old_line = line;\n    $(exprs).each(function()\
+            \ { \n        var expr = this;\n        line = line.replace(expr.rx, expr.repl);\n\
+            \    });\n\n    if(old_line != line) changed += 1;\n    return line;\n\
+            });\n\nif(changed > 0) \n{\n    cm.replaceSelection( lines.join('\\n')\
+            \ );\n    panel.fireEvent('contentChanged');\n    editor.showPopup('generic-yes',\
+            \ 'Zmieniono ' + changed + ' linii.', 1500);\n    editor.advancePopupQueue();\n\
+            }\nelse {\n    editor.showPopup('generic-info',  'Brak zmian w tek\u015B\
+            cie', 1500);\n    editor.advancePopupQueue();\n}\n\nif(!partial)\n   \
+            \ cm.selectLines( cm.nthLine(cpos.line), cpos.character )"}
+    model: toolbar.scriptlet
+    pk: lineregexp
+-   fields: {code: "var cm = panel.texteditor;\r\nvar text = cm.selection();\r\n\r\
+            \nif(!text) return;\r\nvar repl = '';\r\nvar lcase = text.toLowerCase();\r\
+            \nvar ucase = text.toUpperCase();\r\n\r\nif(lcase == text) repl = ucase;\
+            \ /* was lowercase */\r\nelse if(ucase != text) repl = lcase; /* neither\
+            \ lower- or upper-case */\r\nelse { /* upper case -> title-case */\r\n\
+            \   var words = $(lcase.split(/\\s/)).map(function() { \r\n        if(this.length\
+            \ > 0) { return this[0].toUpperCase() + this.slice(1); } else { return\
+            \ ''}\r\n   }); \r\n   repl = words.join(' ');\r\n} \r\n\r\nif(repl !=\
+            \ text) {\r\n    cm.replaceSelection(repl);\r\n    panel.fireEvent('contentChanged');\r\
+            \n};"}
+    model: toolbar.scriptlet
+    pk: lowercase
+-   fields: {code: "$(params).each(function() {\n    $.log(this[0], this[1]);\n  \
+            \  editor.callScriptlet(this[0], panel, this[1]);\n\n});"}
+    model: toolbar.scriptlet
+    pk: macro
+-   fields: {code: '-'}
+    model: toolbar.scriptlet
+    pk: slugify
diff --git a/src/toolbar/locale/pl/LC_MESSAGES/django.mo b/src/toolbar/locale/pl/LC_MESSAGES/django.mo
new file mode 100644 (file)
index 0000000..54dabdf
Binary files /dev/null and b/src/toolbar/locale/pl/LC_MESSAGES/django.mo differ
diff --git a/src/toolbar/locale/pl/LC_MESSAGES/django.po b/src/toolbar/locale/pl/LC_MESSAGES/django.po
new file mode 100644 (file)
index 0000000..8c7cfb1
--- /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-08-03 12:14+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:17
+msgid "button group"
+msgstr "grupa przycisków"
+
+#: models.py:17
+msgid "button groups"
+msgstr "grupy przycisków"
+
+#: models.py:51
+msgid "button"
+msgstr "przycisk"
+
+#: models.py:51
+msgid "buttons"
+msgstr "przyciski"
+
+#: models.py:75
+msgid "javascript"
+msgstr ""
diff --git a/src/toolbar/management/__init__.py b/src/toolbar/management/__init__.py
new file mode 100644 (file)
index 0000000..792d600
--- /dev/null
@@ -0,0 +1 @@
+#
diff --git a/src/toolbar/management/commands/__init__.py b/src/toolbar/management/commands/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/toolbar/management/commands/fixbuttons.py b/src/toolbar/management/commands/fixbuttons.py
new file mode 100644 (file)
index 0000000..de48ced
--- /dev/null
@@ -0,0 +1,73 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
+
+from django.core.management.base import NoArgsCommand
+from toolbar.models import Button, ButtonGroup
+import json
+import re
+
+
+class Command(NoArgsCommand):
+
+    def handle_noargs(self, **options):
+        buttons = Button.objects.all()
+        print "Validating parameters... "
+        for b in buttons:
+            params = b.params
+            try:
+                v = json.loads(b.params)
+            except ValueError, e:
+                print 'Trying to fix button "%s" ...' % b.slug
+                if params[0] == u'(':
+                    params = params[1:]
+                if params[-1] == u')':
+                    params = params[:-1]
+                try:
+                    v = son.loads(re.sub(u'([\\w-]+)\\s*:', u'"\\1": ', params).encode('utf-8'))
+                except ValueError, e:
+                    print "Unable to fix '%s' " % b.params
+                    print "Try to fix this button manually and rerun the script."
+                    return False
+
+            # resave
+            b.params = json.dumps(v)
+            b.save()
+
+        print "Merge duplicate buttons (if any)..."
+        hash = {}
+        for b in buttons:
+            if b.slug not in hash:
+                hash[b.slug] = b
+                continue
+
+            # duplicate button
+            print "Found duplicate of '%s'" % b.slug
+            a = hash[b.slug]
+
+            remove_duplicate = True
+            if a.params != b.params:
+                print "Conflicting params for duplicate of '%s'." % b.slug
+                print "Groups will be joined, but won't remove duplicates."
+                remove_duplicate = False
+
+            for g in b.group.all():
+                a.group.add(g)
+
+            b.group.clear()
+
+            a.save()
+            if remove_duplicate:
+                b.delete()
+
+        print "Searching for empty groups and orphaned buttons:"
+        for g in ButtonGroup.objects.all():
+            if len(g.button_set.all()) == 0:
+                print "Empty group: '%s'" % g.slug
+
+        for b in Button.objects.all():
+            if len(b.group.all()) == 0:
+                print "orphan: '%s'" % b.slug
diff --git a/src/toolbar/migrations/0001_initial.py b/src/toolbar/migrations/0001_initial.py
new file mode 100644 (file)
index 0000000..5a6f27c
--- /dev/null
@@ -0,0 +1,93 @@
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+class Migration(SchemaMigration):
+
+    def forwards(self, orm):
+
+        # Adding model 'ButtonGroup'
+        db.create_table('toolbar_buttongroup', (
+            ('position', self.gf('django.db.models.fields.IntegerField')(default=0)),
+            ('slug', self.gf('django.db.models.fields.SlugField')(max_length=50, db_index=True)),
+            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('name', self.gf('django.db.models.fields.CharField')(max_length=32)),
+        ))
+        db.send_create_signal('toolbar', ['ButtonGroup'])
+
+        # Adding model 'Button'
+        db.create_table('toolbar_button', (
+            ('key_mod', self.gf('django.db.models.fields.PositiveIntegerField')(default=1, blank=True)),
+            ('scriptlet', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['toolbar.Scriptlet'], null=True, blank=True)),
+            ('tooltip', self.gf('django.db.models.fields.CharField')(max_length=120, blank=True)),
+            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('link', self.gf('django.db.models.fields.CharField')(default='', max_length=256, blank=True)),
+            ('key', self.gf('django.db.models.fields.CharField')(max_length=1, blank=True)),
+            ('params', self.gf('django.db.models.fields.TextField')(default='[]')),
+            ('label', self.gf('django.db.models.fields.CharField')(max_length=32)),
+            ('slug', self.gf('django.db.models.fields.SlugField')(unique=True, max_length=50, db_index=True)),
+        ))
+        db.send_create_signal('toolbar', ['Button'])
+
+        # Adding M2M table for field group on 'Button'
+        db.create_table('toolbar_button_group', (
+            ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+            ('button', models.ForeignKey(orm['toolbar.button'], null=False)),
+            ('buttongroup', models.ForeignKey(orm['toolbar.buttongroup'], null=False))
+        ))
+        db.create_unique('toolbar_button_group', ['button_id', 'buttongroup_id'])
+
+        # Adding model 'Scriptlet'
+        db.create_table('toolbar_scriptlet', (
+            ('code', self.gf('django.db.models.fields.TextField')()),
+            ('name', self.gf('django.db.models.fields.CharField')(max_length=64, primary_key=True)),
+        ))
+        db.send_create_signal('toolbar', ['Scriptlet'])
+
+
+    def backwards(self, orm):
+
+        # Deleting model 'ButtonGroup'
+        db.delete_table('toolbar_buttongroup')
+
+        # Deleting model 'Button'
+        db.delete_table('toolbar_button')
+
+        # Removing M2M table for field group on 'Button'
+        db.delete_table('toolbar_button_group')
+
+        # Deleting model 'Scriptlet'
+        db.delete_table('toolbar_scriptlet')
+
+
+    models = {
+        'toolbar.button': {
+            'Meta': {'object_name': 'Button'},
+            'group': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['toolbar.ButtonGroup']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'key': ('django.db.models.fields.CharField', [], {'max_length': '1', 'blank': 'True'}),
+            'key_mod': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1', 'blank': 'True'}),
+            'label': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
+            'link': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
+            'params': ('django.db.models.fields.TextField', [], {'default': "'[]'"}),
+            'scriptlet': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['toolbar.Scriptlet']", 'null': 'True', 'blank': 'True'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50', 'db_index': 'True'}),
+            'tooltip': ('django.db.models.fields.CharField', [], {'max_length': '120', 'blank': 'True'})
+        },
+        'toolbar.buttongroup': {
+            'Meta': {'object_name': 'ButtonGroup'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
+            'position': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'})
+        },
+        'toolbar.scriptlet': {
+            'Meta': {'object_name': 'Scriptlet'},
+            'code': ('django.db.models.fields.TextField', [], {}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '64', 'primary_key': 'True'})
+        }
+    }
+
+    complete_apps = ['toolbar']
diff --git a/src/toolbar/migrations/0002_auto__del_field_button_key_mod__chg_field_button_key.py b/src/toolbar/migrations/0002_auto__del_field_button_key_mod__chg_field_button_key.py
new file mode 100644 (file)
index 0000000..af58c6e
--- /dev/null
@@ -0,0 +1,52 @@
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+class Migration(SchemaMigration):
+
+    def forwards(self, orm):
+
+        # Deleting field 'Button.key_mod'
+        db.delete_column('toolbar_button', 'key_mod')
+
+        # Changing field 'Button.key'
+        db.alter_column('toolbar_button', 'key', self.gf('django.db.models.fields.CharField')(max_length=1, null=True))
+
+    def backwards(self, orm):
+
+        # Adding field 'Button.key_mod'
+        db.add_column('toolbar_button', 'key_mod', self.gf('django.db.models.fields.PositiveIntegerField')(default=1, blank=True), keep_default=False)
+
+        # Changing field 'Button.key'
+        db.alter_column('toolbar_button', 'key', self.gf('django.db.models.fields.CharField')(max_length=1, blank=True))
+
+    models = {
+        'toolbar.button': {
+            'Meta': {'object_name': 'Button'},
+            'group': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['toolbar.ButtonGroup']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'key': ('django.db.models.fields.CharField', [], {'max_length': '1', 'null': 'True'}),
+            'label': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
+            'link': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
+            'params': ('django.db.models.fields.TextField', [], {'default': "'[]'"}),
+            'scriptlet': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['toolbar.Scriptlet']", 'null': 'True', 'blank': 'True'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50', 'db_index': 'True'}),
+            'tooltip': ('django.db.models.fields.CharField', [], {'max_length': '120', 'blank': 'True'})
+        },
+        'toolbar.buttongroup': {
+            'Meta': {'object_name': 'ButtonGroup'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
+            'position': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'})
+        },
+        'toolbar.scriptlet': {
+            'Meta': {'object_name': 'Scriptlet'},
+            'code': ('django.db.models.fields.TextField', [], {}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '64', 'primary_key': 'True'})
+        }
+    }
+
+    complete_apps = ['toolbar']
diff --git a/src/toolbar/migrations/0003_button_key_rename_to_accesskey.py b/src/toolbar/migrations/0003_button_key_rename_to_accesskey.py
new file mode 100644 (file)
index 0000000..7a0961d
--- /dev/null
@@ -0,0 +1,47 @@
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+class Migration(SchemaMigration):
+
+    def forwards(self, orm):
+
+        # Deleting field 'Button.key'
+        db.rename_column('toolbar_button', 'key', 'accesskey')
+
+
+
+    def backwards(self, orm):
+        db.rename_column('toolbar_button', 'accesskey', 'key')
+
+
+    models = {
+        'toolbar.button': {
+            'Meta': {'object_name': 'Button'},
+            'accesskey': ('django.db.models.fields.CharField', [], {'max_length': '1', 'null': 'True'}),
+            'group': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['toolbar.ButtonGroup']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'label': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
+            'link': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
+            'params': ('django.db.models.fields.TextField', [], {'default': "'[]'"}),
+            'scriptlet': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['toolbar.Scriptlet']", 'null': 'True', 'blank': 'True'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50', 'db_index': 'True'}),
+            'tooltip': ('django.db.models.fields.CharField', [], {'max_length': '120', 'blank': 'True'})
+        },
+        'toolbar.buttongroup': {
+            'Meta': {'object_name': 'ButtonGroup'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
+            'position': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'})
+        },
+        'toolbar.scriptlet': {
+            'Meta': {'object_name': 'Scriptlet'},
+            'code': ('django.db.models.fields.TextField', [], {}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '64', 'primary_key': 'True'})
+        }
+    }
+
+    complete_apps = ['toolbar']
diff --git a/src/toolbar/migrations/0004_auto__chg_field_button_accesskey.py b/src/toolbar/migrations/0004_auto__chg_field_button_accesskey.py
new file mode 100644 (file)
index 0000000..5f28bf5
--- /dev/null
@@ -0,0 +1,48 @@
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+class Migration(SchemaMigration):
+
+    def forwards(self, orm):
+        
+        # Changing field 'Button.accesskey'
+        db.alter_column('toolbar_button', 'accesskey', self.gf('django.db.models.fields.CharField')(max_length=1))
+
+
+    def backwards(self, orm):
+        
+        # Changing field 'Button.accesskey'
+        db.alter_column('toolbar_button', 'accesskey', self.gf('django.db.models.fields.CharField')(max_length=1, null=True))
+
+
+    models = {
+        'toolbar.button': {
+            'Meta': {'ordering': "('slug',)", 'object_name': 'Button'},
+            'accesskey': ('django.db.models.fields.CharField', [], {'max_length': '1', 'blank': 'True'}),
+            'group': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['toolbar.ButtonGroup']", 'symmetrical': 'False'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'label': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
+            'link': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
+            'params': ('django.db.models.fields.TextField', [], {'default': "'[]'"}),
+            'scriptlet': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['toolbar.Scriptlet']", 'null': 'True', 'blank': 'True'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50', 'db_index': 'True'}),
+            'tooltip': ('django.db.models.fields.CharField', [], {'max_length': '120', 'blank': 'True'})
+        },
+        'toolbar.buttongroup': {
+            'Meta': {'ordering': "('position', 'name')", 'object_name': 'ButtonGroup'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
+            'position': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'})
+        },
+        'toolbar.scriptlet': {
+            'Meta': {'object_name': 'Scriptlet'},
+            'code': ('django.db.models.fields.TextField', [], {}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '64', 'primary_key': 'True'})
+        }
+    }
+
+    complete_apps = ['toolbar']
diff --git a/src/toolbar/migrations/0005_initial_data.py b/src/toolbar/migrations/0005_initial_data.py
new file mode 100644 (file)
index 0000000..b31f380
--- /dev/null
@@ -0,0 +1,46 @@
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import DataMigration
+from django.db import models
+
+class Migration(DataMigration):
+
+    def forwards(self, orm):
+        from django.core.management import call_command
+        call_command("loaddata", "initial_toolbar.yaml")
+
+
+    def backwards(self, orm):
+        "Write your backwards methods here."
+        pass
+
+
+    models = {
+        'toolbar.button': {
+            'Meta': {'ordering': "('slug',)", 'object_name': 'Button'},
+            'accesskey': ('django.db.models.fields.CharField', [], {'max_length': '1', 'blank': 'True'}),
+            'group': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['toolbar.ButtonGroup']", 'symmetrical': 'False'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'label': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
+            'link': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
+            'params': ('django.db.models.fields.TextField', [], {'default': "'[]'"}),
+            'scriptlet': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['toolbar.Scriptlet']", 'null': 'True', 'blank': 'True'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50', 'db_index': 'True'}),
+            'tooltip': ('django.db.models.fields.CharField', [], {'max_length': '120', 'blank': 'True'})
+        },
+        'toolbar.buttongroup': {
+            'Meta': {'ordering': "('position', 'name')", 'object_name': 'ButtonGroup'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
+            'position': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'})
+        },
+        'toolbar.scriptlet': {
+            'Meta': {'object_name': 'Scriptlet'},
+            'code': ('django.db.models.fields.TextField', [], {}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '64', 'primary_key': 'True'})
+        }
+    }
+
+    complete_apps = ['toolbar']
diff --git a/src/toolbar/migrations/__init__.py b/src/toolbar/migrations/__init__.py
new file mode 100644 (file)
index 0000000..9012566
--- /dev/null
@@ -0,0 +1 @@
+# pragma: no cover
diff --git a/src/toolbar/models.py b/src/toolbar/models.py
new file mode 100644 (file)
index 0000000..a23e346
--- /dev/null
@@ -0,0 +1,75 @@
+# -*- coding: utf-8 -*-
+#
+# This file is part of FNP-Redakcja, 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 _
+
+
+class ButtonGroup(models.Model):
+    name = models.CharField(max_length=32)
+    slug = models.SlugField()
+    position = models.IntegerField(default=0)
+
+    class Meta:
+        ordering = ('position', 'name',)
+        verbose_name, verbose_name_plural = _('button group'), _('button groups')
+
+    def __unicode__(self):
+        return self.name
+
+    def to_dict(self, with_buttons=False):
+        d = {'name': self.name, 'position': self.position}
+
+        if with_buttons:
+            d['buttons'] = [b.to_dict() for b in self.button_set.all()]
+
+        return d
+
+
+class Button(models.Model):
+    label = models.CharField(max_length=32)
+    slug = models.SlugField(unique=True)  # unused
+
+    # behaviour
+    params = models.TextField(default='[]')  # TODO: should be a JSON field
+    scriptlet = models.ForeignKey('Scriptlet', null=True, blank=True)
+    link = models.CharField(max_length=256, blank=True, default='')
+
+    # ui related stuff
+    accesskey = models.CharField(blank=True, max_length=1)
+
+    tooltip = models.CharField(blank=True, max_length=120)
+
+    # Why the button is restricted to have the same position in each group ?
+    # position = models.IntegerField(default=0)
+    group = models.ManyToManyField(ButtonGroup)
+
+    class Meta:
+        ordering = ('slug',)
+        verbose_name, verbose_name_plural = _('button'), _('buttons')
+
+    @property
+    def full_tooltip(self):
+        return u"%s %s" % (self.tooltip, "[Alt+%s]" % self.accesskey if self.accesskey else "")
+
+    def to_dict(self):
+        return {
+            'label': self.label,
+            'tooltip': self.tooltip,
+            'accesskey': self.accesskey,
+            'params': self.params,
+            'scriptlet_id': self.scriptlet_id,
+        }
+
+    def __unicode__(self):
+        return self.label
+
+
+class Scriptlet(models.Model):
+    name = models.CharField(max_length=64, primary_key=True)
+    code = models.TextField()
+
+    def __unicode__(self):
+        return _(u'javascript') + u':' + self.name
diff --git a/src/toolbar/templates/toolbar/button.html b/src/toolbar/templates/toolbar/button.html
new file mode 100644 (file)
index 0000000..cdffcaf
--- /dev/null
@@ -0,0 +1,13 @@
+{% if button.link %}
+<a href="{{button.link}}" target="_blank">
+{% endif %}
+<button type="button"
+       data-ui-action="{{ button.scriptlet_id }}"
+       data-ui-action-params="{{ button.params|escape }}"
+       data-ui-accesskey="{{ button.accesskey }}"
+       {% if button.tooltip %}title="{{ button.full_tooltip }}"{% endif %} >
+       {{ button.label|safe }}
+</button>
+{% if button.link %}
+</a>
+{% endif %}
\ No newline at end of file
diff --git a/src/toolbar/templates/toolbar/toolbar.html b/src/toolbar/templates/toolbar/toolbar.html
new file mode 100644 (file)
index 0000000..b9624d6
--- /dev/null
@@ -0,0 +1,21 @@
+{% load toolbar_tags %}
+<div class="toolbar">
+    <select class="group_selector">
+        {% for group in toolbar_groups %}
+        <option value="{{ group.slug }}" {% if forloop.first %}selected="selected"{% endif %}>{{ group.name }}</option>
+        {% endfor %}
+    </select>
+
+       <button type="button" class="prev">&lt;</button>
+    <div class="button_group_container">
+        {% for group in toolbar_groups %}
+        <ul data-group="{{ group.slug }}" class="button_group">
+            {# buttons for this group #}
+            {% for button in group.button_set.all %}
+               <li class="toolbar-button">{% toolbar_button button %}</li>
+            {% endfor %}
+        </ul>
+        {% endfor %}
+    </div>
+    <button type="button" class="next">&gt;</button>
+</div>
diff --git a/src/toolbar/templatetags/__init__.py b/src/toolbar/templatetags/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/toolbar/templatetags/toolbar_tags.py b/src/toolbar/templatetags/toolbar_tags.py
new file mode 100644 (file)
index 0000000..0766677
--- /dev/null
@@ -0,0 +1,19 @@
+# -*- coding: utf-8 -*-
+#
+# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
+from django import template
+from toolbar import models
+
+register = template.Library()
+
+
+@register.inclusion_tag('toolbar/toolbar.html')
+def toolbar():
+    return {'toolbar_groups': models.ButtonGroup.objects.all().select_related()}
+
+
+@register.inclusion_tag('toolbar/button.html')
+def toolbar_button(b):
+    return {'button': b}
diff --git a/src/wiki/__init__.py b/src/wiki/__init__.py
new file mode 100644 (file)
index 0000000..c53f0e7
--- /dev/null
@@ -0,0 +1 @@
+  # pragma: no cover
diff --git a/src/wiki/admin.py b/src/wiki/admin.py
new file mode 100644 (file)
index 0000000..ae309a9
--- /dev/null
@@ -0,0 +1,9 @@
+from django.contrib import admin
+
+from wiki import models
+
+
+class ThemeAdmin(admin.ModelAdmin):
+    search_fields = ['name']
+
+admin.site.register(models.Theme, ThemeAdmin)
diff --git a/src/wiki/fixtures/initial_themes.yaml b/src/wiki/fixtures/initial_themes.yaml
new file mode 100644 (file)
index 0000000..fc0b773
--- /dev/null
@@ -0,0 +1,1246 @@
+- fields: {name: Alkohol}
+  model: wiki.theme
+  pk: 1
+- fields: {name: Ambicja}
+  model: wiki.theme
+  pk: 2
+- fields: {name: "Anio\u0142"}
+  model: wiki.theme
+  pk: 3
+- fields: {name: Antysemityzm}
+  model: wiki.theme
+  pk: 4
+- fields: {name: Arkadia}
+  model: wiki.theme
+  pk: 5
+- fields: {name: Artysta}
+  model: wiki.theme
+  pk: 6
+- fields: {name: "Bezdomno\u015B\u0107"}
+  model: wiki.theme
+  pk: 7
+- fields: {name: "Bezpiecze\u0144stwo"}
+  model: wiki.theme
+  pk: 8
+- fields: {name: Bieda}
+  model: wiki.theme
+  pk: 9
+- fields: {name: Bijatyka}
+  model: wiki.theme
+  pk: 10
+- fields: {name: Bogactwo}
+  model: wiki.theme
+  pk: 14
+- fields: {name: Brat}
+  model: wiki.theme
+  pk: 16
+- fields: {name: Bunt}
+  model: wiki.theme
+  pk: 17
+- fields: {name: Buntownik}
+  model: wiki.theme
+  pk: 18
+- fields: {name: Burza}
+  model: wiki.theme
+  pk: 19
+- fields: {name: "B\xF3g"}
+  model: wiki.theme
+  pk: 15
+- fields: {name: "B\u0142azen"}
+  model: wiki.theme
+  pk: 11
+- fields: {name: "B\u0142oto"}
+  model: wiki.theme
+  pk: 13
+- fields: {name: "B\u0142\u0105dzenie"}
+  model: wiki.theme
+  pk: 12
+- fields: {name: Car}
+  model: wiki.theme
+  pk: 20
+- fields: {name: Carpe diem}
+  model: wiki.theme
+  pk: 21
+- fields: {name: "Chciwo\u015B\u0107"}
+  model: wiki.theme
+  pk: 25
+- fields: {name: Chleb}
+  model: wiki.theme
+  pk: 26
+- fields: {name: Choroba}
+  model: wiki.theme
+  pk: 28
+- fields: {name: Chrystus}
+  model: wiki.theme
+  pk: 29
+- fields: {name: Chrzest}
+  model: wiki.theme
+  pk: 30
+- fields: {name: "Ch\u0142op"}
+  model: wiki.theme
+  pk: 27
+- fields: {name: "Cia\u0142o"}
+  model: wiki.theme
+  pk: 31
+- fields: {name: "Ciemno\u015B\u0107"}
+  model: wiki.theme
+  pk: 22
+- fields: {name: Cierpienie}
+  model: wiki.theme
+  pk: 32
+- fields: {name: "Cie\u0144"}
+  model: wiki.theme
+  pk: 23
+- fields: {name: Cisza}
+  model: wiki.theme
+  pk: 24
+- fields: {name: Cmentarz}
+  model: wiki.theme
+  pk: 33
+- fields: {name: Cnota}
+  model: wiki.theme
+  pk: 34
+- fields: {name: Cud}
+  model: wiki.theme
+  pk: 36
+- fields: {name: Czarownica}
+  model: wiki.theme
+  pk: 37
+- fields: {name: Czary}
+  model: wiki.theme
+  pk: 38
+- fields: {name: Czas}
+  model: wiki.theme
+  pk: 39
+- fields: {name: Czyn}
+  model: wiki.theme
+  pk: 40
+- fields: {name: "Czy\u015Bciec"}
+  model: wiki.theme
+  pk: 41
+- fields: {name: "C\xF3rka"}
+  model: wiki.theme
+  pk: 35
+- fields: {name: Dama}
+  model: wiki.theme
+  pk: 42
+- fields: {name: Danse macabre}
+  model: wiki.theme
+  pk: 43
+- fields: {name: Deszcz}
+  model: wiki.theme
+  pk: 44
+- fields: {name: "Diabe\u0142"}
+  model: wiki.theme
+  pk: 45
+- fields: {name: Dobro}
+  model: wiki.theme
+  pk: 46
+- fields: {name: Dom}
+  model: wiki.theme
+  pk: 47
+- fields: {name: "Doros\u0142o\u015B\u0107"}
+  model: wiki.theme
+  pk: 48
+- fields: {name: Drzewo}
+  model: wiki.theme
+  pk: 49
+- fields: {name: Duch}
+  model: wiki.theme
+  pk: 50
+- fields: {name: Duma}
+  model: wiki.theme
+  pk: 52
+- fields: {name: Dusza}
+  model: wiki.theme
+  pk: 51
+- fields: {name: Dworek}
+  model: wiki.theme
+  pk: 53
+- fields: {name: Dworzanin}
+  model: wiki.theme
+  pk: 54
+- fields: {name: "Dw\xF3r"}
+  model: wiki.theme
+  pk: 55
+- fields: {name: "Dzieci\u0144stwo"}
+  model: wiki.theme
+  pk: 56
+- fields: {name: Dziecko}
+  model: wiki.theme
+  pk: 57
+- fields: {name: Dziedzictwo}
+  model: wiki.theme
+  pk: 58
+- fields: {name: Dziewictwo}
+  model: wiki.theme
+  pk: 59
+- fields: {name: "D\u017Awi\u0119k"}
+  model: wiki.theme
+  pk: 60
+- fields: {name: Egzorcyzm}
+  model: wiki.theme
+  pk: 61
+- fields: {name: Elita}
+  model: wiki.theme
+  pk: 62
+- fields: {name: Emigrant}
+  model: wiki.theme
+  pk: 63
+- fields: {name: "Fa\u0142sz"}
+  model: wiki.theme
+  pk: 64
+- fields: {name: Filozof}
+  model: wiki.theme
+  pk: 65
+- fields: {name: Fircyk}
+  model: wiki.theme
+  pk: 66
+- fields: {name: Flirt}
+  model: wiki.theme
+  pk: 67
+- fields: {name: Gospodarz}
+  model: wiki.theme
+  pk: 71
+- fields: {name: Gospodyni}
+  model: wiki.theme
+  pk: 72
+- fields: {name: Gotycyzm}
+  model: wiki.theme
+  pk: 74
+- fields: {name: "Go\u015B\u0107"}
+  model: wiki.theme
+  pk: 73
+- fields: {name: Gra}
+  model: wiki.theme
+  pk: 76
+- fields: {name: Grzech}
+  model: wiki.theme
+  pk: 78
+- fields: {name: "Grzeczno\u015B\u0107"}
+  model: wiki.theme
+  pk: 79
+- fields: {name: "Gr\xF3b"}
+  model: wiki.theme
+  pk: 77
+- fields: {name: Gwiazda}
+  model: wiki.theme
+  pk: 80
+- fields: {name: "G\xF3ra"}
+  model: wiki.theme
+  pk: 75
+- fields: {name: "G\u0142upiec"}
+  model: wiki.theme
+  pk: 68
+- fields: {name: "G\u0142upota"}
+  model: wiki.theme
+  pk: 69
+- fields: {name: "G\u0142\xF3d"}
+  model: wiki.theme
+  pk: 70
+- fields: {name: Handel}
+  model: wiki.theme
+  pk: 81
+- fields: {name: "Ha\u0144ba"}
+  model: wiki.theme
+  pk: 82
+- fields: {name: Historia}
+  model: wiki.theme
+  pk: 83
+- fields: {name: Honor}
+  model: wiki.theme
+  pk: 84
+- fields: {name: Idealista}
+  model: wiki.theme
+  pk: 85
+- fields: {name: "Imi\u0119"}
+  model: wiki.theme
+  pk: 86
+- fields: {name: Interes}
+  model: wiki.theme
+  pk: 87
+- fields: {name: "Jab\u0142ka"}
+  model: wiki.theme
+  pk: 88
+- fields: {name: Jedzenie}
+  model: wiki.theme
+  pk: 89
+- fields: {name: "Jesie\u0144"}
+  model: wiki.theme
+  pk: 90
+- fields: {name: Kaleka}
+  model: wiki.theme
+  pk: 91
+- fields: {name: Kara}
+  model: wiki.theme
+  pk: 92
+- fields: {name: Karczma}
+  model: wiki.theme
+  pk: 93
+- fields: {name: "Kl\u0119ska"}
+  model: wiki.theme
+  pk: 94
+- fields: {name: Kobieta}
+  model: wiki.theme
+  pk: 97
+- fields: {name: "Kobieta \"upad\u0142a\""}
+  model: wiki.theme
+  pk: 99
+- fields: {name: Kobieta demoniczna}
+  model: wiki.theme
+  pk: 98
+- fields: {name: Kochanek}
+  model: wiki.theme
+  pk: 100
+- fields: {name: Kochanek romantyczny}
+  model: wiki.theme
+  pk: 101
+- fields: {name: Kolonializm}
+  model: wiki.theme
+  pk: 102
+- fields: {name: Kondycja ludzka}
+  model: wiki.theme
+  pk: 103
+- fields: {name: Konflikt}
+  model: wiki.theme
+  pk: 104
+- fields: {name: "Konflikt wewn\u0119trzny"}
+  model: wiki.theme
+  pk: 105
+- fields: {name: "Koniec \u015Bwiata"}
+  model: wiki.theme
+  pk: 106
+- fields: {name: "Korzy\u015B\u0107"}
+  model: wiki.theme
+  pk: 108
+- fields: {name: Kot}
+  model: wiki.theme
+  pk: 109
+- fields: {name: "Ko\u0144"}
+  model: wiki.theme
+  pk: 107
+- fields: {name: "Kradzie\u017C"}
+  model: wiki.theme
+  pk: 110
+- fields: {name: Krew}
+  model: wiki.theme
+  pk: 111
+- fields: {name: Krzywda}
+  model: wiki.theme
+  pk: 113
+- fields: {name: "Kr\xF3l"}
+  model: wiki.theme
+  pk: 112
+- fields: {name: "Ksi\u0105dz"}
+  model: wiki.theme
+  pk: 114
+- fields: {name: "Ksi\u0105\u017Cka"}
+  model: wiki.theme
+  pk: 115
+- fields: {name: "Ksi\u0119\u017Cyc"}
+  model: wiki.theme
+  pk: 116
+- fields: {name: Kuchnia}
+  model: wiki.theme
+  pk: 117
+- fields: {name: Kuszenie}
+  model: wiki.theme
+  pk: 118
+- fields: {name: Kwiaty}
+  model: wiki.theme
+  pk: 119
+- fields: {name: "K\u0142amstwo"}
+  model: wiki.theme
+  pk: 95
+- fields: {name: "K\u0142\xF3tnia"}
+  model: wiki.theme
+  pk: 96
+- fields: {name: Labirynt}
+  model: wiki.theme
+  pk: 120
+- fields: {name: Las}
+  model: wiki.theme
+  pk: 121
+- fields: {name: Lato}
+  model: wiki.theme
+  pk: 122
+- fields: {name: Lekarz}
+  model: wiki.theme
+  pk: 123
+- fields: {name: Lenistwo}
+  model: wiki.theme
+  pk: 124
+- fields: {name: Liberat}
+  model: wiki.theme
+  pk: 126
+- fields: {name: List}
+  model: wiki.theme
+  pk: 125
+- fields: {name: Los}
+  model: wiki.theme
+  pk: 127
+- fields: {name: Lud}
+  model: wiki.theme
+  pk: 128
+- fields: {name: Lustro}
+  model: wiki.theme
+  pk: 129
+- fields: {name: Marzenie}
+  model: wiki.theme
+  pk: 132
+- fields: {name: Maska}
+  model: wiki.theme
+  pk: 133
+- fields: {name: Maszyna}
+  model: wiki.theme
+  pk: 134
+- fields: {name: Matka}
+  model: wiki.theme
+  pk: 135
+- fields: {name: Matka Boska}
+  model: wiki.theme
+  pk: 136
+- fields: {name: "Ma\u0142\u017Ce\u0144stwo"}
+  model: wiki.theme
+  pk: 131
+- fields: {name: Melancholia}
+  model: wiki.theme
+  pk: 139
+- fields: {name: Miasto}
+  model: wiki.theme
+  pk: 142
+- fields: {name: Mieszczanin}
+  model: wiki.theme
+  pk: 143
+- fields: {name: Mizoginia}
+  model: wiki.theme
+  pk: 152
+- fields: {name: "Mi\u0142osierdzie"}
+  model: wiki.theme
+  pk: 144
+- fields: {name: "Mi\u0142o\u015B\u0107"}
+  model: wiki.theme
+  pk: 145
+- fields: {name: "Mi\u0142o\u015B\u0107 niespe\u0142niona"}
+  model: wiki.theme
+  pk: 146
+- fields: {name: "Mi\u0142o\u015B\u0107 platoniczna"}
+  model: wiki.theme
+  pk: 147
+- fields: {name: "Mi\u0142o\u015B\u0107 romantyczna"}
+  model: wiki.theme
+  pk: 148
+- fields: {name: "Mi\u0142o\u015B\u0107 silniejsza ni\u017C \u015Bmier\u0107"}
+  model: wiki.theme
+  pk: 149
+- fields: {name: "Mi\u0142o\u015B\u0107 spe\u0142niona"}
+  model: wiki.theme
+  pk: 150
+- fields: {name: "Mi\u0142o\u015B\u0107 tragiczna"}
+  model: wiki.theme
+  pk: 151
+- fields: {name: Moda}
+  model: wiki.theme
+  pk: 154
+- fields: {name: Modlitwa}
+  model: wiki.theme
+  pk: 155
+- fields: {name: Morderstwo}
+  model: wiki.theme
+  pk: 156
+- fields: {name: Morze}
+  model: wiki.theme
+  pk: 157
+- fields: {name: Motyl}
+  model: wiki.theme
+  pk: 158
+- fields: {name: Mucha}
+  model: wiki.theme
+  pk: 159
+- fields: {name: Muzyka}
+  model: wiki.theme
+  pk: 160
+- fields: {name: "M\u0105dro\u015B\u0107"}
+  model: wiki.theme
+  pk: 137
+- fields: {name: "M\u0105\u017C"}
+  model: wiki.theme
+  pk: 138
+- fields: {name: "M\u0119drzec"}
+  model: wiki.theme
+  pk: 140
+- fields: {name: "M\u0119\u017Cczyzna"}
+  model: wiki.theme
+  pk: 141
+- fields: {name: "M\u0142odo\u015B\u0107"}
+  model: wiki.theme
+  pk: 153
+- fields: {name: Narodziny}
+  model: wiki.theme
+  pk: 161
+- fields: {name: "Nar\xF3d"}
+  model: wiki.theme
+  pk: 162
+- fields: {name: Natura}
+  model: wiki.theme
+  pk: 163
+- fields: {name: Nauczyciel}
+  model: wiki.theme
+  pk: 164
+- fields: {name: Nauczycielka}
+  model: wiki.theme
+  pk: 165
+- fields: {name: Nauka}
+  model: wiki.theme
+  pk: 166
+- fields: {name: "Niebezpiecze\u0144stwo"}
+  model: wiki.theme
+  pk: 167
+- fields: {name: Niedziela}
+  model: wiki.theme
+  pk: 168
+- fields: {name: Niemiec}
+  model: wiki.theme
+  pk: 169
+- fields: {name: "Nienawi\u015B\u0107"}
+  model: wiki.theme
+  pk: 170
+- fields: {name: Niewola}
+  model: wiki.theme
+  pk: 172
+- fields: {name: "Nie\u015Bmiertelno\u015B\u0107"}
+  model: wiki.theme
+  pk: 171
+- fields: {name: Noc}
+  model: wiki.theme
+  pk: 173
+- fields: {name: Nuda}
+  model: wiki.theme
+  pk: 174
+- fields: {name: Obcy}
+  model: wiki.theme
+  pk: 175
+- fields: {name: "Obowi\u0105zek"}
+  model: wiki.theme
+  pk: 177
+- fields: {name: "Obraz \u015Bwiata"}
+  model: wiki.theme
+  pk: 178
+- fields: {name: "Obrz\u0119dy"}
+  model: wiki.theme
+  pk: 179
+- fields: {name: Obyczaje}
+  model: wiki.theme
+  pk: 180
+- fields: {name: Obywatel}
+  model: wiki.theme
+  pk: 181
+- fields: {name: "Ob\u0142ok"}
+  model: wiki.theme
+  pk: 176
+- fields: {name: "Odrodzenie przez gr\xF3b"}
+  model: wiki.theme
+  pk: 182
+- fields: {name: Odwaga}
+  model: wiki.theme
+  pk: 183
+- fields: {name: Ofiara}
+  model: wiki.theme
+  pk: 184
+- fields: {name: "Ogie\u0144"}
+  model: wiki.theme
+  pk: 185
+- fields: {name: "Ogr\xF3d"}
+  model: wiki.theme
+  pk: 186
+- fields: {name: Ojciec}
+  model: wiki.theme
+  pk: 187
+- fields: {name: Ojczyzna}
+  model: wiki.theme
+  pk: 188
+- fields: {name: Oko}
+  model: wiki.theme
+  pk: 189
+- fields: {name: "Okrucie\u0144stwo"}
+  model: wiki.theme
+  pk: 191
+- fields: {name: "Okr\u0119t"}
+  model: wiki.theme
+  pk: 190
+- fields: {name: Omen}
+  model: wiki.theme
+  pk: 192
+- fields: {name: Opieka}
+  model: wiki.theme
+  pk: 193
+- fields: {name: Organizm}
+  model: wiki.theme
+  pk: 194
+- fields: {name: "Otch\u0142a\u0144"}
+  model: wiki.theme
+  pk: 195
+- fields: {name: "O\u015Bwiadczyny"}
+  model: wiki.theme
+  pk: 415
+- fields: {name: "Paj\u0105k"}
+  model: wiki.theme
+  pk: 196
+- fields: {name: "Pami\u0119\u0107"}
+  model: wiki.theme
+  pk: 197
+- fields: {name: Pan}
+  model: wiki.theme
+  pk: 198
+- fields: {name: "Panna m\u0142oda"}
+  model: wiki.theme
+  pk: 199
+- fields: {name: Patriota}
+  model: wiki.theme
+  pk: 201
+- fields: {name: "Pa\u0144stwo"}
+  model: wiki.theme
+  pk: 200
+- fields: {name: "Piek\u0142o"}
+  model: wiki.theme
+  pk: 202
+- fields: {name: Pielgrzym}
+  model: wiki.theme
+  pk: 203
+- fields: {name: "Pieni\u0105dz"}
+  model: wiki.theme
+  pk: 204
+- fields: {name: Pies}
+  model: wiki.theme
+  pk: 205
+- fields: {name: "Pija\u0144stwo"}
+  model: wiki.theme
+  pk: 207
+- fields: {name: Piwnica}
+  model: wiki.theme
+  pk: 208
+- fields: {name: "Pi\u0119tno"}
+  model: wiki.theme
+  pk: 206
+- fields: {name: Plotka}
+  model: wiki.theme
+  pk: 209
+- fields: {name: "Pobo\u017Cno\u015B\u0107"}
+  model: wiki.theme
+  pk: 210
+- fields: {name: "Poca\u0142unek"}
+  model: wiki.theme
+  pk: 211
+- fields: {name: Pochlebstwo}
+  model: wiki.theme
+  pk: 212
+- fields: {name: "Podr\xF3\u017C"}
+  model: wiki.theme
+  pk: 216
+- fields: {name: "Podst\u0119p"}
+  model: wiki.theme
+  pk: 217
+- fields: {name: Poeta}
+  model: wiki.theme
+  pk: 213
+- fields: {name: Poetka}
+  model: wiki.theme
+  pk: 214
+- fields: {name: Poezja}
+  model: wiki.theme
+  pk: 215
+- fields: {name: Pogrzeb}
+  model: wiki.theme
+  pk: 218
+- fields: {name: Pojedynek}
+  model: wiki.theme
+  pk: 219
+- fields: {name: Pokora}
+  model: wiki.theme
+  pk: 220
+- fields: {name: Pokusa}
+  model: wiki.theme
+  pk: 221
+- fields: {name: Polak}
+  model: wiki.theme
+  pk: 222
+- fields: {name: Polityka}
+  model: wiki.theme
+  pk: 223
+- fields: {name: Polowanie}
+  model: wiki.theme
+  pk: 224
+- fields: {name: Polska}
+  model: wiki.theme
+  pk: 225
+- fields: {name: Portret}
+  model: wiki.theme
+  pk: 226
+- fields: {name: Porwanie}
+  model: wiki.theme
+  pk: 227
+- fields: {name: "Potw\xF3r"}
+  model: wiki.theme
+  pk: 229
+- fields: {name: Powstanie}
+  model: wiki.theme
+  pk: 230
+- fields: {name: Powstaniec}
+  model: wiki.theme
+  pk: 231
+- fields: {name: Pozory}
+  model: wiki.theme
+  pk: 232
+- fields: {name: "Pozycja spo\u0142eczna"}
+  model: wiki.theme
+  pk: 233
+- fields: {name: "Po\u015Bwi\u0119cenie"}
+  model: wiki.theme
+  pk: 228
+- fields: {name: "Po\u017Car"}
+  model: wiki.theme
+  pk: 234
+- fields: {name: "Po\u017C\u0105danie"}
+  model: wiki.theme
+  pk: 235
+- fields: {name: Praca}
+  model: wiki.theme
+  pk: 236
+- fields: {name: Praca organiczna}
+  model: wiki.theme
+  pk: 238
+- fields: {name: Praca u podstaw}
+  model: wiki.theme
+  pk: 237
+- fields: {name: Prawda}
+  model: wiki.theme
+  pk: 239
+- fields: {name: Prawnik}
+  model: wiki.theme
+  pk: 240
+- fields: {name: Prometeusz}
+  model: wiki.theme
+  pk: 241
+- fields: {name: Proroctwo}
+  model: wiki.theme
+  pk: 242
+- fields: {name: Prorok}
+  model: wiki.theme
+  pk: 243
+- fields: {name: Przebranie}
+  model: wiki.theme
+  pk: 245
+- fields: {name: Przeczucie}
+  model: wiki.theme
+  pk: 246
+- fields: {name: "Przedmurze chrze\u015Bcija\u0144stwa"}
+  model: wiki.theme
+  pk: 247
+- fields: {name: "Przekle\u0144stwo"}
+  model: wiki.theme
+  pk: 248
+- fields: {name: Przekupstwo}
+  model: wiki.theme
+  pk: 249
+- fields: {name: Przemiana}
+  model: wiki.theme
+  pk: 250
+- fields: {name: Przemijanie}
+  model: wiki.theme
+  pk: 251
+- fields: {name: Przemoc}
+  model: wiki.theme
+  pk: 252
+- fields: {name: "Przestrze\u0144"}
+  model: wiki.theme
+  pk: 253
+- fields: {name: "Przyja\u017A\u0144"}
+  model: wiki.theme
+  pk: 254
+- fields: {name: "Przyroda nieo\u017Cywiona"}
+  model: wiki.theme
+  pk: 255
+- fields: {name: "Przysi\u0119ga"}
+  model: wiki.theme
+  pk: 256
+- fields: {name: "Przyw\xF3dca"}
+  model: wiki.theme
+  pk: 257
+- fields: {name: "Pr\xF3\u017Cno\u015B\u0107"}
+  model: wiki.theme
+  pk: 244
+- fields: {name: Ptak}
+  model: wiki.theme
+  pk: 258
+- fields: {name: Pustynia}
+  model: wiki.theme
+  pk: 259
+- fields: {name: Pycha}
+  model: wiki.theme
+  pk: 260
+- fields: {name: Raj}
+  model: wiki.theme
+  pk: 261
+- fields: {name: Realista}
+  model: wiki.theme
+  pk: 262
+- fields: {name: Religia}
+  model: wiki.theme
+  pk: 263
+- fields: {name: Rewolucja}
+  model: wiki.theme
+  pk: 264
+- fields: {name: Robak}
+  model: wiki.theme
+  pk: 265
+- fields: {name: Robotnik}
+  model: wiki.theme
+  pk: 266
+- fields: {name: Rodzina}
+  model: wiki.theme
+  pk: 267
+- fields: {name: Rosja}
+  model: wiki.theme
+  pk: 268
+- fields: {name: Rosjanin}
+  model: wiki.theme
+  pk: 269
+- fields: {name: Rozczarowanie}
+  model: wiki.theme
+  pk: 271
+- fields: {name: Rozpacz}
+  model: wiki.theme
+  pk: 272
+- fields: {name: Rozstanie}
+  model: wiki.theme
+  pk: 273
+- fields: {name: Rozum}
+  model: wiki.theme
+  pk: 274
+- fields: {name: "Ro\u015Bliny"}
+  model: wiki.theme
+  pk: 270
+- fields: {name: Ruiny}
+  model: wiki.theme
+  pk: 275
+- fields: {name: Rycerz}
+  model: wiki.theme
+  pk: 276
+- fields: {name: Rzeka}
+  model: wiki.theme
+  pk: 277
+- fields: {name: Salon}
+  model: wiki.theme
+  pk: 278
+- fields: {name: "Samob\xF3jstwo"}
+  model: wiki.theme
+  pk: 279
+- fields: {name: Samolubstwo}
+  model: wiki.theme
+  pk: 280
+- fields: {name: Samotnik}
+  model: wiki.theme
+  pk: 281
+- fields: {name: "Samotno\u015B\u0107"}
+  model: wiki.theme
+  pk: 282
+- fields: {name: Sarmata}
+  model: wiki.theme
+  pk: 283
+- fields: {name: Sen}
+  model: wiki.theme
+  pk: 287
+- fields: {name: Serce}
+  model: wiki.theme
+  pk: 288
+- fields: {name: Sielanka}
+  model: wiki.theme
+  pk: 290
+- fields: {name: Sierota}
+  model: wiki.theme
+  pk: 291
+- fields: {name: Siostra}
+  model: wiki.theme
+  pk: 293
+- fields: {name: "Si\u0142a"}
+  model: wiki.theme
+  pk: 292
+- fields: {name: "Sk\u0105piec"}
+  model: wiki.theme
+  pk: 299
+- fields: {name: "Sobowt\xF3r"}
+  model: wiki.theme
+  pk: 300
+- fields: {name: "Spowied\u017A"}
+  model: wiki.theme
+  pk: 302
+- fields: {name: "Spo\u0142ecznik"}
+  model: wiki.theme
+  pk: 301
+- fields: {name: "Sprawiedliwo\u015B\u0107"}
+  model: wiki.theme
+  pk: 303
+- fields: {name: "Staro\u015B\u0107"}
+  model: wiki.theme
+  pk: 304
+- fields: {name: Strach}
+  model: wiki.theme
+  pk: 305
+- fields: {name: "Str\xF3j"}
+  model: wiki.theme
+  pk: 306
+- fields: {name: Stworzenie}
+  model: wiki.theme
+  pk: 307
+- fields: {name: Sumienie}
+  model: wiki.theme
+  pk: 308
+- fields: {name: Swaty}
+  model: wiki.theme
+  pk: 309
+- fields: {name: Syberia}
+  model: wiki.theme
+  pk: 310
+- fields: {name: Syn}
+  model: wiki.theme
+  pk: 311
+- fields: {name: Syn marnotrawny}
+  model: wiki.theme
+  pk: 312
+- fields: {name: Syzyf}
+  model: wiki.theme
+  pk: 313
+- fields: {name: Szaleniec}
+  model: wiki.theme
+  pk: 314
+- fields: {name: "Szale\u0144stwo"}
+  model: wiki.theme
+  pk: 315
+- fields: {name: "Szanta\u017C"}
+  model: wiki.theme
+  pk: 316
+- fields: {name: Szatan}
+  model: wiki.theme
+  pk: 317
+- fields: {name: "Szcz\u0119\u015Bcie"}
+  model: wiki.theme
+  pk: 318
+- fields: {name: "Szko\u0142a"}
+  model: wiki.theme
+  pk: 319
+- fields: {name: Szlachcic}
+  model: wiki.theme
+  pk: 320
+- fields: {name: Szpieg}
+  model: wiki.theme
+  pk: 321
+- fields: {name: Sztuka}
+  model: wiki.theme
+  pk: 322
+- fields: {name: "S\u0105d"}
+  model: wiki.theme
+  pk: 285
+- fields: {name: "S\u0105d Ostateczny"}
+  model: wiki.theme
+  pk: 286
+- fields: {name: "S\u0105siad"}
+  model: wiki.theme
+  pk: 284
+- fields: {name: "S\u0119dzia"}
+  model: wiki.theme
+  pk: 289
+- fields: {name: "S\u0142awa"}
+  model: wiki.theme
+  pk: 294
+- fields: {name: "S\u0142owo"}
+  model: wiki.theme
+  pk: 296
+- fields: {name: "S\u0142o\u0144ce"}
+  model: wiki.theme
+  pk: 295
+- fields: {name: "S\u0142uga"}
+  model: wiki.theme
+  pk: 297
+- fields: {name: "S\u0142u\u017Calczo\u015B\u0107"}
+  model: wiki.theme
+  pk: 298
+- fields: {name: Tajemnica}
+  model: wiki.theme
+  pk: 332
+- fields: {name: Taniec}
+  model: wiki.theme
+  pk: 333
+- fields: {name: "Tch\xF3rzostwo"}
+  model: wiki.theme
+  pk: 334
+- fields: {name: Teatr}
+  model: wiki.theme
+  pk: 335
+- fields: {name: Testament}
+  model: wiki.theme
+  pk: 336
+- fields: {name: Theatrum mundi}
+  model: wiki.theme
+  pk: 338
+- fields: {name: Trucizna}
+  model: wiki.theme
+  pk: 340
+- fields: {name: Trup}
+  model: wiki.theme
+  pk: 341
+- fields: {name: "Tw\xF3rczo\u015B\u0107"}
+  model: wiki.theme
+  pk: 342
+- fields: {name: "T\u0119sknota"}
+  model: wiki.theme
+  pk: 337
+- fields: {name: "T\u0142um"}
+  model: wiki.theme
+  pk: 339
+- fields: {name: "Ucze\u0144"}
+  model: wiki.theme
+  pk: 343
+- fields: {name: Uczta}
+  model: wiki.theme
+  pk: 344
+- fields: {name: Umiarkowanie}
+  model: wiki.theme
+  pk: 346
+- fields: {name: Upadek}
+  model: wiki.theme
+  pk: 347
+- fields: {name: "Upi\xF3r"}
+  model: wiki.theme
+  pk: 348
+- fields: {name: Uroda}
+  model: wiki.theme
+  pk: 345
+- fields: {name: "Urz\u0119dnik"}
+  model: wiki.theme
+  pk: 349
+- fields: {name: Vanitas}
+  model: wiki.theme
+  pk: 350
+- fields: {name: Walka}
+  model: wiki.theme
+  pk: 351
+- fields: {name: Walka klas}
+  model: wiki.theme
+  pk: 352
+- fields: {name: Wampir}
+  model: wiki.theme
+  pk: 353
+- fields: {name: Warszawa}
+  model: wiki.theme
+  pk: 354
+- fields: {name: Wdowa}
+  model: wiki.theme
+  pk: 356
+- fields: {name: Wdowiec}
+  model: wiki.theme
+  pk: 357
+- fields: {name: Wesele}
+  model: wiki.theme
+  pk: 358
+- fields: {name: Wiatr}
+  model: wiki.theme
+  pk: 359
+- fields: {name: Wiedza}
+  model: wiki.theme
+  pk: 363
+- fields: {name: "Wierno\u015B\u0107"}
+  model: wiki.theme
+  pk: 360
+- fields: {name: Wierzenia}
+  model: wiki.theme
+  pk: 361
+- fields: {name: "Wie\u015B"}
+  model: wiki.theme
+  pk: 362
+- fields: {name: "Wie\u017Ca Babel"}
+  model: wiki.theme
+  pk: 364
+- fields: {name: Wina}
+  model: wiki.theme
+  pk: 367
+- fields: {name: Wino}
+  model: wiki.theme
+  pk: 368
+- fields: {name: Wiosna}
+  model: wiki.theme
+  pk: 369
+- fields: {name: Wizja}
+  model: wiki.theme
+  pk: 370
+- fields: {name: "Wi\u0119zienie"}
+  model: wiki.theme
+  pk: 365
+- fields: {name: "Wi\u0119zie\u0144"}
+  model: wiki.theme
+  pk: 366
+- fields: {name: Woda}
+  model: wiki.theme
+  pk: 373
+- fields: {name: Wojna}
+  model: wiki.theme
+  pk: 374
+- fields: {name: "Wojna pokole\u0144"}
+  model: wiki.theme
+  pk: 375
+- fields: {name: "Wolno\u015B\u0107"}
+  model: wiki.theme
+  pk: 376
+- fields: {name: "Wr\xF3g"}
+  model: wiki.theme
+  pk: 377
+- fields: {name: Wspomnienia}
+  model: wiki.theme
+  pk: 378
+- fields: {name: "Wsp\xF3\u0142praca"}
+  model: wiki.theme
+  pk: 379
+- fields: {name: Wygnanie}
+  model: wiki.theme
+  pk: 380
+- fields: {name: Wyrzuty sumienia}
+  model: wiki.theme
+  pk: 381
+- fields: {name: Wyspa}
+  model: wiki.theme
+  pk: 382
+- fields: {name: Wzrok}
+  model: wiki.theme
+  pk: 383
+- fields: {name: "W\u0105\u017C"}
+  model: wiki.theme
+  pk: 355
+- fields: {name: "W\u0142adza"}
+  model: wiki.theme
+  pk: 371
+- fields: {name: "W\u0142asno\u015B\u0107"}
+  model: wiki.theme
+  pk: 372
+- fields: {name: Zabawa}
+  model: wiki.theme
+  pk: 384
+- fields: {name: Zabobony}
+  model: wiki.theme
+  pk: 385
+- fields: {name: Zamek}
+  model: wiki.theme
+  pk: 386
+- fields: {name: "Zar\u0119czyny"}
+  model: wiki.theme
+  pk: 387
+- fields: {name: "Zazdro\u015B\u0107"}
+  model: wiki.theme
+  pk: 389
+- fields: {name: "Za\u015Bwiaty"}
+  model: wiki.theme
+  pk: 388
+- fields: {name: Zbawienie}
+  model: wiki.theme
+  pk: 390
+- fields: {name: Zbrodnia}
+  model: wiki.theme
+  pk: 391
+- fields: {name: Zbrodniarz}
+  model: wiki.theme
+  pk: 392
+- fields: {name: Zdrada}
+  model: wiki.theme
+  pk: 393
+- fields: {name: Zdrowie}
+  model: wiki.theme
+  pk: 394
+- fields: {name: Zemsta}
+  model: wiki.theme
+  pk: 395
+- fields: {name: "Zes\u0142aniec"}
+  model: wiki.theme
+  pk: 396
+- fields: {name: Ziarno}
+  model: wiki.theme
+  pk: 397
+- fields: {name: Ziemia}
+  model: wiki.theme
+  pk: 398
+- fields: {name: Zima}
+  model: wiki.theme
+  pk: 399
+- fields: {name: Zmartwychwstanie}
+  model: wiki.theme
+  pk: 403
+- fields: {name: "Zwierz\u0119ta"}
+  model: wiki.theme
+  pk: 405
+- fields: {name: "Zwyci\u0119stwo"}
+  model: wiki.theme
+  pk: 406
+- fields: {name: "Zw\u0105tpienie"}
+  model: wiki.theme
+  pk: 404
+- fields: {name: "Z\u0142o"}
+  model: wiki.theme
+  pk: 400
+- fields: {name: "Z\u0142odziej"}
+  model: wiki.theme
+  pk: 401
+- fields: {name: "Z\u0142oty wiek"}
+  model: wiki.theme
+  pk: 402
+- fields: {name: "\u0141zy"}
+  model: wiki.theme
+  pk: 130
+- fields: {name: "\u015Alub"}
+  model: wiki.theme
+  pk: 323
+- fields: {name: "\u015Amiech"}
+  model: wiki.theme
+  pk: 324
+- fields: {name: "\u015Amier\u0107"}
+  model: wiki.theme
+  pk: 325
+- fields: {name: "\u015Amier\u0107 bohaterska"}
+  model: wiki.theme
+  pk: 326
+- fields: {name: "\u015Apiew"}
+  model: wiki.theme
+  pk: 327
+- fields: {name: "\u015Awiat\u0142o"}
+  model: wiki.theme
+  pk: 328
+- fields: {name: "\u015Awit"}
+  model: wiki.theme
+  pk: 331
+- fields: {name: "\u015Awi\u0119toszek"}
+  model: wiki.theme
+  pk: 329
+- fields: {name: "\u015Awi\u0119ty"}
+  model: wiki.theme
+  pk: 330
+- fields: {name: "\u017Ba\u0142oba"}
+  model: wiki.theme
+  pk: 407
+- fields: {name: "\u017Bebrak"}
+  model: wiki.theme
+  pk: 408
+- fields: {name: "\u017Bona"}
+  model: wiki.theme
+  pk: 410
+- fields: {name: "\u017Bo\u0142nierz"}
+  model: wiki.theme
+  pk: 409
+- fields: {name: "\u017Bycie jako w\u0119dr\xF3wka"}
+  model: wiki.theme
+  pk: 411
+- fields: {name: "\u017Bycie snem"}
+  model: wiki.theme
+  pk: 412
+- fields: {name: "\u017Byd"}
+  model: wiki.theme
+  pk: 413
+- fields: {name: "\u017Bywio\u0142y"}
+  model: wiki.theme
+  pk: 414
+
diff --git a/src/wiki/forms.py b/src/wiki/forms.py
new file mode 100644 (file)
index 0000000..3ef3ed1
--- /dev/null
@@ -0,0 +1,104 @@
+# -*- coding: utf-8 -*-
+#
+# This file is part of FNP-Redakcja, 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 catalogue.models import Chunk
+
+
+class DocumentPubmarkForm(forms.Form):
+    """
+        Form for marking revisions for publishing.
+    """
+
+    id = forms.CharField(widget=forms.HiddenInput)
+    publishable = forms.BooleanField(required=False, initial=True,
+            label=_('Publishable'))
+    revision = forms.IntegerField(widget=forms.HiddenInput)
+
+
+class DocumentTextSaveForm(forms.Form):
+    """
+    Form for saving document's text:
+
+        * parent_revision - revision which the modified text originated from.
+        * comment - user's verbose comment; will be used in commit.
+        * stage_completed - mark this change as end of given stage.
+
+    """
+
+    parent_revision = forms.IntegerField(widget=forms.HiddenInput, required=False)
+    text = forms.CharField(widget=forms.HiddenInput)
+
+    author_name = forms.CharField(
+        required=True,
+        label=_(u"Author"),
+        help_text=_(u"Your name"),
+    )
+
+    author_email = forms.EmailField(
+        required=True,
+        label=_(u"Author's email"),
+        help_text=_(u"Your email address, so we can show a gravatar :)"),
+    )
+
+    comment = forms.CharField(
+        required=True,
+        widget=forms.Textarea,
+        label=_(u"Your comments"),
+        help_text=_(u"Describe changes you made."),
+    )
+
+    stage_completed = forms.ModelChoiceField(
+        queryset=Chunk.tag_model.objects.all(),
+        required=False,
+        label=_(u"Completed"),
+        help_text=_(u"If you completed a life cycle stage, select it."),
+    )
+
+    publishable = forms.BooleanField(required=False, initial=False,
+        label=_('Publishable'),
+        help_text=_(u"Mark this revision as publishable.")
+    )
+
+    def __init__(self, *args, **kwargs):
+        user = kwargs.pop('user')
+        r = super(DocumentTextSaveForm, self).__init__(*args, **kwargs)
+        if user and user.is_authenticated():
+            self.fields['author_name'].required = False
+            self.fields['author_email'].required = False
+        return r
+
+
+class DocumentTextRevertForm(forms.Form):
+    """
+    Form for reverting document's text:
+
+        * revision - revision to revert to.
+        * comment - user's verbose comment; will be used in commit.
+
+    """
+
+    revision = forms.IntegerField(widget=forms.HiddenInput)
+
+    author_name = forms.CharField(
+        required=False,
+        label=_(u"Author"),
+        help_text=_(u"Your name"),
+    )
+
+    author_email = forms.EmailField(
+        required=False,
+        label=_(u"Author's email"),
+        help_text=_(u"Your email address, so we can show a gravatar :)"),
+    )
+
+    comment = forms.CharField(
+        required=True,
+        widget=forms.Textarea,
+        label=_(u"Your comments"),
+        help_text=_(u"Describe the reason for reverting."),
+    )
diff --git a/src/wiki/helpers.py b/src/wiki/helpers.py
new file mode 100644 (file)
index 0000000..877a9d0
--- /dev/null
@@ -0,0 +1,61 @@
+from datetime import datetime
+from functools import wraps
+
+from django import http
+import json
+from django.utils.functional import Promise
+
+
+class ExtendedEncoder(json.JSONEncoder):
+
+    def default(self, obj):
+        if isinstance(obj, Promise):
+            return unicode(obj)
+
+        if isinstance(obj, datetime):
+            return datetime.ctime(obj) + " " + (datetime.tzname(obj) or 'GMT')
+
+        return json.JSONEncoder.default(self, obj)
+
+
+# shortcut for JSON reponses
+class JSONResponse(http.HttpResponse):
+
+    def __init__(self, data={}, **kwargs):
+        # get rid of content_type
+        kwargs.pop('content_type', None)
+
+        data = json.dumps(data, cls=ExtendedEncoder)
+        super(JSONResponse, self).__init__(data, content_type="application/json", **kwargs)
+
+
+# return errors
+class JSONFormInvalid(JSONResponse):
+    def __init__(self, form):
+        super(JSONFormInvalid, self).__init__(form.errors, status=400)
+
+
+class JSONServerError(JSONResponse):
+    def __init__(self, *args, **kwargs):
+        kwargs['status'] = 500
+        super(JSONServerError, self).__init__(*args, **kwargs)
+
+
+def ajax_login_required(view):
+    @wraps(view)
+    def authenticated_view(request, *args, **kwargs):
+        if not request.user.is_authenticated():
+            return http.HttpResponse("Login required.", status=401, content_type="text/plain")
+        return view(request, *args, **kwargs)
+    return authenticated_view
+
+
+def ajax_require_permission(permission):
+    def decorator(view):
+        @wraps(view)
+        def authorized_view(request, *args, **kwargs):
+            if not request.user.has_perm(permission):
+                return http.HttpResponse("Access Forbidden.", status=403, content_type="text/plain")
+            return view(request, *args, **kwargs)
+        return authorized_view
+    return decorator
diff --git a/src/wiki/locale/pl/LC_MESSAGES/django.mo b/src/wiki/locale/pl/LC_MESSAGES/django.mo
new file mode 100644 (file)
index 0000000..cc5b3a5
Binary files /dev/null and b/src/wiki/locale/pl/LC_MESSAGES/django.mo differ
diff --git a/src/wiki/locale/pl/LC_MESSAGES/django.po b/src/wiki/locale/pl/LC_MESSAGES/django.po
new file mode 100644 (file)
index 0000000..a679724
--- /dev/null
@@ -0,0 +1,470 @@
+# 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: Platforma Redakcyjna\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2013-07-10 16:58+0200\n"
+"PO-Revision-Date: 2013-07-10 16:58+0100\n"
+"Last-Translator: Radek Czajka <radoslaw.czajka@nowoczesnapolska.org.pl>\n"
+"Language-Team: Fundacja Nowoczesna Polska <fundacja@nowoczesnapolska.org."
+"pl>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Poedit 1.5.4\n"
+
+#: forms.py:19 forms.py:63 views.py:279
+msgid "Publishable"
+msgstr "Gotowe do publikacji"
+
+#: forms.py:38 forms.py:89
+msgid "Author"
+msgstr "Autor"
+
+#: forms.py:39 forms.py:90
+msgid "Your name"
+msgstr "Imię i nazwisko"
+
+#: forms.py:44 forms.py:95
+msgid "Author's email"
+msgstr "E-mail autora"
+
+#: forms.py:45 forms.py:96
+msgid "Your email address, so we can show a gravatar :)"
+msgstr "Adres e-mail, żebyśmy mogli pokazać gravatar :)"
+
+#: forms.py:51 forms.py:102
+msgid "Your comments"
+msgstr "Twój komentarz"
+
+#: forms.py:52
+msgid "Describe changes you made."
+msgstr "Opisz swoje zmiany"
+
+#: forms.py:58
+msgid "Completed"
+msgstr "Ukończono"
+
+#: forms.py:59
+msgid "If you completed a life cycle stage, select it."
+msgstr "Jeśli został ukończony etap prac, wskaż go."
+
+#: forms.py:64
+msgid "Mark this revision as publishable."
+msgstr "Oznacz tę wersję jako gotową do publikacji."
+
+#: forms.py:103
+msgid "Describe the reason for reverting."
+msgstr "Opisz powód przywrócenia."
+
+#: models.py:14
+msgid "name"
+msgstr "nazwa"
+
+#: models.py:18
+msgid "theme"
+msgstr "motyw"
+
+#: models.py:19
+msgid "themes"
+msgstr "motywy"
+
+#: views.py:281
+msgid "Published"
+msgstr "Opublikowano"
+
+#: views.py:302
+msgid "Revision marked"
+msgstr "Wersja oznaczona"
+
+#: views.py:304
+msgid "Nothing changed"
+msgstr "Nic nie uległo zmianie"
+
+#: templates/admin/wiki/theme/change_list.html:22
+msgid "Table for Redmine wiki"
+msgstr "Tabela do wiki na Redmine"
+
+#: templates/wiki/diff_table.html:5
+msgid "Old version"
+msgstr "Stara wersja"
+
+#: templates/wiki/diff_table.html:6
+msgid "New version"
+msgstr "Nowa wersja"
+
+#: templates/wiki/document_details.html:32
+msgid "Click to open/close gallery"
+msgstr "Kliknij, aby (ro)zwinąć galerię"
+
+#: templates/wiki/document_details_base.html:33
+msgid "Help"
+msgstr "Pomoc"
+
+#: templates/wiki/document_details_base.html:35
+msgid "Version"
+msgstr "Wersja"
+
+#: templates/wiki/document_details_base.html:35
+msgid "Unknown"
+msgstr "nieznana"
+
+#: templates/wiki/document_details_base.html:37
+#: templates/wiki/pubmark_dialog.html:16
+msgid "Save"
+msgstr "Zapisz"
+
+#: templates/wiki/document_details_base.html:38
+msgid "Save attempt in progress"
+msgstr "Trwa zapisywanie"
+
+#: templates/wiki/document_details_base.html:39
+msgid "There is a newer version of this document!"
+msgstr "Istnieje nowsza wersja tego dokumentu!"
+
+#: templates/wiki/pubmark_dialog.html:17 templates/wiki/revert_dialog.html:40
+msgid "Cancel"
+msgstr "Anuluj"
+
+#: templates/wiki/revert_dialog.html:39
+msgid "Revert"
+msgstr "Przywróć"
+
+#: templates/wiki/tabs/annotations_view.html:9
+msgid "all"
+msgstr "wszystkie"
+
+#: templates/wiki/tabs/annotations_view_item.html:3
+msgid "Annotations"
+msgstr "Przypisy"
+
+#: templates/wiki/tabs/gallery_view.html:5
+msgid "Go to first image of this part"
+msgstr "Przejdź na początek"
+
+#: templates/wiki/tabs/gallery_view.html:8
+msgid "Previous"
+msgstr "Poprzednie"
+
+#: templates/wiki/tabs/gallery_view.html:13
+msgid "Next"
+msgstr "Następne"
+
+#: templates/wiki/tabs/gallery_view.html:16
+msgid "Zoom in"
+msgstr "Powiększ"
+
+#: templates/wiki/tabs/gallery_view.html:17
+msgid "Zoom out"
+msgstr "Zmniejsz"
+
+#: templates/wiki/tabs/gallery_view_item.html:3
+msgid "Gallery"
+msgstr "Galeria"
+
+#: templates/wiki/tabs/history_view.html:5
+msgid "Compare versions"
+msgstr "Porównaj wersje"
+
+#: templates/wiki/tabs/history_view.html:8
+msgid "Mark for publishing"
+msgstr "Oznacz do publikacji"
+
+#: templates/wiki/tabs/history_view.html:11
+msgid "Revert document"
+msgstr "Przywróć wersję"
+
+#: templates/wiki/tabs/history_view.html:14
+msgid "View version"
+msgstr "Zobacz wersję"
+
+#: templates/wiki/tabs/history_view_item.html:3
+msgid "History"
+msgstr "Historia"
+
+#: templates/wiki/tabs/search_view.html:3
+#: templates/wiki/tabs/search_view.html:5
+msgid "Search"
+msgstr "Szukaj"
+
+#: templates/wiki/tabs/search_view.html:8
+msgid "Replace with"
+msgstr "Zamień na"
+
+#: templates/wiki/tabs/search_view.html:10
+msgid "Replace"
+msgstr "Zamień"
+
+#: templates/wiki/tabs/search_view.html:12
+msgid "Replace all"
+msgstr "Zamień wszystko"
+
+msgid "Options"
+msgstr "Opcje"
+
+#: templates/wiki/tabs/search_view.html:15
+msgid "Case sensitive"
+msgstr "Rozróżniaj wielkość liter"
+
+#: templates/wiki/tabs/search_view.html:17
+msgid "From cursor"
+msgstr "Zacznij od kursora"
+
+#: templates/wiki/tabs/search_view_item.html:3
+msgid "Search and replace"
+msgstr "Znajdź i zamień"
+
+#: templates/wiki/tabs/source_editor_item.html:5
+msgid "Source code"
+msgstr "Kod źródłowy"
+
+#: templates/wiki/tabs/summary_view.html:13
+msgid "Refresh from working copy"
+msgstr "Odśwież z edytowanej wersji"
+
+#: templates/wiki/tabs/summary_view.html:17
+msgid "Title"
+msgstr "Tytuł"
+
+#: templates/wiki/tabs/summary_view.html:21
+msgid "Go to the book's page"
+msgstr "Przejdź do strony książki"
+
+#: templates/wiki/tabs/summary_view.html:24
+msgid "Document ID"
+msgstr "ID dokumentu"
+
+#: templates/wiki/tabs/summary_view.html:28
+msgid "Current version"
+msgstr "Aktualna wersja"
+
+#: templates/wiki/tabs/summary_view.html:31
+msgid "Last edited by"
+msgstr "Ostatnio edytowane przez"
+
+#: templates/wiki/tabs/summary_view.html:35
+msgid "Link to gallery"
+msgstr "Link do galerii"
+
+#: templates/wiki/tabs/summary_view.html:40
+msgid "Characters in document"
+msgstr "Znaków w dokumencie"
+
+#: templates/wiki/tabs/summary_view.html:41
+msgid "pages"
+msgstr "stron maszynopisu"
+
+#: templates/wiki/tabs/summary_view.html:41
+msgid "untagged"
+msgstr "nieotagowane"
+
+#: templates/wiki/tabs/summary_view_item.html:3
+msgid "Summary"
+msgstr "Podsumowanie"
+
+#: templates/wiki/tabs/wysiwyg_editor.html:9
+msgid "Insert theme"
+msgstr "Wstaw motyw"
+
+#: templates/wiki/tabs/wysiwyg_editor.html:12
+msgid "Insert annotation"
+msgstr "Wstaw przypis"
+
+#: templates/wiki/tabs/wysiwyg_editor_item.html:3
+msgid "Visual editor"
+msgstr "Edytor wizualny"
+
+#~ msgid "ZIP file"
+#~ msgstr "Plik ZIP"
+
+#~ msgid "Chunk with this slug already exists"
+#~ msgstr "Część z tym slugiem już istnieje"
+
+#~ msgid "Append to"
+#~ msgstr "Dołącz do"
+
+#~ msgid "title"
+#~ msgstr "tytuł"
+
+#~ msgid "scan gallery name"
+#~ msgstr "nazwa galerii skanów"
+
+#~ msgid "parent"
+#~ msgstr "rodzic"
+
+#~ msgid "parent number"
+#~ msgstr "numeracja rodzica"
+
+#~ msgid "book"
+#~ msgstr "książka"
+
+#~ msgid "books"
+#~ msgstr "książki"
+
+#~ msgid "Slug already used for %s"
+#~ msgstr "Slug taki sam jak dla pliku %s"
+
+#~ msgid "Slug already used in repository."
+#~ msgstr "Dokument o tym slugu już istnieje w repozytorium."
+
+#~ msgid "File should be UTF-8 encoded."
+#~ msgstr "Plik powinien mieć kodowanie UTF-8."
+
+#~ msgid "Tag added"
+#~ msgstr "Dodano tag"
+
+#~ msgid "Append book"
+#~ msgstr "Dołącz książkę"
+
+#~ msgid "edit"
+#~ msgstr "edytuj"
+
+#~ msgid "add basic document structure"
+#~ msgstr "dodaj podstawową strukturę dokumentu"
+
+#~ msgid "change master tag to"
+#~ msgstr "zmień tak master na"
+
+#~ msgid "add begin trimming tag"
+#~ msgstr "dodaj początkowy ogranicznik"
+
+#~ msgid "add end trimming tag"
+#~ msgstr "dodaj końcowy ogranicznik"
+
+#~ msgid "unstructured text"
+#~ msgstr "tekst bez struktury"
+
+#~ msgid "unknown XML"
+#~ msgstr "nieznany XML"
+
+#~ msgid "broken document"
+#~ msgstr "uszkodzony dokument"
+
+#~ msgid "Apply fixes"
+#~ msgstr "Wykonaj zmiany"
+
+#~ msgid "Append to other book"
+#~ msgstr "Dołącz do innej książki"
+
+#~ msgid "Last published"
+#~ msgstr "Ostatnio opublikowano"
+
+#~ msgid "Full XML"
+#~ msgstr "Pełny XML"
+
+#~ msgid "HTML version"
+#~ msgstr "Wersja HTML"
+
+#~ msgid "TXT version"
+#~ msgstr "Wersja TXT"
+
+#~ msgid "EPUB version"
+#~ msgstr "Wersja EPUB"
+
+#~ msgid "PDF version"
+#~ msgstr "Wersja PDF"
+
+#~ msgid "This book cannot be published yet"
+#~ msgstr "Ta książka nie może jeszcze zostać opublikowana"
+
+#~ msgid "Add chunk"
+#~ msgstr "Dodaj część"
+
+#~ msgid "Clear filter"
+#~ msgstr "Wyczyść filtr"
+
+#~ msgid "No books found."
+#~ msgstr "Nie znaleziono książek."
+
+#~ msgid "Your last edited documents"
+#~ msgstr "Twoje ostatnie edycje"
+
+#~ msgid "Bulk documents upload"
+#~ msgstr "Hurtowe dodawanie dokumentów"
+
+#~ msgid ""
+#~ "Please submit a ZIP with UTF-8 encoded XML files. Files not ending with "
+#~ "<code>.xml</code> will be ignored."
+#~ msgstr ""
+#~ "Proszę wskazać archiwum ZIP z plikami XML w kodowaniu UTF-8. Pliki nie "
+#~ "kończące się na <code>.xml</code> zostaną zignorowane."
+
+#~ msgid "Upload"
+#~ msgstr "Załaduj"
+
+#~ msgid ""
+#~ "There have been some errors. No files have been added to the repository."
+#~ msgstr "Wystąpiły błędy. Żadne pliki nie zostały dodane do repozytorium."
+
+#~ msgid "Offending files"
+#~ msgstr "Błędne pliki"
+
+#~ msgid "Correct files"
+#~ msgstr "Poprawne pliki"
+
+#~ msgid "Files have been successfully uploaded to the repository."
+#~ msgstr "Pliki zostały dodane do repozytorium."
+
+#~ msgid "Uploaded files"
+#~ msgstr "Dodane pliki"
+
+#~ msgid "Skipped files"
+#~ msgstr "Pominięte pliki"
+
+#~ msgid "Files skipped due to no <code>.xml</code> extension"
+#~ msgstr "Pliki pominięte z powodu braku rozszerzenia <code>.xml</code>."
+
+#~ msgid "Users"
+#~ msgstr "Użytkownicy"
+
+#~ msgid "Assigned to me"
+#~ msgstr "Przypisane do mnie"
+
+#~ msgid "Unassigned"
+#~ msgstr "Nie przypisane"
+
+#~ msgid "All"
+#~ msgstr "Wszystkie"
+
+#~ msgid "Add"
+#~ msgstr "Dodaj"
+
+#~ msgid "Admin"
+#~ msgstr "Administracja"
+
+#~ msgid "First correction"
+#~ msgstr "Autokorekta"
+
+#~ msgid "Tagging"
+#~ msgstr "Tagowanie"
+
+#~ msgid "Initial Proofreading"
+#~ msgstr "Korekta"
+
+#~ msgid "Annotation Proofreading"
+#~ msgstr "Sprawdzenie przypisów źródła"
+
+#~ msgid "Modernisation"
+#~ msgstr "Uwspółcześnienie"
+
+#~ msgid "Themes"
+#~ msgstr "Motywy"
+
+#~ msgid "Editor's Proofreading"
+#~ msgstr "Ostateczna redakcja literacka"
+
+#~ msgid "Technical Editor's Proofreading"
+#~ msgstr "Ostateczna redakcja techniczna"
+
+#~ msgid "Finished stage: %s"
+#~ msgstr "Ukończony etap: %s"
+
+#~ msgid "Refresh"
+#~ msgstr "Odśwież"
+
+#~ msgid "Insert special character"
+#~ msgstr "Wstaw znak specjalny"
diff --git a/src/wiki/migrations/0001_initial.py b/src/wiki/migrations/0001_initial.py
new file mode 100644 (file)
index 0000000..4acf5ba
--- /dev/null
@@ -0,0 +1,21 @@
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+class Migration(SchemaMigration):
+    
+    def forwards(self, orm):
+        pass
+    
+    
+    def backwards(self, orm):
+        pass
+    
+    
+    models = {
+        
+    }
+    
+    complete_apps = ['wiki']
diff --git a/src/wiki/migrations/0002_auto__add_theme.py b/src/wiki/migrations/0002_auto__add_theme.py
new file mode 100644 (file)
index 0000000..6688139
--- /dev/null
@@ -0,0 +1,38 @@
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+class Migration(SchemaMigration):
+    
+    def forwards(self, orm):
+        
+        # Adding model 'Theme'
+        db.create_table('wiki_theme', (
+            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('name', self.gf('django.db.models.fields.CharField')(unique=True, max_length=50)),
+        ))
+        db.send_create_signal('wiki', ['Theme'])
+        
+        if not db.dry_run:
+            from django.core.management import call_command
+            call_command("loaddata", "initial_themes.yaml")
+
+    
+    
+    def backwards(self, orm):
+        
+        # Deleting model 'Theme'
+        db.delete_table('wiki_theme')
+    
+    
+    models = {
+        'wiki.theme': {
+            'Meta': {'object_name': 'Theme'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '50'})
+        }
+    }
+    
+    complete_apps = ['wiki']
diff --git a/src/wiki/migrations/__init__.py b/src/wiki/migrations/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/wiki/models.py b/src/wiki/models.py
new file mode 100644 (file)
index 0000000..c539908
--- /dev/null
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+#
+# This file is part of FNP-Redakcja, 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 _
+
+import logging
+logger = logging.getLogger("fnp.wiki")
+
+
+class Theme(models.Model):
+    name = models.CharField(_('name'), max_length=50, unique=True)
+
+    class Meta:
+        ordering = ('name',)
+        verbose_name = _('theme')
+        verbose_name_plural = _('themes')
+
+    def __unicode__(self):
+        return self.name
+
+    def __repr__(self):
+        return "Theme(name=%r)" % self.name
+
diff --git a/src/wiki/nice_diff.py b/src/wiki/nice_diff.py
new file mode 100644 (file)
index 0000000..b228fad
--- /dev/null
@@ -0,0 +1,61 @@
+# -*- coding: utf-8 -*-
+#
+# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
+import difflib
+import re
+from collections import deque
+
+from django.template.loader import render_to_string
+from django.utils.html import escape as html_escape
+
+DIFF_RE = re.compile(r"""\x00([+^-])""", re.UNICODE)
+NAMES = {'+': 'added', '-': 'removed', '^': 'changed'}
+
+
+def diff_replace(match):
+    return """<span class="diff_mark diff_mark_%s">""" % NAMES[match.group(1)]
+
+
+def filter_line(line):
+    return  DIFF_RE.sub(diff_replace, html_escape(line)).replace('\x01', '</span>')
+
+
+def format_changeset(a, b, change):
+    return (a[0], filter_line(a[1]), b[0], filter_line(b[1]), change)
+
+
+def html_diff_table(la, lb, context=None):
+    all_changes = difflib._mdiff(la, lb)
+
+    if context is None:
+        changes = (format_changeset(*c) for c in all_changes)
+    else:
+        changes = []
+        q = deque()
+        after_change = False
+
+        for changeset in all_changes:
+            q.append(changeset)
+
+            if changeset[2]:
+                after_change = True
+                if not after_change:
+                    changes.append((0, '-----', 0, '-----', False))
+                changes.extend(format_changeset(*c) for c in q)
+                q.clear()
+            else:
+                if len(q) == context and after_change:
+                    changes.extend(format_changeset(*c) for c in q)
+                    q.clear()
+                    after_change = False
+                elif len(q) > context:
+                    q.popleft()
+
+    return render_to_string("wiki/diff_table.html", {
+        "changes": changes,
+    })
+
+
+__all__ = ['html_diff_table']
diff --git a/src/wiki/settings.py b/src/wiki/settings.py
new file mode 100644 (file)
index 0000000..50f49d8
--- /dev/null
@@ -0,0 +1,3 @@
+from django.conf import settings
+
+GALLERY_URL = settings.MEDIA_URL + 'images/'
diff --git a/src/wiki/templates/admin/wiki/theme/change_list.html b/src/wiki/templates/admin/wiki/theme/change_list.html
new file mode 100755 (executable)
index 0000000..3e5d2ea
--- /dev/null
@@ -0,0 +1,29 @@
+{% extends "admin/change_list.html" %}
+{% load i18n %}
+
+{% block extrahead %}
+{{ block.super }}
+<script type="text/javascript">
+(function($) {
+    $(document).ready(function($) {
+        $("#redmine-table-switch").click(function() {
+            $('#redmine-table').toggle()
+        });
+    });
+})(django.jQuery);
+</script>
+{% endblock %}
+
+
+
+{% block pretitle %}
+
+
+<a id="redmine-table-switch">↓ {% trans "Table for Redmine wiki" %} ↓</a>
+<div id="redmine-table" style="display:none; padding:1em; border: 1px solid #aaa;">
+    |{% for theme in cl.get_queryset %}[[{{ theme }}]]|{% if forloop.counter|divisibleby:7 %}<br/>
+        |{% endif %}{% endfor %}
+</div>
+
+{{ block.super }}
+{% endblock %}
diff --git a/src/wiki/templates/wiki/diff_table.html b/src/wiki/templates/wiki/diff_table.html
new file mode 100644 (file)
index 0000000..818c38c
--- /dev/null
@@ -0,0 +1,21 @@
+{% load i18n %}
+<table class="diff_table">
+       <thead>
+               <tr>
+                       <th colspan="2">{% trans "Old version" %}</th>
+                       <th colspan="2">{% trans "New version" %}</th>
+               </tr>
+       </thead>
+<tbody>
+{% for an, a, bn, b, has_change in changes %}
+
+<tr class="{% if has_change %}change{% endif %}">
+<td>{{an}}</td>
+<td class="left">{{ a|safe }}&nbsp;</td>
+<td>{{bn}}</td>
+<td class="right">{{ b|safe }}&nbsp;</td>
+</tr>
+
+{% endfor %}
+</tbody>
+</table>
\ No newline at end of file
diff --git a/src/wiki/templates/wiki/document_details.html b/src/wiki/templates/wiki/document_details.html
new file mode 100644 (file)
index 0000000..2211c0a
--- /dev/null
@@ -0,0 +1,50 @@
+{% extends "wiki/document_details_base.html" %}
+{% load i18n %}
+
+{% block extrabody %}
+{{ block.super }}
+<script src="{{ STATIC_URL }}js/lib/codemirror-0.8/codemirror.js" type="text/javascript" charset="utf-8">
+</script>
+<script src="{{ STATIC_URL }}js/wiki/loader.js" type="text/javascript" charset="utf-8"> </script>
+{% endblock %}
+
+{% block tabs-menu %}
+    {% include "wiki/tabs/summary_view_item.html" %}
+    {% include "wiki/tabs/wysiwyg_editor_item.html" %}
+    {% include "wiki/tabs/source_editor_item.html" %}
+    {% include "wiki/tabs/history_view_item.html" %}
+{% endblock %}
+
+{% block tabs-content %}
+    {% include "wiki/tabs/summary_view.html" %}
+    {% include "wiki/tabs/wysiwyg_editor.html" %}
+    {% include "wiki/tabs/source_editor.html" %}
+    {% include "wiki/tabs/history_view.html" %}
+{% endblock %}
+
+{% block tabs-right %}
+    {% include "wiki/tabs/gallery_view_item.html" %}
+    {% include "wiki/tabs/annotations_view_item.html" %}
+    {% include "wiki/tabs/search_view_item.html" %}
+{% endblock %}
+
+{% block splitter-extra %}
+<div class="vsplitbar" title="{% trans "Click to open/close gallery" %}">
+    <p class="vsplitbar-title"></p>
+    </div>
+    <div id="sidebar">
+        {% include "wiki/tabs/gallery_view.html" %}
+        {% include "wiki/tabs/annotations_view.html" %}
+        {% include "wiki/tabs/search_view.html" %}
+    </div>
+    <div id="drag-layer"></div>
+{% endblock %}
+
+{% block dialogs %}
+    {% include "wiki/save_dialog.html" %}
+    {% include "wiki/revert_dialog.html" %}
+    {% include "wiki/tag_dialog.html" %}
+    {% if can_pubmark %}
+        {% include "wiki/pubmark_dialog.html" %}
+    {% endif %}
+{% endblock %}
diff --git a/src/wiki/templates/wiki/document_details_base.html b/src/wiki/templates/wiki/document_details_base.html
new file mode 100644 (file)
index 0000000..00c4f6a
--- /dev/null
@@ -0,0 +1,58 @@
+{% extends "base.html" %}
+{% load toolbar_tags i18n %}
+
+{% block titleextra %}{% if chunk.pretty_title %}{{ chunk.pretty_title }}{% else %}{{ chunk.book.title }}{% endif %}{% endblock %}
+{% block extrahead %}
+{% load pipeline %}
+{% stylesheet 'detail' %}
+{% endblock %}
+
+{% block extrabody %}
+<script type="text/javascript">
+    var STATIC_URL = '{{STATIC_URL}}';
+</script>
+{% javascript 'detail' %}
+{% endblock %}
+
+{% block maincontent %}
+<div id="document-meta"
+       data-chunk-id="{{ chunk.pk }}" style="display:none">
+
+       <span data-key="gallery">{{ chunk.book.gallery }}</span>
+       <span data-key="gallery-start">{% if chunk.gallery_start %}{{ chunk.gallery_start }}{% endif %}</span>
+       <span data-key="revision">{{ revision }}</span>
+    <span data-key="diff">{{ request.GET.diff }}</span>
+
+       {% block meta-extra %} {% endblock %}
+</div>
+
+<div id="header">
+    <h1><a href="{% url 'catalogue_document_list' %}"><img src="{{STATIC_URL}}icons/go-home.png"  alt="Home" /></a><a href="{% url 'catalogue_document_list' %}">Strona<br>główna</a></h1>
+    <div id="tools">
+        <a href="{{ REDMINE_URL }}projects/wl-publikacje/wiki/Pomoc" target="_blank">
+        {% trans "Help" %}</a>
+        | {% include "registration/head_login.html" %}
+        | {% trans "Version" %}: <span id="document-revision">{% trans "Unknown" %}</span>
+               {% if not readonly %}
+            | <button style="margin-left: 6px" id="save-button">{% trans "Save" %}</button>
+                       <span id='save-attempt-info'>{% trans "Save attempt in progress" %}</span>
+            <span id='out-of-date-info'>{% trans "There is a newer version of this document!" %}</span>
+               {% endif %}
+    </div>
+    <ol id="tabs" class="tabs">
+       {% block tabs-menu %} {% endblock %}
+    </ol>
+    <ol id="tabs-right" class="tabs">
+        {% block tabs-right %} {% endblock %}
+    </ol>
+</div>
+<div id="splitter">
+    <div id="editor" class="{% block editor-class %} {% endblock %}">
+       {% block tabs-content %} {% endblock %}
+    </div>
+       {% block splitter-extra %} {% endblock %}
+</div>
+
+{% block dialogs %} {% endblock %}
+
+{% endblock %}
diff --git a/src/wiki/templates/wiki/document_details_readonly.html b/src/wiki/templates/wiki/document_details_readonly.html
new file mode 100644 (file)
index 0000000..71556a1
--- /dev/null
@@ -0,0 +1,27 @@
+{% extends "wiki/document_details_base.html" %}
+{% load i18n %}
+
+{% block editor-class %}readonly{% endblock %}
+
+{% block extrabody %}
+{{ block.super }}
+<script src="{{STATIC_URL}}js/lib/codemirror-0.8/codemirror.js" type="text/javascript" charset="utf-8">
+</script>
+<script src="{{STATIC_URL}}js/wiki/loader_readonly.js" type="text/javascript" charset="utf-8"> </script>
+{% endblock %}
+
+{% block tabs-menu %}
+    {% include "wiki/tabs/wysiwyg_editor_item.html" %}
+       {% include "wiki/tabs/source_editor_item.html" %}
+{% endblock %}
+
+{% block tabs-content %}
+    {% include "wiki/tabs/wysiwyg_editor.html" %}
+       {% include "wiki/tabs/source_editor.html" %}
+{% endblock %}
+
+{% block splitter-extra %}
+{% endblock %}
+
+{% block dialogs %}
+{% endblock %}
\ No newline at end of file
diff --git a/src/wiki/templates/wiki/pubmark_dialog.html b/src/wiki/templates/wiki/pubmark_dialog.html
new file mode 100755 (executable)
index 0000000..a70a0c3
--- /dev/null
@@ -0,0 +1,20 @@
+{% load i18n %}
+<div id="pubmark_dialog" class="dialog" data-ui-jsclass="PubmarkDialog">
+       <form method="POST" action="#">
+    {% csrf_token %}
+               {% for field in forms.pubmark.visible_fields %}
+               <p>{{ field.label_tag }} {{ field }} <span data-ui-error-for="{{ field.name }}"> </span></p>
+               <p>{{ field.help_text }}</p>
+               {% endfor %}
+
+               {% for f in forms.pubmark.hidden_fields %}
+                       {{ f }}
+               {% endfor %}
+               <p data-ui-error-for="__all__"> </p>
+
+               <p class="action_area">
+                       <button type="submit" class="ok" data-ui-action="save">{% trans "Save" %}</button>
+                       <button type="button" class="cancel" data-ui-action="cancel">{% trans "Cancel" %}</button>
+               </p>
+       </form>
+</div>
diff --git a/src/wiki/templates/wiki/revert_dialog.html b/src/wiki/templates/wiki/revert_dialog.html
new file mode 100644 (file)
index 0000000..6f1793b
--- /dev/null
@@ -0,0 +1,43 @@
+{% load i18n %}
+<div id="revert_dialog" class="dialog" data-ui-jsclass="RevertDialog">
+       <form method="POST" action="#">
+    {% csrf_token %}
+       <p>{{ forms.text_revert.comment.label }}</p>
+       <p class="help_text">
+               {{ forms.text_revert.comment.help_text}}
+               <span data-ui-error-for="{{ forms.text_revert.comment.name }}"> </span>
+       </p>
+       {{forms.text_revert.comment }}
+
+
+
+       {% if request.user.is_anonymous %}
+    <table style='margin:0 4%;'>
+    <tr>
+        <td>{{ forms.text_revert.author_name.label }}:</td>
+        <td>{{ forms.text_revert.author_name }}
+        <span class="help_text">{{ forms.text_revert.author_name.help_text }}</span>
+        <span data-ui-error-for="{{ forms.text_revert.author_name.name }}"> </span></td>
+    </tr>
+    <tr>
+        <td>{{ forms.text_revert.author_email.label }}:</td>
+        <td>{{ forms.text_revert.author_email }}
+        <span class="help_text">{{ forms.text_revert.author_email.help_text }}</span>
+        <span data-ui-error-for="{{ forms.text_revert.author_email.name }}"> </span></td>
+    </tr>
+    </table>
+       {% endif %}
+
+
+       {% for f in forms.text_revert.hidden_fields %}
+               {{ f }}
+       {% endfor %}
+
+       <p data-ui-error-for="__all__"> </p>
+
+       <p class="action_area">
+               <button type="submit" class="ok" data-ui-action="revert">{% trans "Revert" %}</button>
+               <button type="button" class="cancel" data-ui-action="cancel">{% trans "Cancel" %}</button>
+       </p>
+       </form>
+</div>
diff --git a/src/wiki/templates/wiki/save_dialog.html b/src/wiki/templates/wiki/save_dialog.html
new file mode 100644 (file)
index 0000000..b133045
--- /dev/null
@@ -0,0 +1,59 @@
+{% load i18n %}
+<div id="save_dialog" class="dialog" data-ui-jsclass="SaveDialog">
+       <form method="POST" action="#">
+    {% csrf_token %}
+       <p>{{ forms.text_save.comment.label }}</p>
+       <p class="help_text">
+               {{ forms.text_save.comment.help_text}}
+               <span data-ui-error-for="{{ forms.text_save.comment.name }}"> </span>
+       </p>
+       {{forms.text_save.comment }}
+
+
+
+       {% if request.user.is_anonymous %}
+    <table style='margin:0 4%;'>
+    <tr>
+        <td>{{ forms.text_save.author_name.label }}:</td>
+        <td>{{ forms.text_save.author_name }}
+        <span class="help_text">{{ forms.text_save.author_name.help_text }}</span>
+        <span data-ui-error-for="{{ forms.text_save.author_name.name }}"> </span></td>
+    </tr>
+    <tr>
+        <td>{{ forms.text_save.author_email.label }}:</td>
+        <td>{{ forms.text_save.author_email }}
+        <span class="help_text">{{ forms.text_save.author_email.help_text }}</span>
+        <span data-ui-error-for="{{ forms.text_save.author_email.name }}"> </span></td>
+    </tr>
+    </table>
+       {% else %}
+       <p>
+               {{ forms.text_save.stage_completed.label }}:
+               {{ forms.text_save.stage_completed }}
+               <span class="help_text">{{ forms.text_save.stage_completed.help_text }}</span>
+               <span data-ui-error-for="{{ forms.text_save.stage_completed.name }}"> </span>
+       </p>
+    {% if can_pubmark %}
+       <p>
+               {{ forms.text_save.publishable.label_tag }}:
+               {{ forms.text_save.publishable }}
+               <span class="help_text">{{ forms.text_save.publishable.help_text }}</span>
+               <span data-ui-error-for="{{ forms.text_save.publishable.name }}"> </span>
+       </p>
+    {% endif %}
+
+       {% endif %}
+
+
+       {% for f in forms.text_save.hidden_fields %}
+               {{ f }}
+       {% endfor %}
+
+       <p data-ui-error-for="__all__"> </p>
+
+       <p class="action_area">
+               <button type="submit" class="ok" data-ui-action="save">Zapisz</button>
+               <button type="button" class="cancel" data-ui-action="cancel">Anuluj</button>
+       </p>
+       </form>
+</div>
diff --git a/src/wiki/templates/wiki/tabs/annotations_view.html b/src/wiki/templates/wiki/tabs/annotations_view.html
new file mode 100644 (file)
index 0000000..8e54242
--- /dev/null
@@ -0,0 +1,20 @@
+{% load i18n %}
+<div id="side-annotations">
+    <!-- annotations toolbar -->
+    <div class="toolbar">
+        <button class="refresh" title="Przypisy autorskie" data-tag="pa">pa</button>
+        <button class="refresh active" title="Przypisy edytorskie" data-tag="pe">pe</button>
+        <button class="refresh" title="Przypisy redakcyjne" data-tag="pr">pr</button>
+        <button class="refresh" title="Przypisy tłumacza" data-tag="pt">pt</button>
+        <button class="refresh" title="Wszystkie przypisy" data-tag="pa,pe,pr,pt">{% trans "all" %}</button>
+        <div class="toolbar-end">
+        </div>
+    </div>
+    <div class="error-message">
+    </div>
+    <div class="annotations-list">
+    </div>
+    <div class="spinner">
+        <img src='/media/static/img/spinner.gif' alt="Loading" />
+    </div>
+</div>
diff --git a/src/wiki/templates/wiki/tabs/annotations_view_item.html b/src/wiki/templates/wiki/tabs/annotations_view_item.html
new file mode 100644 (file)
index 0000000..7f17ce5
--- /dev/null
@@ -0,0 +1,4 @@
+{% load i18n %}
+<li id="AnnotationsPerspective" data-ui-related="side-annotations" data-ui-jsclass="AnnotationsPerspective">
+    <a href="#"><sup title="{% trans "Annotations" %}">[1]</sup></a>
+</li>
diff --git a/src/wiki/templates/wiki/tabs/gallery_view.html b/src/wiki/templates/wiki/tabs/gallery_view.html
new file mode 100644 (file)
index 0000000..4e57ea5
--- /dev/null
@@ -0,0 +1,27 @@
+{% load i18n %}
+<div id="side-gallery">
+    <!-- gallery toolbar -->
+    <div class="toolbar">
+        <button class="start-page" alt="{% trans "Go to first image of this part" %}" title="{% trans "Go to first image of this part" %}">
+            <img src="{{STATIC_URL}}icons/revert.png"/>
+        </button>
+        <button class="previous-page" alt="{% trans "Previous" %}" title="{% trans "Previous" %}">
+            <img src="{{STATIC_URL}}icons/go-previous.png"/>
+        </button>
+        <input type="text" size="3" maxlength="3" value="0" class="page-number" />
+        <span id="imagesCount">/0</span>
+        <button class="next-page" alt="{% trans "Next" %}" title="{% trans "Next" %}">
+            <img src="{{STATIC_URL}}icons/go-next.png"
+                alt="{% trans "Next" %}" title="{% trans "Next" %}"/>
+        </button>
+        <button class="zoom-in">{% trans "Zoom in" %}</button>
+        <button class="zoom-out">{% trans "Zoom out" %}</button>
+        <div class="toolbar-end">
+        </div>
+    </div>
+    <div class="error_message">
+    </div>
+    <div class="gallery-image">
+        <img src="{{MEDIA_URL}}images/empty.png" alt="no image" />
+    </div>
+</div>
diff --git a/src/wiki/templates/wiki/tabs/gallery_view_item.html b/src/wiki/templates/wiki/tabs/gallery_view_item.html
new file mode 100644 (file)
index 0000000..0ad3add
--- /dev/null
@@ -0,0 +1,4 @@
+{% load i18n %}
+<li id="ScanGalleryPerspective" data-ui-related="side-gallery" data-ui-jsclass="ScanGalleryPerspective" class='active'>
+    <a href="#"><img src="{{STATIC_URL}}icons/image-x-generic.png" alt="{% trans "Gallery" %}" title="{% trans "Gallery" %}" /></a>
+</li>
diff --git a/src/wiki/templates/wiki/tabs/history_view.html b/src/wiki/templates/wiki/tabs/history_view.html
new file mode 100644 (file)
index 0000000..93b0bb6
--- /dev/null
@@ -0,0 +1,41 @@
+{% load i18n %}
+<div id="history-view-editor" class="editor" style="display: none">
+    <div class="toolbar">
+       <button type="button" id="make-diff-button"
+                       data-enabled-when="2" disabled="disabled">{% trans "Compare versions" %}</button>
+        {% if can_pubmark %}
+               <button type="button" id="pubmark-changeset-button"
+                       data-enabled-when="1" disabled="disabled">{% trans "Mark for publishing" %}</button>
+        {% endif %}
+               <button type="button" id="doc-revert-button"
+                       data-enabled-when="1" disabled="disabled">{% trans "Revert document" %}</button>
+               <button id="open-preview-button" disabled="disabled"
+                       data-enabled-when="1"
+                       data-basehref="{% url 'wiki_editor_readonly' chunk.book.slug chunk.slug %}">{% trans "View version" %}</button>
+
+       </div>
+    <div id="history-view">
+        <p class="message-box" style="display:none;"></p>
+
+               <table id="changes-list-container">
+        <tbody id="changes-list">
+        </tbody>
+               <tbody style="display: none;">
+                       <tr class="entry row-stub">
+                       <td data-stub-value="version"></td>
+                       <td>
+                <span data-stub-value="date"></span>
+               <br/><span data-stub-value="author"></span>
+                               <br />
+                               <span data-stub-value="description"></span>
+                       </td>
+                       <td>
+                <div data-stub-value="publishable"></div>
+                <div data-stub-value="published"></div>
+                <div data-stub-value="tag"></div>
+                       </td>
+               </tr>
+               </tbody>
+               </table>
+    </div>
+</div>
diff --git a/src/wiki/templates/wiki/tabs/history_view_item.html b/src/wiki/templates/wiki/tabs/history_view_item.html
new file mode 100644 (file)
index 0000000..e9375cd
--- /dev/null
@@ -0,0 +1,4 @@
+{% load i18n %}
+<li id="HistoryPerspective" data-ui-related="history-view-editor" data-ui-jsclass="HistoryPerspective">
+    <a href="#">{% trans "History" %}</a>
+</li>
diff --git a/src/wiki/templates/wiki/tabs/search_view.html b/src/wiki/templates/wiki/tabs/search_view.html
new file mode 100644 (file)
index 0000000..2ed15ec
--- /dev/null
@@ -0,0 +1,21 @@
+{% load i18n %}
+<div id='side-search'>
+    <p>{% trans "Search" %}:<br/>
+    <input id='search-input' /><br/>
+    <button id='search-button'>{% trans "Search" %}</button>
+    </p>
+
+    <p>{% trans "Replace with" %}:<br/>
+        <input id='replace-input' /><br/>
+        <button id='replace-button'>{% trans "Replace" %}</button><br/>
+        <input type="checkbox" id="replace-all" />
+            <label for="replace-all">{% trans "Replace all" %}</label><br/>
+    </p>
+
+    <p>{% trans "Options" %}<br/>
+        <input type="checkbox" id="search-case-sensitive" />
+            <label for="search-case-sensitive">{% trans "Case sensitive" %}</label><br/>
+        <input type="checkbox" id="search-from-cursor" />
+            <label for="search-from-cursor">{% trans "From cursor" %}</label><br/>
+    </p>
+</div>
\ No newline at end of file
diff --git a/src/wiki/templates/wiki/tabs/search_view_item.html b/src/wiki/templates/wiki/tabs/search_view_item.html
new file mode 100644 (file)
index 0000000..7acfaac
--- /dev/null
@@ -0,0 +1,4 @@
+{% load i18n %}
+<li id="SearchPerspective" data-ui-related="side-search" data-ui-jsclass="SearchPerspective">
+    <img src="{{STATIC_URL}}icons/system-search.png" alt="{% trans "Search and replace" %}" title="{% trans "Search and replace" %}" />
+</li>
diff --git a/src/wiki/templates/wiki/tabs/source_editor.html b/src/wiki/templates/wiki/tabs/source_editor.html
new file mode 100644 (file)
index 0000000..9b22825
--- /dev/null
@@ -0,0 +1,5 @@
+{% load toolbar_tags i18n %}
+<div id="source-editor" class="editor">
+    {% if not readonly %}{% toolbar %}{% endif %}
+    <textarea id="codemirror_placeholder">&lt;br/&gt;</textarea>
+</div>
diff --git a/src/wiki/templates/wiki/tabs/source_editor_item.html b/src/wiki/templates/wiki/tabs/source_editor_item.html
new file mode 100644 (file)
index 0000000..22b6d66
--- /dev/null
@@ -0,0 +1,6 @@
+{% load i18n %}
+<li id="CodeMirrorPerspective"
+       data-ui-related="source-editor"
+       data-ui-jsclass="CodeMirrorPerspective">
+    <a href="#">{% trans "Source code" %}</a>
+</li>
\ No newline at end of file
diff --git a/src/wiki/templates/wiki/tabs/summary_view.html b/src/wiki/templates/wiki/tabs/summary_view.html
new file mode 100644 (file)
index 0000000..49ad5fe
--- /dev/null
@@ -0,0 +1,45 @@
+{% load i18n %}
+<div id="summary-view-editor" class="editor" style="display: none">
+    <!-- <div class="toolbar">
+    </div> -->
+    <div id="summary-view">
+       <div class="summary-cover-area">
+               <p><img id="summary-cover" class="book-cover"
+                       {% if revision %}
+                            src="{% url 'cover_preview' chunk.book.slug chunk.slug revision %}"
+                       {% else %}
+                            src="{% url 'cover_preview' chunk.book.slug chunk.slug %}"
+                       {% endif %}></p>
+               <p><button id="summary-cover-refresh">{% trans "Refresh from working copy" %}</button></p>
+               </div>
+
+               <h2>
+                       <label>{% trans "Title" %}:</label>
+                       <span data-ui-editable="true" data-edit-target="meta.displayTitle"
+                       >{{ chunk.pretty_name }}</span>
+               </h2>
+        <p><a href="{{ chunk.book.get_absolute_url }}">{% trans "Go to the book's page" %}</a>
+        </p>
+               <p>
+                       <label>{% trans "Document ID" %}:</label>
+                       <span>{{ chunk.book.slug }}/{{ chunk.slug }}</span>
+               </p>
+               <p>
+                       <label>{% trans "Current version" %}:</label>
+                       {{ chunk.revision }} ({{ chunk.head.created_at }})
+               </p>
+               <p>
+                       <label>{% trans "Last edited by" %}:</label>
+                       {{ chunk.head.author }}
+               </p>
+               <p>
+                       <label>{% trans "Link to gallery" %}:</label>
+                       <span data-ui-editable="true" data-edit-target="meta.galleryLink"
+                       >{{ chunk.book.gallery }}</span>
+               </p>
+               <p>
+                   <label>{% trans "Characters in document" %}:</label>
+                   <span id="charcount"></span> (<span id="charcount_pages"></span> {% trans "pages" %}<span id="charcount_untagged">, {% trans "untagged" %}</span>)
+               </p>
+       </div>
+</div>
diff --git a/src/wiki/templates/wiki/tabs/summary_view_item.html b/src/wiki/templates/wiki/tabs/summary_view_item.html
new file mode 100644 (file)
index 0000000..ea7ae74
--- /dev/null
@@ -0,0 +1,4 @@
+{% load i18n %}
+<li id="SummaryPerspective" data-ui-related="summary-view-editor" data-ui-jsclass="SummaryPerspective">
+    <a href="#">{% trans "Summary" %}</a>
+</li>
diff --git a/src/wiki/templates/wiki/tabs/wysiwyg_editor.html b/src/wiki/templates/wiki/tabs/wysiwyg_editor.html
new file mode 100644 (file)
index 0000000..f54f3fb
--- /dev/null
@@ -0,0 +1,18 @@
+{% load i18n %}
+<div id="simple-editor" class="editor" style="display: none">
+    <div id="html-view" class="htmlview">
+    </div>
+
+       <div class="toolbar">
+       {% if not readonly %}
+        <button id="insert-theme-button">
+            {% trans "Insert theme" %}
+        </button>
+        <button id="insert-annotation-button">
+            {% trans "Insert annotation" %}
+        </button>
+               {% endif %}
+        <div class="toolbar-end">
+        </div>
+    </div>
+</div>
diff --git a/src/wiki/templates/wiki/tabs/wysiwyg_editor_item.html b/src/wiki/templates/wiki/tabs/wysiwyg_editor_item.html
new file mode 100644 (file)
index 0000000..ec853cd
--- /dev/null
@@ -0,0 +1,4 @@
+{% load i18n %}
+<li id="VisualPerspective" data-ui-related="simple-editor" data-ui-jsclass="VisualPerspective">
+    <a href="#">{% trans "Visual editor" %}</a>
+</li>
diff --git a/src/wiki/tests/xslt/auto/auto_test.js b/src/wiki/tests/xslt/auto/auto_test.js
new file mode 100644 (file)
index 0000000..f9e50ee
--- /dev/null
@@ -0,0 +1,139 @@
+var jsdom = require("jsdom");
+var $ = require('jquery')(new jsdom.JSDOM().window);
+var fs = require('fs');
+var exec = require('child_process').exec;
+var pd = require('pretty-data').pd;
+var ansidiff = require('ansidiff');
+
+
+eval(fs.readFileSync(__dirname + '/../../../../redakcja/static/js/wiki/xslt.js') + '');
+
+
+function assertNodesEqual(lhs, rhs, areHTMLNodes) {
+
+    function throwError() {
+        throw new Error(ansidiff.chars(pd.xml(lhs), pd.xml(rhs)));
+    }
+
+    var lhsNode = $(lhs)[0],
+        rhsNode = $(rhs)[0];
+
+    if(areHTMLNodes) {
+        var getMagicAttrsInfo = function(node) {
+            var valuePrefix = 'x-attr-value-';
+            var namePrefix = 'x-attr-name-';
+            var startsWith = function(str, prefix) { return str.substr(0, prefix.length) === prefix; }
+            var isNameAttribute = function(attr) { return startsWith(attr.name, namePrefix); }
+            var isValueAttribute = function(attr) { return startsWith(attr.name, valuePrefix); }
+            var extractId = function(attr) { return attr.name.split('-').pop(); }
+            var temp = {};
+            var toret = {map: {}, magicAttrsList: []};
+            
+            for(var i = 0; i < node.attributes.length; i++) {
+                var attr = node.attributes[i];
+                if(isNameAttribute(attr) || isValueAttribute(attr)) {
+                    var id = extractId(attr);
+                    temp[id] = typeof temp[id] === 'undefined' ? {} : temp[id];
+                    if(isNameAttribute(attr))
+                        temp[id]['name'] = attr.value;
+                    else
+                        temp[id]['value'] = attr.value;
+                    toret.magicAttrsList.push(attr.name);
+                }
+            }
+
+            Object.keys(temp).forEach(function(id) {
+                var pair = temp[id];
+                toret.map[pair.name] = typeof toret.map[pair.name] === 'undefined' ? toret.map[pair.name] = [] : toret.map[pair.name];
+                toret.map[pair.name].push(pair.value);
+            });
+            
+            return toret;
+        }
+    
+        var removeAttrs = function(node, attrsNames) {
+            attrsNames.forEach(function(name) {
+                node.removeAttribute(name);
+            });
+        }
+        
+        var mapsEqual = function(map1, map2) {
+            if(Object.keys(map1).length != Object.keys(map2).length)
+                return false;        
+            var arraysEqual = function(a1, a2) {return a1.slice().sort().join('') === a2.slice().sort().join('');}
+            Object.keys(map1).forEach(function(key) {
+                if(Object.keys(map2).indexOf(key) === -1 || !arraysEqual(map2[key], map1[key]))
+                    return false;
+            });
+            return true;
+        }
+    
+        var lhsMagicAttrsInfo = getMagicAttrsInfo(lhsNode),
+            rhsMagicAttrsInfo = getMagicAttrsInfo(rhsNode);
+        
+        removeAttrs(lhsNode, lhsMagicAttrsInfo.magicAttrsList);
+        removeAttrs(rhsNode, rhsMagicAttrsInfo.magicAttrsList);
+        
+        if(!mapsEqual(lhsMagicAttrsInfo.map, rhsMagicAttrsInfo.map))
+            throwError();
+    }
+    
+    if(!lhsNode.isEqualNode(rhsNode))
+        throwError();
+}
+
+suite('wiki.tests.xslt.auto', function() {
+        
+        var tempFileName = '.temp.xml';
+        var xsltStyleSheetPath = __dirname + '/../../../../redakcja/static/xsl/wl2html_client.xsl';
+        
+        fs.readdirSync(__dirname + '/data/').forEach(function(fileName) {
+            
+            if(fileName === tempFileName)
+                return;
+            
+            var ext = fileName.split('.').pop();
+            if(ext !== 'html' && ext !== 'xml')
+                return;
+
+            var inputData = fs.readFileSync(__dirname + '/data/' + fileName) + '';
+            
+            if(ext === 'html') {
+                test('[HTML->XML->HTML] ' + fileName, function(done) {
+                    var result = html2text({
+                        element: $(inputData)[0],
+                        stripOuter: false,
+                        success: function(generatedXML) {
+                            fs.writeFileSync(tempFileName, generatedXML);
+                            exec(['xsltproc', xsltStyleSheetPath, tempFileName].join(' ') , {}, 
+                                function(error, stdout, stderr) {
+                                    fs.unlinkSync(tempFileName);
+                                    assertNodesEqual(inputData, stdout, true);
+                                    done();
+                            });
+                        },
+                        error: function(msg){throw msg;}
+                    });
+                });
+            } else if(ext === 'xml') {
+                test('[XML->HTML->XML] ' + fileName, function(done) {
+                    var originalXML = $(inputData);
+                    exec(['xsltproc', xsltStyleSheetPath, __dirname + '/data/' + fileName].join(' ') , {},
+                        function(error, stdout, stderr) {
+                            var generatedHTML = $(stdout);
+                            var result = html2text({
+                                element: generatedHTML[0],
+                                stripOuter: false,
+                                success: function(xmltext) {
+                                    assertNodesEqual(inputData, xmltext);
+                                    done();
+                                },
+                                error: function(msg){throw msg;}
+                            });
+                    });
+                });
+            }
+        });
+});
+
+
diff --git a/src/wiki/tests/xslt/auto/data/akap.html b/src/wiki/tests/xslt/auto/data/akap.html
new file mode 100644 (file)
index 0000000..9b2a3ed
--- /dev/null
@@ -0,0 +1,3 @@
+<p xmlns="http://www.w3.org/1999/xhtml" x-editable="true" class="akap " x-node="akap">
+    Potrzebne materialy k1asa
+</p>
diff --git a/src/wiki/tests/xslt/auto/data/big.xml b/src/wiki/tests/xslt/auto/data/big.xml
new file mode 100644 (file)
index 0000000..de73d5f
--- /dev/null
@@ -0,0 +1,1049 @@
+<utwor><liryka_l> 
+
+
+<!-- TRIM_BEGIN -->
+
+
+<strofa>Mistrz rzekł i dodał: «Prawdzie nie skłamałem»./ 
+A na dnie jamy wszyscy potępieńce/ 
+Stanęli, patrzą na mnie gronem całem,/ 
+Zapominając z podziwu o męce./ 
+--- «Jak ujrzysz słońce, powiedz Dulcynowi<pr><slowo_obce>Dulcyn</slowo_obce> --- braciszek zakonny; umknął po kryjomu i wstąpił w rolę zapaleńca religijnego, wmawiając w lud łatwowierny, że on jest rzeczywistym apostołem bożym. W r. 1305 w górach Nowary założył sektę znaną odtąd pod imieniem dulcynów. Dulcyn w krótkim czasie zebrał z tysiąc zwolenników około siebie, lecz ścigany przez wojsko biskupa miasta Vercelli schwytany został w górach Nowary razem ze swoją żoną i z nią [w:] 1307 r. żywcem spalony na rynku tegoż miasta.</pr>/ 
+Jeśli tu prędko zstąpić nieochoczy,/ 
+Niech skupi żywność, śniegiem się otoczy;/ 
+Bo jak ci mówię, bez śniegu i głodu,/ 
+Nowarczyk w górach niełatwo go złowi»./ 
+<begin id="b1315850210533-628546177"/><motyw id="m1315850210533-628546177">Duch</motyw>Tak z podniesioną stopą do pochodu,/ 
+Duch Mahometa mówił, potem nogę/ 
+Na dłuż prostując poszedł w swoją drogę./ 
+Duch drugi z krtanią przebitą, kaleki,/ 
+Z nosem rozciętym pod same powieki,/ 
+I z jednym uchem od lewego oka,/ 
+Stanął, z nim całe wstrzymało się grono:/ 
+Duch patrząc na mnie z twarzą zadziwioną,/ 
+Otworzył gębę jak jamę czerwoną,/ 
+Którą broczyła świeża krwi posoka,<end id="e1315850210533-628546177"/>/ 
+I rzekł: «Schodzący tu gościu bez winy,/ 
+Jeśli nie ludzi wielkie podobieństwo,/ 
+Widziałem ciebie pomiędzy Latyny./ 
+Przypomnij sobie Piotra z Medicyny<pr><slowo_obce>Przypomnij sobie Piotra z Medicyny</slowo_obce> --- Medicyna wziął imię od miejsca swego urodzenia w ziemi bolońskiej; warchoł i intrygant, kłócił
+lud ze szlachtą, waśnił władców Rawenny i Rimini.</pr>!/ 
+Idąc z Wercelli smugiem do Markabo<pr><slowo_obce>Idąc z Wercelli smugiem do Markabo</slowo_obce> --- Kraj lombardzki od Wercelli w Piemoncie aż do ujścia rzeki Po, nad którą stał niegdyś zamek Makrabo.</pr>/ 
+<begin id="b1315850272218-1030517085"/><motyw id="m1315850272218-1030517085">Kara, Śmierć</motyw>Ostrzeż ode mnie dwóch najlepszych z Fano,/ 
+Andziolello i Gwido ich miano<pr><slowo_obce>Andziolello i Gwido (...) zdradzi najniegodniej</slowo_obce> --- Malatestino, książę w Rimini, który był na jedno oko ciemnym, zaprosił dwóch najznakomitszych obywateli miasta Fano, Gwidona del Cassero i Angiolela de Carignano do miasteczka Katoliki na ucztę, gdzie mieli się wspólnie naradzać nad pewnymi sprawami. Wysłał po nich łodzie, lecz majtkowie z jego rozkazu w porcie Katoliki obydwu wrzucili do morza. Dante w tej pieśni prorokuje, co się już stało.</pr>,/ 
+Że niespodziane czeka ich męczeństwo./ 
+Gdy zmysł proroczy widzi tu niesłabo,/ 
+Wrzucą ich w morze w porcie Katoliki;/ 
+Bo od Majorki do Cypru zatoki/ 
+Neptun nie widział nigdy takiej zbrodni,/ 
+Gdzie wciąż koczują greckie rozbójniki./ 
+Zdrajca rządzący, chociaż jednooki,/ 
+Krajem, którego duch, co za mną kroczy,/ 
+Nie chciałby nigdy widzieć w żywe oczy,/ 
+Sprosi ich w gości, zdradzi najniegodniej,/ 
+Czym ich uwolni od ślubów ofiary/ 
+Przeciw wiejącym wiatrom od Fokary<pr><slowo_obce>Fokara</slowo_obce> --- góra blisko Katoliki skąd często wiejące wiatry i burze mitrężą żeglugę. Ponieważ zginęli, nie potrzebują już obawiać się tych wichrów.</pr>».<end id="e1315850272218-1030517085"/>/ 
+--- «Gdy chcesz,» odrzekłem, «abym tam wysoko/ 
+Mówił o tobie w twoim ziemskim raju,/ 
+Kto ten nieszczęsny, wskaż mi go na oko,/ 
+Któremu było tak gorzko w tym kraju?»/ 
+Do towarzysza duch wyciągnął rękę,/ 
+Na oścież jemu otwierając szczękę,/ 
+Krzyczał: «Patrz teraz, to on, lecz nie mówi:/ 
+On myśl wątpiącą uśpił Cezarowi,/ 
+Twierdząc, że zawsze niebezpiecznie czekać,/ 
+Zamiar dojrzały do czynu odwlekać»./ 
+Jakże przeraził mnie wrażeniem dzikiem,/ 
+Ze swoim w krtani uciętym językiem,/ 
+Ten Kurion<pr><slowo_obce>Kurio</slowo_obce> --- wygnany z senatu rzymskiego jako stronnik i przyjaciel Cezara, z nim się połączył i pierwszy go zachęcił do przejścia Rubikonu, co było powodem wojen domowych. Ma tu odcięty język, którym dał tak złą radę.</pr> niegdyś tak zuchwały w mowie./ 
+Duch drugi obie miał ucięte ręce,/ 
+W zmroku wywijał tępymi ramiony./ 
+Krwią, co z nich ciekła, cały oczerniony,/ 
+I krzyknął, krzyk ten aż mi szumiał w głowie:/ 
+«Przypomnij Moskę<pr><slowo_obce>Przypomnij Moskę</slowo_obce> --- Buondelmonte przyrzekł ożenić się z panną z domu Amidei, gdy tymczasem nagle zmienił swój zamysł i ożenił się z krewną Donatich. Ten postępek rozjątrzył gniew wielu domów spokrewnionych z rodem Amidei. Uberti i Lamberti głośno wołali: ,,Zemścić się, ukarać Buondelmonta!". Starsi radzili roztropność i umiarkowanie: lecz młody Moska wrzący gniewem, radził zabić Buondelmonta, którego sam na koniec przebił puginałem. Ta familijna tragedia dała początek stronnictwom i sporom, jakie przez długie czasy wstrząsały Florencją.</pr>, przebóg, ja to rzekłem:/ 
+»Koniec koroną powinien być czynów!«/ 
+Z tych słów urosły kłótnie florentynów»./ 
+--- «I śmierć twej całej rodziny!» dodałem./ 
+On wtenczas z bólu łypiąc okiem wściekłem,/ 
+Odszedł jak gdyby już oszalał w męce./ 
+Ja wciąż na trzodę piekielną patrzałem;/ 
+I to widziałem, czego bez dowodu/ 
+Nie śmiałbym w pieśni opowiadać mojej,/ 
+Gdyby nie dobry nasz świadek, sumienie,/ 
+Które pod zbroją nam czystości swojej/ 
+Serce umacnia, zagłusza zwątpienie./ 
+<begin id="b1315905795971-3302522595"/><motyw id="m1315905795971-3302522595">Duch</motyw>Widziałem, wierzcie na słowo poety,/ 
+Tułów bez głowy, jako inne cienie/ 
+Szedł równie dobrze sporym krokiem chodu,/ 
+A w ręku trzymał swą uciętą głowę;/ 
+Głowa na włosach na obraz latarni/ 
+Zwieszona, z gestem bolesnym męczarni/ 
+Patrzając na nas, mówiła: Niestety!/ 
+Tułów przejrzysty jak szkło kryształowe/ 
+Sam stał się lampą i razem w tej chwili/ 
+Dwoje ich w jednym, jeden w dwojgu byli./ 
+Ile to widmo było rzeczywiste,/ 
+Pan nasz i mściciel wie o tym zaiste!/ 
+Tułów podszedłszy pod szyję mostową,/ 
+Wzniósł w górę rękę z całą swoją głową,<end id="e1315905795971-3302522595"/>/ 
+Ażeby do nas przybliżyć swe słowo,/ 
+Które tak brzmiało: «Gościu nieumarły,/ 
+Patrz, jakie męki na mnie się wywarły,/ 
+Patrz, czy jest większa od mojej tortura?/ 
+Jeśli mną zająć chcesz ciekawość czyją,/ 
+Wiedz, że ja byłem Bertrandem z Bornijo<pr><slowo_obce>Wiedz, że ja byłem Bertrandem z Bornijo</slowo_obce> --- Bertrand z Bornio (Bertram de Born) był obwiniany za to, że między Henrykiem II, angielskim królem, a jego synem jątrzył niezgodę i syna do buntu przeciw własnemu ojcu pobudził. Ponieważ Bertrand przeciw głowie rodziny jej członków buntował, tu za karę nosi własną głowę odciętą od tułowia. Ta głowa służy mu w piekle za latarkę, jak i na ziemi podobną jemu powinna była posługę, ażeby go ochroniła od smutnych następstw jego występku.</pr>/ 
+Dałem złe rady młodemu królowi,/ 
+Jątrzyłem syna przeciwko ojcowi:/ 
+Sam Architofel nie gorszym się wyda,/ 
+Co Absalona jątrzył na Dawida./ 
+Za to żem dzielił, co łączy natura,/ 
+Chodzę tu, przebóg, jak bezgłowa mara;/ 
+I słusznie, jaka zbrodnia, taka kara».</strofa> 
+
+
+
+
+<naglowek_rozdzial>Pieśń XXIX</naglowek_rozdzial> 
+
+
+
+
+
+
+
+
+<akap>(Krąg VIII. Tłumok X. Fałszerze. Alchemiści.)</akap> 
+
+
+
+<strofa>Rozmaitymi tłum ludu ranami/ 
+Tak moje oczy upoił w tej chwili,/ 
+Że pragnął spocząć i trzeźwić je łzami./ 
+«Co tak spoglądasz?» przemówił Wirgili,/ 
+«Czemu tak pilno patrzą twe powieki/ 
+Na tłum tych cieniów tak smutnie kaleki?/ 
+Tegoś nie robił przy drugich tłumokach:/ 
+Gdy chcesz policzyć, duchów tu jest ile,/ 
+Pomyśl; ta jama wykuta w opokach,/ 
+Ma w swym obwodzie dwadzieścia dwie mile./ 
+Księżyc już zaszedł pod nasze podnóże<pr><slowo_obce>Księżyc już zaszedł pod nasze podnóże</slowo_obce> --- Kiedy poeci rozpoczynali swoją pielgrzymkę po piekle, księżyc był w pełni, a więc słońce, kiedy teraz księżyc jest pod ich stopami, stoi ponad ich głową; przeto w tej chwili na wschodniej półsferze jest samo południe.</pr>,/ 
+Odtąd dość krótki (szanuj lot chwil czynnych,)/ 
+Czas dozwolony do pielgrzymki twojej;/ 
+Jeszcze masz widzieć wiele rzeczy innych,/ 
+O których może myśl twa ani roi»./ 
+Odpowiedziałem: «Gdybyś spojrzeć raczył/ 
+Na powód, co tu wzrok mój tak zahaczył,/ 
+Dłużej mi patrzeć pozwoliłbyś może?»/ 
+Wódz szedł, ja za nim z wolna sunąc nogą,/ 
+Dodałem jeszcze, wciąż mówić ochoczy:/ 
+«Tam, skąd utkwione odjąłem me oczy,/ 
+W jamie, co w skał tych zapadła krawędzie,/ 
+Myślę, swej winy płacze duch mój krewny,/ 
+Która tam jego kosztuje tak drogo!»/ 
+Mistrz odpowiedział: «Hamuj pociąg rzewny,/ 
+Dłużej tym duchem nie rozrzewniaj myśli,/ 
+Marz o czym innym, gdzie on jest, niech będzie./ 
+Widziałem jego w chwili, gdyśmy przyszli/ 
+Na łuk mostowy, on cię zauważał,/ 
+Wskazywał, żywo palcem ci pograżał;/ 
+Słyszałem, jak nań jeden duch przeklęty:/ 
+Geri del Bello, po imieniu krzyknął<pr><slowo_obce>Geri del Bello</slowo_obce> --- po matce krewny Dantego; zabił go Sachetti; śmierci jego trzydzieści lat po popełnionym morderstwie pomści jego synowiec.</pr>,/ 
+Lecz tyś był hrabią z Hetfortu zajęty<pr><slowo_obce>Hrabia z Hetfortu</slowo_obce> --- jest to Bertrand z Bornio, o którym wzmianka była w pieśni poprzedniej.</pr>,/ 
+W to miejsce wtedy spojrzałeś, gdy zniknął»./ 
+A ja: «Śmierć jego, której wstyd podziela/ 
+Krew nasza, od nas wygląda mściciela;/ 
+Dlatego, myślę, z uczuciem pogardy,/ 
+Nie mówiąc do mnie, odszedł duch ten hardy,/ 
+Co go w mej duszy jeszcze droższym czyni»./ 
+Tak mówiąc szliśmy do pierwszej przystani,/ 
+Skąd mógłbym widzieć dno innej otchłani,/ 
+Gdyby być mogło świetlej<pe><slowo_obce>świetlej</slowo_obce> --- jaśniej.</pe> w tej jaskini./ 
+Gdym już od mostu stał o kilka kroków,/ 
+<begin id="b1315906189530-2342764147"/><motyw id="m1315906189530-2342764147">Krzywda, Kara</motyw>Gdzie był ostatni klasztor <slowo_obce>Złych Tłumoków</slowo_obce><pr><slowo_obce>Gdzie był ostatni klasztor Złych Tłumoków</slowo_obce> --- Ostatni oddział kręgu ósmego poeta nazywa klasztorem Złych Tłumoków, prawdopodobnie bez głębszej myśli, bo duchy są tam jak wszędzie w piekle zamknięte jak mnichy za furtą klasztorną.</pr>,/ 
+Krzyki boleści, co w mej czułej duszy,/ 
+Ile ich było, razem się zmieniły/ 
+W żelazne strzały, tak serce raniły,/ 
+Tak, że rękoma zatykałem uszy./ 
+W miesiącu sierpniu społem tłum zebrany/ 
+Chorych z Maremmy i Waldikijany<pr><slowo_obce>Waldikiana</slowo_obce> --- okolica błotnista i bardzo niezdrowa dla jej mieszkańców; leży między Arezzo a Kortoną. Oprócz Sardynii, gdzie w środku lata powietrze równie jest niezdrowe, poeta wspomina błota, tak zwane Maremmy, rozciągające się wzdłuż brzegów morskich między Pizą a Sieną.</pr>/ 
+I co w sardyńskich szpitalach się mieści,/ 
+Dałby nam chyba obraz tych boleści./ 
+Z jamy buchały podobne wyziewy,/ 
+Jakimi dysze<pe><slowo_obce>dysze</slowo_obce> --- dziś popr. forma: dyszy.</pe> zgangreniałe<pe><slowo_obce>zgangreniały</slowo_obce> --- toczony gangreną.</pe> ciało.<end id="e1315906189530-2342764147"/>/ 
+I zwróciliśmy krok nasz na brzeg lewy<pr><slowo_obce>I zwróciliśmy krok nasz na brzeg lewy</slowo_obce> --- Poeci znowu zstępują z łuku mostowego na tamę, ażeby mogli, będąc bliżej otchłani, widzieć lepiej, co się w niej dzieje. Czytelnik może często pyta sam siebie: jak Dante w ciemnej nocy piekielnej mógł coś widzieć? Sam poeta wyraźnie tego nie objaśnia; być może, że ogień, który widzieliśmy w różnych miejscach piekła, a mianowicie ten, co oświeca cnotliwych i uczonych mężów pogańskich, ten, który spada na czyniących gwałt przeciw Bogu, ten, co się pali w grobach odszczepieńców religijnych, na koniec ten, w którym fałszywi doradcy są ukryci, tyle światła po wszystkich kręgach piekielnych rozrzuca, że tworzy się jakiś zmrok pół widny, przez który można coś widzieć. A może ci, których przewodnikiem jest rozum (jak tu Dantego prowadzi Wergiliusz, symbol rozumu), zupełną nocą nie są otoczeni i widzą jak w półzmroku.</pr>,/ 
+Który się długo jedną ciągnie skałą;/ 
+<begin id="b1315906247406-1597770989"/><motyw id="m1315906247406-1597770989">Kara</motyw>Spojrzałem w otchłań, gdzie na dnie jej łoża/ 
+Karze fałszerzy sprawiedliwość boża./ 
+Nie mógł smutniejszy być widok w Eginie,/ 
+Gdzie tysiącami chory lud wymierał,/ 
+A mór<pe><slowo_obce>mór</slowo_obce> (daw.) --- zaraza, pomór.</pe> zwierzęta i płazy pożerał;/ 
+Gdzie, jeśli wierzyć na poetów słowo,/ 
+Z nasienia mrówek wylęgły na nowo/ 
+Lud ją zaludnił, zwany Mirmidony,/ 
+Jak widok duchów w tej ciemnej dolinie,/ 
+Jakby stos trupów bezładnie zrzucony./ 
+Ten brzuchem cały do ziemi przypada,/ 
+Ów głowę skłonił na ramię sąsiada,/ 
+Ci na czworakach pełzają jak gady./ 
+A szliśmy milcząc jako dwa niemowy<pe><slowo_obce>dwa niemowy</slowo_obce> --- dziś popr.: dwie niemowy.</pe>,/ 
+Słuchając, patrząc na chore gromady,/ 
+Niezdolne dźwigać bezwładne tułowy<pe><slowo_obce>tułowy</slowo_obce> --- dziś popr. forma: tułowia.</pe>./ 
+Dwóch wzajem na się opartych siedziało<pr><slowo_obce>Dwóch wzajem na się opartych siedziało</slowo_obce> --- Dwaj alchemicy, jako fałszerze złota, siedzą wzajem o siebie oparci, okryci trądem i przez całą wieczność drapią aż do krwi bolące ich strupy.</pr>,/ 
+Strupy im całe cętkowały ciało;/ 
+Jak zgrzebłem żywo pociąga stajenny/ 
+Przed przyjściem pana, choć ziewa półsenny,/ 
+Tak ci po strupach paznokciami wodzą,/ 
+Przez co bolesne swe świerzby łagodzą,/ 
+Przeciwko którym już nie było środka;/ 
+Jak pod kucharskim nożem pstrąg lub płotka/ 
+Miece<pe><slowo_obce>miece</slowo_obce> --- dziś popr. forma: miota.</pe> łuskami, tak strupów kawały/ 
+Spod ich paznokci jak łuska leciały.<end id="e1315906247406-1597770989"/>/ 
+«Ty, co jak garbarz przez smutną konieczność,»/ 
+Rzekł wódz mój, «skórę chropawą pryszczami/ 
+Wyprawiasz, gładząc paznokcia ostrzami,/ 
+Mów, czy się spotkam tutaj z Latynami?/ 
+Oby twój paznokć<pe><slowo_obce>paznokć</slowo_obce> --- dziś popr. forma: paznokieć; tu forma skrócona dla zachowania rytmu jedenastozgłoskowca.</pe> nie stępiał przez wieczność!»/ 
+--- «Tutaj w nas obu dwóch Latynów witasz!»/ 
+Rzekł jeden z płaczem, «ktoś ty, co nas pytasz?»/ 
+Mój wódz tak mówił: «Jestem duch i oto/ 
+Schodzę tu razem z żyjącą istotą,/ 
+By całe piekło poznał chodząc ze mną»./ 
+Dwa cienie łamiąc podporę wzajemną,/ 
+Ze drżeniem ku mnie zwrócili się<pe><slowo_obce>Dwa cienie (...) zwrócili się</slowo_obce> --- dziś popr. forma czasownika: zwróciły się.</pe> oba;/ 
+Na wieść tę wszyscy zerwali się społem./ 
+Mistrz rzekł: «Mów teraz, co ci się podoba»./ 
+Rad z przyzwolenia tak mówić zacząłem:/ 
+«Niech pamięć o was czas, co wszystko gładzi/ 
+Wskrzesi od wschodu słońca do zachodu;/ 
+<begin id="b1315906353790-3331885609"/><motyw id="m1315906353790-3331885609">Czary, Grzech, Kara</motyw>Kto wy jesteście, z jakiego narodu,/ 
+Że wśród mąk takich wam tu serce radzi/ 
+Przede mną z chęcią otworzyć się szczerą?»/ 
+Cień rzekł: «Arezzo jest moja kraina<pr><slowo_obce>Cień rzekł: Arezzo jest moja kraina</slowo_obce> --- Griftolino, rodem z miasta Arezzo, alchemik, utrzymywał, że umie latać. Gdy Albero z Sieny, naturalny syn tamtejszego biskupa, udał się doń, aby go tej sztuki nauczył, a to się oczywiście nie udało, wymógł na swym ojcu, że kazał alchemika spalić na stosie jako czarownika.</pr>/ 
+Kazał mnie spalić z Syjeny Albero,/ 
+Chociaż tu inna wtrąciła mnie wina./ 
+Wprawdzie z nim mówiąc napomknąłem żartem,/ 
+Że ze mnie dzielny powietrzny latawiec;/ 
+On człowiek małej głowy a ciekawiec<pe><slowo_obce>ciekawiec</slowo_obce> --- dziś popr.: ciekawski. </pe>,/ 
+Chciał, abym odkrył sztuki tajemnicę;/ 
+A że nie byłem Dedalem w praktyce,/ 
+Spłonąłem za to, żem kumał się z czartem./ 
+A że był ze mnie alchemik imienny,/ 
+Minos niemylny w sędziego pojęciu,/ 
+Wtrącił mnie w Tłumok ostatni z dziesięciu».<end id="e1315906353790-3331885609"/>/ 
+«Oprócz Francuzów,» mówiłem poecie,/ 
+«Więcej próżnego narodu na świecie/ 
+Nie ma zaiste jako lud z Syjeny»./ 
+Wtenczas to słysząc, drugi trędowaty,/ 
+Mówił: «Z nich pierwszym wyjątkiem jest Strikka<pr><slowo_obce>Z nich pierwszym wyjątkiem jest Strikka</slowo_obce> --- Tu poeta ironicznie wyszydza wszystkich głośnych marnotrawców i rozgardiaszów sieneńskich.</pr>,/ 
+Który wydawał pieniądz przyzwoicie,/ 
+Trwonił kapitał, gdy brakło intraty;/ 
+Drugim Nikolo, co pierwszy użycie/ 
+Zbytkowne odkrył wonnego goździka<pr><slowo_obce>użycie zbytkowne odkrył wonnego goździka</slowo_obce> --- Dowiadujemy się tu od poety, że Nikolo pierwszy smakosz sieneński, pierwszy korzenne zaprawy do sosów i pieczeni wprowadził w użycie.</pr>/ 
+Mógłbym wyłączyć inne pasibraty<pr><slowo_obce>inne pasibraty</slowo_obce> --- Za czasów Dantego w Sienie było towarzystwo złożone z dwunastu młodych i bogatych ludzi, którzy sprzedawszy swoje posiadłości ziemskie, złożyli wielki kapitał, ażeby wspólnie go roztrwonić i zarazem żyć wesoło. Kapitał ten, jak mówi Benvenuto d'Imola, składał się z 216 tysięcy florenów, a w przeciągu niespełna roku już go marnotrawcy stracili.</pr>,/ 
+Z tych Asciano winnicę bogatą,/ 
+Wielki las przejadł i Abbagliato/ 
+Dowiódł, że jemu w głowie niepstrokato./ 
+Lecz iżbyś wiedział, kto twoim wykrzykom/ 
+Taki wtór trzyma przeciw sieneńczykom,/ 
+Skieruj twe oczy do mojej figury,/ 
+A w twej pamięci rysy me odżyją./ 
+Ja za pomocą alchemiji złoto/ 
+Rad fałszowałem, jam cień Kapokijo<pr><slowo_obce>złoto rad fałszowałem, jam cień Kapokijo</slowo_obce> --- Kapokio, sieneńczyk, znany osobiście poecie, miał jakoby razem z Dantem uczyć się fizyki i historii naturalnej.</pr>!/ 
+Możesz przypomnieć, że byłem niecnotą/ 
+A przy tym małpą wyborną z natury».</strofa> 
+
+
+
+
+<naglowek_rozdzial>Pieśń XXX</naglowek_rozdzial> 
+
+
+
+<akap>(Ciąg dalszy.)</akap> 
+
+
+<strofa><begin id="b1315906624392-3995347367"/><motyw id="m1315906624392-3995347367">Morderstwo, Grzech</motyw>Gdy za Semelę gorączką obrazy<pr><slowo_obce>Semele</slowo_obce> --- córka Kadmusa, która z Jowisza urodziła Bachusa. Junona przez zazdrość prześladowała za to cały ród Kadmusa, a szczególnie jej siostrę Ino, mamkę Bachusa. Atamas, mąż jej, rozwścieklony gniewem pozabijał jej dzieci. Tak była ukarana nienawiść macosza, z jaką Ino dzieci z pierwszej żony Atamasa prześladowała.</pr>/ 
+Junona wrzała przeciw krwi tebańskiej,/ 
+Spędzając na nich gniew po wiele razy,/ 
+Widząc swą żonę Atamas szatański/ 
+Wchodzącą w progi z dwojgiem małych dzieci,/ 
+Krzyknął jak wściekły: «Rozciągajmy sieci,/ 
+Lwica z lwiątkami w ich przeguby wleci»./ 
+I jedno dziecko, Learkiem nazwane,/ 
+Porwał i cisnął o twardą skał ścianę,/ 
+A matka, myśl tę snadź rozpacz natchnęła,/ 
+Z drugim ciężarem swoim utonęła.<end id="e1315906624392-3995347367"/>/ 
+Kiedy Fortuna twarz swą odwróciła/ 
+Od wielkich Trojan, tak że jednej chwili/ 
+Społem królestwo i króla stracili,/ 
+Hekuba smutna, niewolnica chora,/ 
+Płacząc nad ciałem swego Polidora<pr><slowo_obce>Hekuba smutna (...) nad ciałem swego Polidora</slowo_obce> --- Hekuba, żona Priama, opłakująca zwłoki syna swojego.</pr>,/ 
+Jak pies szalona w swej boleści wyła,/ 
+Tak boleść wszystkie jej zmysły zmąciła!/ 
+<begin id="b1315906686233-136023046"/><motyw id="m1315906686233-136023046">Nienawiść, Kara</motyw>Jednak Tebanie i Trojanie wściekli,/ 
+Choć tyle zwierząt i ludzi wysiekli,/ 
+Nie pastwili się z okrucieństwem takiem:/ 
+Dwa cienie nagie biegły jednym szlakiem,/ 
+Gryząc się w biegu, jak wieprz wszystkim w oczy/ 
+Rzuca się, gdy z swej zagrody wyskoczy./ 
+Jeden z nich, biegnąc, wpadł na Kapokiję,/ 
+W kark jemu pięścią grzmotnął ponad uchem,/ 
+Zwalił, po ziemi ciągnął go za szyję,/ 
+Gracując drogę Kapokija brzuchem.<end id="e1315906686233-136023046"/>/ 
+A Griffolino, z przestrachu wielkiego/ 
+Drżąc, mówił do mnie: «To duch Jana Skiki<pr><slowo_obce>To duch Jana Skiki</slowo_obce> --- Jan Skika, z familii Kawalkantych, rodem florentyńczyk: miał talent naśladowania głosu i gestów swoich znajomych, którego często na złe używał. Będąc przyjacielem Szymona Donata, któremu umarł jego krewny Buoso (zob. Pieśń XXV, gdzie on między złodziejami się znajduje) bez testamentu, a nie mógł po nim brać spadku z powodu, że Buoso zostawił bliższych krewnych, Szymon Donati, ażeby zostać spadkobiercą, przez kilka dni ukrywał ciało zmarłego Buosa, rozgłosiwszy, że jeszcze złożony chorobą oczekuje bliskiego zgonu, a w miejsce zmarłego położył w łóżko swojego przyjaciela Jana Skikę, który udając głos i postać Buosa, zrobił testament, pisząc w nim spadkobiercą po Buosie Szymona Donata, z którym zawarł uprzednio umowę, że w nagrodę dobrze odegranej roli weźmie klacz wielkiej ceny z jego stada.</pr>,/ 
+Tak wszystkich dręczy ten szaleniec dziki»./ 
+--- «Jeśli ten drugi duch cię nie uszczyknie<pe><slowo_obce>uszczyknie</slowo_obce> --- dziś popr.: uszczknie; tu forma wydłużona dla zachowania rytmu jedenastozgłoskowca.</pe>/ 
+Zębem lub szponą,» wołałem do niego,/ 
+«Powiedz mi wprzódy, kto on jest, nim zniknie»./ 
+A on: <begin id="b1315906794189-1532356216"/><motyw id="m1315906794189-1532356216">Kłamstwo, Grzech</motyw>«To Myrry starożytnej dusza<pr><slowo_obce>Myrra</slowo_obce> --- córka króla Cypru, winna tej zbrodni, jaką tu opisał poeta, po jej odkryciu przeklęta i wygnana przez ojca, uciekła do Arabii, gdzie długo i póty opłakiwała swój występek, aż od łez i żalu zamieniła się w gumę drzewną od jej imienia nazwaną myrra.</pr>,/ 
+Która do ojca czuła upał żywy,/ 
+Przeciwko prawu miłości uczciwej;/ 
+Pragnąc grzech ukryć w kryjomym niewstydzie,/ 
+Ojca pod cudzą postacią spokusza<pe><slowo_obce>spokusza</slowo_obce> --- dziś popr. forma: kusi; tu forma dokonana, wydłużona dla zachowania rytmu jedenastozgłoskowca.</pe>,/ 
+Podobnie jak duch, który oto idzie!/ 
+Znęcony zyskiem końskich stad królowy,/ 
+Wybieg zaiste cudacki i nowy,/ 
+Zamiast Donata w łóżko się położył,/ 
+Skłamał testament i znów z grobu ożył»./ 
+Gdy znikli z oczu mych ci dwaj okrutni,<end id="e1315906794189-1532356216"/>/ 
+<begin id="b1315906962269-313830910"/><motyw id="m1315906962269-313830910">Duch</motyw>Wnet odwróciłem ciekawe spojrzenie,/ 
+Patrzeć na inne tam leżące cienie./ 
+Jeden cień byłby podobny do lutni<pr><slowo_obce>Jeden cień byłby podobny do lutni</slowo_obce> --- Lutnia ma okrągły wystający brzuch, a cienką szyję; w tym więc podobieństwo do chorego na wodną puchlinę.</pr>, / 
+Gdyby miał otwór jego brzuch obrzydły,/ 
+W miejscu, skąd ciało rozdziela się w widły:/ 
+Puchlina wodna w jego ciele całem/ 
+Zmieniając kształty przez wilgoci chore,/ 
+Rozwarte usta zwiesiła do brody,/ 
+Jako suchotnik, gdy pragnieniem gore.<end id="e1315906962269-313830910"/>/ 
+«Wy, których wita z podziwem ta jama,»/ 
+Cień mówił, «patrzcie na mistrza Adama<pr><slowo_obce>Mistrz Adam</slowo_obce> --- rodem z Brescii, na żądanie hrabiego z Romeny, chcącego fałszerstwem monety poprawić zły stan swojego skarbu, fałszował złote floreny mieszaniną podlejszych metali, za co był na stosie ogniem spalony. Puchlina wodna oznacza chorobliwy stan finansowy tych krajów, co zniżaniem wewnętrznej wartości monety chcą sobie chwilowo dopomóc. Przypomnienie tego, co mistrz Adam miał niegdyś, a czego teraz brak czuje przez własną winę, piękne jest i prawdziwe! Tasso, który często w cudze odziewał się pióra, miał wyraźnie to miejsce przed oczyma w <tytul_dziela>Jerozolimie Wyzwolonej</tytul_dziela>, Pieśń XIII.</pr>!/ 
+Czegom zapragnął, żyjąc wszystko miałem,/ 
+Teraz, niestety, pragnę kropli wody./ 
+Małe strumyki, co żywią nurt Arny,/ 
+Płynąc z pagórków zielonych Kasenty,/ 
+Z rzeźwiącą treścią, przejrzyste aż do dna,/ 
+Ciągną tam oczy tu z wieczności czarnej;/ 
+Bo ich obrazu marzone ponęty/ 
+Więcej mnie trawią jak puchlina wodna./ 
+Tu Sprawiedliwość z swoim sądem w zgodzie,/ 
+Tym samym miejscem, gdziem grzeszył, mnie bodzie,/ 
+<begin id="b1315907016817-1028229828"/><motyw id="m1315907016817-1028229828">Kłamstwo, Grzech, Kara</motyw>By więcej westchnień wydostać z grzesznika./ 
+Tam jest Romena, gdziem oprócz psot wiela/ 
+Fałszował pieniądz z popiersiem Chrzciciela,/ 
+Za co spalono żywcem fałszownika./ 
+Lecz gdybym spotkał tu duchy Gwidona/ 
+I Aleksandra, i obu ich brata<pr><slowo_obce>Gwidon, Aleksander</slowo_obce> --- hrabiowie z Romeny.</pr>,/ 
+Jeszcze bym tego nie mieniał widoku/ 
+Na Fontebrandę, co stoi w mym oku<pr><slowo_obce>Fontebrandą</slowo_obce> --- wspaniała fontanna w Sienie.</pr>./ 
+Już jeden pono tu zstąpił ze świata,/ 
+Jeśli wieść do mnie doszła nieskażona/ 
+Z ust innych cieniów; ale co mi po tem,/ 
+Gdy ja tu leżę jak przybity młotem!/ 
+Gdybym tak lekki był, że zrobić mogę/ 
+W sto lat krok jeden, już poszedłbym w drogę,/ 
+Szukając jego w tej wielkiej przepaści./ 
+Która w obwodzie ma mil jedenaście/ 
+A wszerz pół mili. Zły duch, co w nich siedzi./ 
+Skusił mnie radą tych hrabiów Romeny,/ 
+Abym w mennicy bił takie floreny,/ 
+W których jest najmniej trzy karaty miedzi<end id="e1315907016817-1028229828"/>»./ 
+Rzekłem do niego: «Wskaż tych dwóch na imię,/ 
+Co tam na prawo leżą razem w dymie,/ 
+Jak zimą dymiąc parą z mokrej ręki?»/ 
+--- «Tak ich znalazłem,» odpowiedział «wtedy,/ 
+Gdy mnie w tę otchłań wtrącono na męki,/ 
+I wątpię, z miejsca czy ruszą się kiedy?/ 
+To cień świadczącej krzywo Putyfary<pr><slowo_obce>To cień świadczącej krzywo Putyfary</slowo_obce> --- Żona Putifara i Sinon Grek, oboje fałszywie świadczący; ta Józefa, którego chciała wciągnąć do winy, fałszywie oskarżyła przed mężem; a ten fałszywą wieścią o koniu drewnianym, którego Grecy zostawili pod murami Troi, oszukał Trojan. Za to oboje w zgniłej gorączce tarzają się tu po ziemi.</pr>,/ 
+Drugi Grek Sinon, co oszukał Troję;/ 
+W zgniłej gorączce tu leżą oboje,/ 
+Z ciał swych cuchnące wyziewając pary»./ 
+Cień pierwszy zemstę warzący w milczeniu,/ 
+Że śmiał bezczelną nazwać po imieniu,/ 
+Pięścią w brzuch twardy Adama uderzył,/ 
+Który jak bęben huk daleki szerzył,/ 
+Mistrz Adam wzajem dłonią niemniej twardą/ 
+Odbił policzek mówiąc doń ze wzgardą:/ 
+«Choć mi puchlina poruszyć się broni,/ 
+O! do wybitej dość ruchu mam w dłoni»./ 
+Na to cień drugi: «Gdyś na stos wstępował,/ 
+Pewnoś tak żywo ręką nie szermował,/ 
+Lecz miałeś równie, może więcej, żywą/ 
+Rękę, gdy biłeś monetę fałszywą»./ 
+Opuchły tak rzekł: «Nie kłamie twe słowo,/ 
+Lecz gdzieś był z swoją prawdomówną mową,/ 
+Kiedy o prawdę pytano się w Troi?»/ 
+--- «Jam fałsz powiedział,» Grek odparł na nowo,/ 
+«A tyś fałszował pieniądz najniegodniej./ 
+Jam winny jednej, a ty mnóstwa zbrodni»./ 
+--- «Czy koń drewniany na myśli ci stoi?»/ 
+Cisnął żart mówca, co miał brzuch wydęty,/ 
+«Świat za mną woła: bądź, kłamco, przeklęty!»/ 
+--- «Niech ci nawzajem» tak Grek mówił w gniewie/ 
+«Język pragnienie spali jak zarzewie,/ 
+Niech zgniła woda tak twój brzuch wydyma,/ 
+By jak zagroda stał ci przed oczyma»./ 
+A mincarz<pe><slowo_obce>mincarz</slowo_obce> a. <slowo_obce>mincerz</slowo_obce> --- rzemieślnik zajmujący się biciem monet i medali.</pe>: «Bluźnisz na twą gębę całą,/ 
+Bo jeśli pragnę i mam spuchłe ciało,/ 
+Tyś sam w gorączce i gore ci głowa./ 
+Ażebyś lizał Narcyza zwierciadło<pr><slowo_obce>Zwierciadło Narcyza</slowo_obce> --- woda, w której przeglądał się piękny młodzieniec Narcissus.</pr>,/ 
+Krótka by ciebie skłoniła przemowa»./ 
+Gdym był zajęty ich kłótnią zajadłą,/ 
+Mistrz rzekł: «Czas, abyś tę gawiedź porzucił,/ 
+Niewiele braknie, bym z tobą się skłócił»./ 
+Na głos ten jam się do mistrza obrócił,/ 
+<begin id="b1315907201088-3637867090"/><motyw id="m1315907201088-3637867090">Wstyd, Wina, Słowo</motyw>Ogromnym wstydem spłoniony na twarzy;/ 
+Podobny temu, co nieszczęście marzy,/ 
+A marząc życzy, by to, co się śniło,/ 
+Snem się rozwiało, jakby nic nie było./ 
+Chciałem przemówić, wstyd mi uciął słowa,/ 
+Choć wola z winy tłumaczyć się radzi,/ 
+Ze wstydu przed nim stałem jak niemowa./ 
+Mistrz rzekł: «Wstyd mniejszy większe winy gładzi,/ 
+Uspokój siebie i nie patrz tak smutnie:/ 
+Gdy czasem trafisz na podobne kłótnie,/ 
+Gdzie ludzie w mowie nie dosyć są skromni,/ 
+Że, wódz twój, jestem przy tobie, przypomnij!/ 
+<begin id="b1333739937255-1525704821"/><motyw id="m1333739937255-1525704821">Słowo, Mądrość</motyw>Bo chcieć łakomie słuchać lada bredni,/ 
+Chętka ta zdradza umysł dość powszedni<end id="e1333739937255-1525704821"/>».<end id="e1315907201088-3637867090"/></strofa> 
+
+
+
+
+<naglowek_rozdzial>Pieśń XXXI</naglowek_rozdzial> 
+
+
+<akap>(Do kręgu IX. Studnia. Olbrzymi. Anteus osadza poetów na dnie piekła.)</akap> 
+
+
+
+<strofa>Język, co wprzódy zranił mnie do tyla,/ 
+Żem spłonął wstydem, wnet balsam żądany/ 
+Podał i leczył jak włócznia Achilla,/ 
+Co i raniła, i leczyła rany<pr><slowo_obce>włócznia Achilla, co i raniła, i leczyła rany</slowo_obce> --- Achilles zranił króla Telefosa, a przyłożenie do rany tejże włóczni, która ją zadała, uzdrowiło rannego.</pr>./ 
+<begin id="b1315907392685-3050830674"/><motyw id="m1315907392685-3050830674">Dźwięk</motyw>I milcząc szliśmy od tej smutnej jamy,/ 
+Brzegiem krążącej wokoło niej tamy<pr><slowo_obce>Brzegiem krążącej wokoło niej tamy</slowo_obce> --- Dziesięć oddziałów zwanych <wyroznienie>Złe Tłumoki</wyroznienie> z rozmaitymi rodzajami oszustów, jacy w nich się znajdują, zwiedziwszy, teraz idą poeci wokoło kamiennej tamy okrążającej ostatni ten oddział kręgu ósmego; żeby przybliżyć się do dziewiątego kręgu piekła, w którym karani są zdrajcy. Jest to ta studnia, o której poeta napomyka na początku Pieśni XVIII.</pr>;/ 
+Tam nie noc, zmrok był pół nocny, pół dzienny,/ 
+Widziałem tylko szary grunt kamienny,/ 
+Lecz echo za mną dźwiękiem rogu grało,/ 
+Dźwiękiem, co mógłby zagłuszyć trzask grzmotu./ 
+W stronę, skąd dźwięk ten pobrzmiewał za skałą,/ 
+Oczy z uwagą obróciłem całą./ 
+Nie tak straszliwie Roland do odwrotu/ 
+Dął w róg wojenny, gdy z chrześcijan żalem<end id="e1315907392685-3050830674"/>/ 
+Karloman przegrał bój pod Ronsewalem<pr><slowo_obce>Karloman przegrał bój pod Ronsewalem</slowo_obce> --- Karol Wielki (Karloman, Charlemagne, Carolin Magnus) gdy walczył z Maurami z Hiszpanii, zdradzony przez jednego ze swoich wodzów, przegrał bitwę pod Roncewalem. Wtedy słynny rycerz Roland tak potężnie zadął w róg, że dźwięk tego rogu więcej jak w okręgu milowym najwyraźniej słyszano.</pr>/ 
+Podniosłem głowę; nad poziom opoki,/ 
+Zda się z jej gruntu, tłum wieżyc wyrasta,/ 
+Dlatego rzekłem: «Widzę mur wysoki,/ 
+Mistrzu, jakiego to widok jest miasta?»/ 
+A mistrz: «Tyś w błędzie, bo patrzysz z daleka,/ 
+Gdy przyjdziesz bliżej, uzna twa powieka,/ 
+Jak oddalenie fałszuje zmysł wzroku,/ 
+Więc trochę więcej przyspiesz twego kroku»./ 
+Wziął mnie za rękę i czule ją ściska,/ 
+I mówi: «Nim ten przedmiot ujrzysz z bliska,/ 
+<begin id="b1315907539427-2384102178"/><motyw id="m1315907539427-2384102178">Potwór</motyw>Aby mniej dziwnym zdał się, jak widzimy,/ 
+Wiedz, że nie wieże to są, lecz olbrzymy,/ 
+Od stóp po biodra sami i bezludni,/ 
+Wokoło zrębów stoją w wielkiej studni»./ 
+Jak wzrok wśród mglistej uwięzły ciemnoty/ 
+Z wolna odkrywa ukryte przedmioty,/ 
+Gdy mgła spadając ze światłem się zetrze;/ 
+Tak przerzynając to ciemne powietrze,/ 
+W miarę jak szedłem pod studni tej progi,/ 
+Błąd mój się rozwiał, a wzrastał chłód trwogi/ 
+<begin id="b1333740292530-1195838801"/><motyw id="m1333740292530-1195838801">Potwór, Olbrzym</motyw>Jak wieże zamku Monteregijone<pr><slowo_obce>Monteregione</slowo_obce> --- zamek w okolicach Sieny.</pr>/ 
+Tworzą okrągłych jego ścian koronę,/ 
+Tak nad zrąb studni pod same podpasy/ 
+Sterczały wkoło potworne Gigasy,/ 
+A którym zda się jeszcze groził z góry,/ 
+Piorunem Jowisz, gdy brzmi z ciemnej chmury,/ 
+Jeden stał twarzą do nas obrócony,/ 
+Barkami, piersią i dwoma ramiony;/ 
+Zaiste, jeśli natura przestała/ 
+Tworzyć olbrzymy, w tym loicznie<pe><slowo_obce>loicznie</slowo_obce> --- dziś popr.: logicznie.</pe> działa,/ 
+Bo przez to, gwoli świata pokojowi,/ 
+Takich siłaczy odjęła Marsowi./ 
+A gdy bez troski zapładza w swym łonie/ 
+Jeszcze ogromne wieloryby, słonie,/ 
+Łatwo w tym dojrzy ludzka przenikliwość/ 
+Mądrą ostrożność jej i sprawiedliwość./ 
+Bo gdzie się rozum człowieka zespoli/ 
+Z siłą potęgi i chęcią złej woli,/ 
+Tam ludziom opór nigdy się nie uda./ 
+Czaszka tak wielka była wielkoluda,/ 
+Jakby świętego Piotra szyszka w Rzymie<pr><slowo_obce>świętego Piotra szyszka w Rzymie</slowo_obce> --- Szyszka pozłocona, spiżowa, 2 i 1/2 metrów wysoka, zabytek starożytności, znajdowała się wtedy przed kościołem św. Piotra w Rzymie, dzisiaj umieszczona na ostatnim dziedzińcu Pałacu Watykańskiego.</pr>./ 
+I inne kości miał równie olbrzymie:/ 
+Tak, że począwszy od pasa w pół ciała,/ 
+Trzech Fryzów rosłych, daremna ich chluba,/ 
+Ledwo by wzrostem doszli mu do czuba<pr><slowo_obce>Trzech Fryzów rosłych (...) Ledwo by wzrostem doszli mu do czuba</slowo_obce> --- Fryzowie, mieszkający w północnych Niemczech, w średnich wiekach uchodzili za wielkoludów.</pr>;/ 
+Bo jego wzrostu potworna wyżyna,/ 
+Mogła, jak studni kończyły się zręby,/ 
+Do miejsca, człowiek gdzie płaszcz swój zapina,/ 
+Mieć miar trzydzieści wielkiej rzymskiej palmy<pr><slowo_obce>palma rzymska</slowo_obce> --- miara nieco większa od stopy paryskiej.</pr>./ 
+<slowo_obce><begin id="b1333740358445-3959607359"/><motyw id="m1333740358445-3959607359">Słowo</motyw>Rapho Lmaj Amec</slowo_obce>! jakaś mowa brzmiała/ 
+Dzika, nam obca, z jego dumnej gęby,/ 
+Dla której słodsze niestosowne psalmy.<end id="e1333740292530-1195838801"/><end id="e1315907539427-2384102178"/>/ 
+Mój wódz doń mówił: «Nieroztropny duchu!/ 
+Zadmij w róg, niech się rozlega w twym uchu/ 
+Łowczy dźwięk rogu; jeśli tobą miota/ 
+Gniew albo inna namiętność żywota,/ 
+Ulgi twej męce szukaj w jego brzmieniu:/ 
+Patrz, on z twej szyi zwisa na rzemieniu,/ 
+Który ogromną twoją pierś oplata»./ 
+Potem rzekł do mnie: «Patrz, to duch Nemroda<pr><slowo_obce>Nemrod</slowo_obce> --- władca asyryjski, sławny łowca, znajomy z ksiąg starego Zakonu, który budowaniem wieży Babel winien był podstępnego buntu przeciwko Bogu.</pr>,/ 
+Głupi, sam siebie oskarża w swej dumie,/ 
+Że przezeń znikła jedna mowa świata./ 
+Zostawmy jego, tu słów naszych szkoda,/ 
+My go nie pojmiem, on nas nie zrozumie».<end id="e1333740358445-3959607359"/>/ 
+<begin id="b1315907628566-3300871456"/><motyw id="m1315907628566-3300871456">Grzech, Kara</motyw>I szliśmy kołem, zwracając się w lewo./ 
+Z dala sterczało jakieś wielkie drzewo,/ 
+Podchodzim, widzim olbrzyma drugiego,/ 
+Jeszcze był dzikszy i wzrostu większego./ 
+Jaki go siłacz łańcuchem skrępował,/ 
+Nie wiem, lecz obie miał związane ręce,/ 
+Łańcuch od szyi pięć razy zagięty/ 
+Ciało mu ściskał żelaznymi pręty./ 
+«Pyszny, z Jowiszem o władzę szermował<pe><slowo_obce>szermować</slowo_obce> --- walczyć; por. szermierka.</pe>,»/ 
+Tak mój wódz mówił, «a skończył na męce:/ 
+To Efialtes<pr><slowo_obce>Efialtes</slowo_obce> --- ze swoim bratem Otusem, zwaliwszy Pelion i Ossę, górę na górę, wdzierali się po nich do nieba. Młody Apollo przebił go strzałą. Olbrzym ten ręce ma związane, lecz za ich poruszeniem ziemia drży cała.</pr>! w walce z olbrzymami/ 
+Przed nim zadrżeli i bogowie sami,/ 
+Myśląc, że pierwszy drzwi niebios wyłamie;/ 
+Teraz bezwładne to straszliwe ramie».<end id="e1315907628566-3300871456"/>/ 
+A ja do wodza: «Chciałbym na swe oczy/ 
+Widzieć potworny kształt Bryjareusza<pr><slowo_obce>Briareusz</slowo_obce> --- sturamienny olbrzym, syn Urana i Ziemi. <wyroznienie>Anteusz</wyroznienie>, syn Ziemi, która, ledwo wyszedł na świat, obdarzyła go nadzwyczajną siłą. Herkules zmuszonym był go zadusić, ponieważ Anteusz, jako wróg nieubłagany Herkulesa, z nim się wciąż mocował. Olbrzym ten nie ma rąk związanych, ponieważ nie brał udziału w wojnie olbrzymów z bogami.</pr>»./ 
+Wódz rzekł: «Tu bliżej ujrzysz Anteusza./ 
+Nieskrępowany i mówić ochoczy,/ 
+On nas jak piłkę na dno piekła stoczy./ 
+<begin id="b1315908459190-2183327380"/><motyw id="m1315908459190-2183327380">Strach, Zło</motyw>Lecz Bryjareusz dobrze dalej stoi,/ 
+Jak ten podobnie skuty, prócz że dwoi/ 
+Przestrach wrażeniem okropności swojej»./ 
+Nigdy tak wieżą gwałtownie nie chwiała/ 
+Trzęsieniem ziemi poruszona skała,/ 
+Jak Efialtes wstrząsł się na te słowa;/ 
+Wtenczas mnie trwoga przeszyła grobowa,/ 
+Strach by mnie dobił i padłbym bez duchu,/ 
+Gdybym nie widział olbrzyma w łańcuchu./ 
+Anteusz, gdyśmy podeszli doń bliżej,/ 
+Nad zrębem studni stał pięć łokci wyżej.<end id="e1315908459190-2183327380"/>/ 
+---<begin id="b1315908511794-3857309908"/><motyw id="m1315908511794-3857309908">Zdrada</motyw>«O ty, co w owej szczęśliwej dolinie<pr><slowo_obce>O ty, co w owej szczęśliwej dolinie</slowo_obce> --- Mieszkanie Anteusza naznacza poeta, według Lukana, w okolicy, w której Hannibala Scypion pokonał.</pr>/ 
+Przez którą imię Scypijona słynie,/ 
+Gdzie Hannibala zmusił do odwrotu,/ 
+Uprowadziłeś do swego namiotu/ 
+Łup z lwów tysiąca; ty, co gdybyś zbrojnie/ 
+Wsparł twoich braci w ich olbrzymiej wojnie,/ 
+Mógłbyś zapewnić tryumf synom ziemi!/ 
+Jeśli nie gardzisz prośbami naszemi,/ 
+Znieś nas, gdzie Kocyt chłód zamarzać zmusza,/ 
+Lecz nie odsyłaj mnie do Tyfeusza<pr><slowo_obce>Tyfeusz</slowo_obce> --- Inny olbrzym.</pr>./ 
+Stąd mój towarzysz, czego chce twa dusza/ 
+(Tylko nam schyl się i nie marszcz tak czoła),/ 
+Sławne twe imię zaniesie do świata;/ 
+On żyw i przed nim jeszcze długie lata,/ 
+Gdy go przed czasem Łaska nie powoła<end id="e1315908511794-3857309908"/>»./ 
+Gdy mowa mistrza zamilkła skończona,/ 
+Olbrzym już wodza wziął w swoje ramiona,/ 
+Których sam Herkul czuł uścisk straszliwy!/ 
+Z ramion olbrzyma rzekł do mnie Wirgili:/ 
+«Chodź, ja do swego przytulę cię łona»./ 
+Tak mnie i wodza Anteusz tej chwili/ 
+Jak jeden ciężar na plecy zarzucił./ 
+<begin id="b1315908542558-1748055252"/><motyw id="m1315908542558-1748055252">Zdrada, Kara</motyw>Patrząc na wieży bolońskiej kąt krzywy<pr><slowo_obce>Patrząc na wieży bolońskiej kąt krzywy</slowo_obce> --- La Carisenda, wieża pochyła w Bolonii, ma sto trzydzieści stóp wysokości,</pr>/ 
+Od strony, kędy ku ziemi się chyli,/ 
+Widz drży, by czasem wiatr ją nie wywrócił:/ 
+Tak mi się wydał olbrzym i tak strwożył,/ 
+Gdy się nachylił i nas obu złożył/ 
+Na dnie otchłani, co razem pożera/ 
+Zdrajcy Judasza duch i Lucyfera;/ 
+Tak pochylony olbrzym w tym momencie/ 
+Podniósł się prosty jak maszt na okręcie.<end id="e1315908542558-1748055252"/></strofa> 
+
+
+
+
+
+<naglowek_rozdzial>Pieśń XXXII</naglowek_rozdzial> 
+
+
+
+<akap>(Krąg IX. Wieczne lody. 1. Kaina. Zdrajcy krewnych. 2. Antenora. Zdrajcy kraju rodzinnego.)</akap> 
+
+
+
+<strofa><begin id="b1315908856530-1720829267"/><motyw id="m1315908856530-1720829267">Poezja</motyw>Gdybym miał dzikie, tak chrapliwe rymy,/ 
+Co by do ciemnej tej studni przystały,/ 
+Na której stoją wszystkie inne skały<pr><slowo_obce>Co by do ciemnej tej studni przystały, na której stoją wszystkie inne skały</slowo_obce> --- Kamienna tama otaczająca krąg ósmy, dźwiga na sobie ciężar wyższych kręgów piekielnych, a szczególnie te skały, które wszerz przez oddziały rozmaite, czyli jamy kręgu ósmego, przechodząc, podróżującym poetom za łuki mostowe służyły.</pr>,/ 
+Wydałbym pełniej całą treść mej myśli;/ 
+Lecz gdy tej władzy w sobie nie widzimy,/ 
+Ważę się podnieść głos nie bez bojaźni./ 
+Nie jest to zamiar jak gra wyobraźni/ 
+Wylęgły w głowie, co dno świata kreśli<pr><slowo_obce>co dno świata kreśli</slowo_obce> --- Według systemu Ptolemeusza niebo ze wszystkimi planetami i gwiazdami krąży wokoło ziemi i dlatego to środkowy punkt ziemi uważa poeta za punkt środkowy całego wszechświata.</pr>,/ 
+Nie czyn języka, co ledwo szczebioce./ 
+Muzy, przyzywam ja wasze pomoce,/ 
+Dajcie mi skrzydła Amfijona lotu<pr><slowo_obce>Dajcie mi skrzydła Amfijona lotu</slowo_obce> --- Amfion zbudował mury tebańskie i siedem bram tegoż miasta. W tej olbrzymiej pracy dopomagały jemu Muzy; w Muzach szukał natchnienia do pieśni, na której dźwięk same kamienie ruszały się z miejsca i wiązały się w mury.</pr>, / 
+Aby nie niższy był wiersz od przedmiotu.<end id="e1315908856530-1720829267"/>/ 
+Mieszkańce miejsca, o przeklęte duchy!/ 
+Które opisać słowami najtrudniej,/ 
+Czemu was owcze nie kryją kożuchy/ 
+W tym chłodnym świecie albo kozie puchy?/ 
+Gdyśmy już byli w zrębach ciemnej studni<pr><slowo_obce>Gdyśmy już byli w zrębach ciemnej studni</slowo_obce> --- W lochu tej studni zdrajcy rozmaici karani są pogrążeniem ich na wieczność w bryłach piekielnego lodu. Najdalej są oni od Boga, źródła wszelkiego ciepła, światła i życia. Nawet łzy ich marzną, skrucha nie ma tu siły uśmierzającej. Lody powstają z rzeki Kocytu, którą tworzą odpływy gorącego Flegetonu, zlodowaciałe wskutek ruchu skrzydeł Lucyfera. Krąg ten dziewiąty podzielony jest na cztery oddziały: pierwszy, najbliższy nazywa się <wyroznienie>Kaina</wyroznienie>, od bratobójcy Kaina tak nazwany, gdzie są karani ci, co zdradzili bliźnich swoich, a szczególnie swoich krewnych. Po nim następuje oddział <wyroznienie>Antenora</wyroznienie>, gdzie spotykamy zdrajców ojczyzny, tak nazwany od trojańskiego Antenora, którego obwiniają, że brał udział w kryjomym porwaniu Palladium tegoż miasta. Trzeci oddział nazywa się <wyroznienie>Ptolomea</wyroznienie> od Ptolomeusza, króla Egiptu, który zdradził zaufanie wielkiego Pompejusza i prawo gościnności w czasie, kiedy był jego gościem. Oddział zaś czwarty nazywa się <wyroznienie>Judeki</wyroznienie> a od Judasza, który zdradził i zaprzedał Chrystusa Pana.</pr>,/ 
+Daleko niżej jak stopy olbrzyma,/ 
+Zrąb jej wysoki gdym mierzył oczyma,/ 
+«Ostrożnie stąpaj!» ktoś rzekł tymi słowy,/ 
+«Abyś omijał stopą nasze głowy»./ 
+Spojrzałem za się, zdziwion nie po mału<pe><slowo_obce>nie po mału</slowo_obce> --- niemało (daw. forma; por. po trochu).</pe>,/ 
+Zmarzłe jezioro leżało pode mną,/ 
+Lśniące jak szyba ogromna kryształu./ 
+Nigdy tak grubym lodem się nie ścina/ 
+Niemiecki Dunaj lub północna Dźwina;/ 
+Gdyby Tabernik albo Pietrapiana<pr><slowo_obce>Tabernik (...) Pietrapiana</slowo_obce> --- Góry, pierwsza w Sklawonii, druga w Toskanii</pr>/ 
+Spadły przypadkiem w tę otchłań podziemną,/ 
+Szyba tych lodów stałaby jak ściana./ 
+Jak żaby skrzecząc wystają znad wody/ 
+W porze, gdy myśli żniwiarka o żniwie,/ 
+Tak cienie blade, skarżące płaczliwie,/ 
+W lód pogrążone sterczały nad lody/ 
+Pół twarzą, gdzie wstyd czerwieni jagody./ 
+Z dzikim hałasem jak klekot bociani/ 
+Brzmiał zgrzyt ich zębów po całej otchłani;/ 
+Skrzepłe ich usta wzrok ponuro toczy,/ 
+Smutek ich serca zdradzały ich oczy./ 
+Gdy spojrzę na dół, pod mymi stopami/ 
+Dwa cienie z sobą zwarły się piersiami,/ 
+Aż czub ich włosów przytykał do czuba./ 
+«Kto wy jesteście?» krzyknąłem. Wtem cienie/ 
+<begin id="b1315909171203-607305320"/><motyw id="m1315909171203-607305320">Cierpienie</motyw>Ukośne ku mnie zwróciły spojrzenie,/ 
+Łzy, co w ich oczach przed chwilą pływały,/ 
+Spadły na rzęsy i chłodem stężały./ 
+Silniej nie ściska deskę z deską śruba,/ 
+Jak potępieńcy zwarli czoła swoje;/ 
+Jak dwa rogami bodące się byki,/ 
+Tak gniew ich obu był wielki i dziki./ 
+Cień, co od mrozu stracił uszu dwoje,/ 
+Schylając głowę rzekł do mnie łaskawie:<end id="e1315909171203-607305320"/>/ 
+«Dlaczego na nas patrzysz tak ciekawie?/ 
+<begin id="b1315909228370-492156331"/><motyw id="m1315909228370-492156331">Kara</motyw>Chcesz wiedzieć, ci dwaj jakiego są rodu<pr><slowo_obce>ci dwaj jakiego są rodu</slowo_obce> --- Aleksander i Napoleon degli Alberti po śmierci ojca robiąc dział między sobą, dwaj bracia, tak się skłócili, że szpadami nawzajem się przebili. Miejsce ich rodzinne leżało nad strumieniem Bisenzio, który przepływa dolinę Falterona między Lukką a Florencją.</pr>?/ 
+Gdzie zdrój Bisencjo wypływa, dolina,/ 
+Własność Albertich, ojczyzną ich była,/ 
+Taż sama, jedna matka ich rodziła./ 
+Nie znajdziesz w całym tym kręgu Kaina/ 
+Godniejszych siedzieć w bryłach tego lodu:/ 
+Nawet ten, którym wzdryga się natura,/ 
+Na wylot włócznią przebity Artura<pr><slowo_obce>Na wylot włócznią przebity Artura</slowo_obce> --- Morderek, syn bajecznego Artura, króla Brytanii, według podania czatował w zasadzce na swojego ojca, ażeby go zabić; lecz ten uprzedził zamach zbrodniarza i włócznią przebił swego syna na wylot, tak że przez otwór rany słońce świeciło.</pr>,/ 
+Ani Kancelieri, ani Maskeroni<pr><slowo_obce>Ani Kancelieri, ani Maskeroni</slowo_obce> --- Fokacia Kancelieri, rodem z Pistoi, odciął rękę swojemu wujowi, a potem go zabił. Maskeroni, florentyńczyk, też był zabójcą swojego krewnego.</pr>./ 
+Jeśliś Toskańczyk, musisz znać, kto oni./ 
+O jeśli głos mój uszu twych nie kazi,/ 
+Wiedz, że ja jestem Kamerione z Pazzi<pr><slowo_obce>Kamicione de Pazzi</slowo_obce> --- zabił Ubertina, swojego krewnego.</pr>,/ 
+Że oczekuję tu jeszcze Karlina<pr><slowo_obce>Karlino de Pazzi</slowo_obce> --- stronnik białych gwelfów; zdradą zdał czarnym gwelfom zamek obronny, leżący nad rzeką Arno za pewną sumę pieniężną.</pr>!»/ 
+Widziałem potem z tysiąc innych twarzy,/ 
+Na pół skostniałych, zsiniałych od chłodu/ 
+Myśl jeszcze z dreszczem o tych lodach marzy!/ 
+Gdy ku środkowi szukamy przechodu<pe><slowo_obce>przechód</slowo_obce> --- przejście.</pe>,/ 
+Gdzie każda ciężkość cięży swoim ciałem,/ 
+W tym wiecznym zmroku z przerażenia drżałem./ 
+Nie wiem, przypadkiem czy wyższym zrządzeniem/ 
+Śród głów sterczących stawiąc<pe><slowo_obce>stawiąc</slowo_obce> --- dziś popr. forma: stawiając.</pe> stopy z trwogą,/ 
+Jednemu na twarz nastąpiłem nogą.<end id="e1315909228370-492156331"/>/ 
+«Za co mnie depczesz?» rzekł duch z bólu blady,/ 
+«Czy mścisz się na mnie Montaperto zdrady?»/ 
+«Czekaj mnie, mistrzu,» mówiłem z wzruszeniem;/ 
+«Chcę mą wątpliwość objaśnić z tym cieniem,/ 
+Potem, jak zechcesz, jam śpieszyć gotowy»./ 
+Wódz stanął, a jam wyzwał do rozmowy/ 
+Ducha, co bluźnił, nie szczędząc klątw długich:/ 
+<begin id="b1315910532405-1765907429"/><motyw id="m1315910532405-1765907429">Kłótnia</motyw>«Powiedz, kto jesteś, ty, co fukasz drugich?»/ 
+--- «Ty sam kto jesteś? Za co w Antenorze/ 
+Idąc, twe stopy drugich twarze depcą?/ 
+Za co naciskasz krokiem tak bolącym,/ 
+Za ciężkim, nawet gdybyś był żyjącym?»/ 
+--- «Ja żyję,» rzekłem, «miło ci być może,/ 
+Gdy kochasz sławę lub wspomnienie skromne,/ 
+Wśród wielu imion twe imię przypomnę»./ 
+On na to: «Odejdź, natrętny pochlebco!/ 
+Pochlebne słowa, żal twego zachodu,/ 
+Nic nas nie grzeją na tych falach lodu»./ 
+Wziąwszy go za kark, rzekłem: «Jak cię zową<pe><slowo_obce>zową</slowo_obce> --- dziś popr. forma 3 os. lm: zwą.</pe>?/ 
+Mów, bo wnet łysą zaświecisz mi głową»./ 
+--- «Rwij włos, nie powiem tobie, jak się zowię,/ 
+Choćbyś sto razy deptał mi po głowie»./ 
+Za czub schwyciłem jego w mgnieniu oka/ 
+I wyrywałem z czaszki włos garściami;/ 
+On słowa żadnej skargi nie wyrzucił,/ 
+Tylko wył dziko i oczy wywrócił.<end id="e1315910532405-1765907429"/>/ 
+Drugi cień krzyknął: «Co tobie, mój Bokka<pr><slowo_obce>Co tobie, mój Bokka</slowo_obce> --- Bocca degli Abbati; na początku bitwy pod Montaperti nad rzeką Arbią podstępem uciął rękę gwelfowi, Jakubowi Pazzi, który niósł sztandar stronnictwa; gwelfowie przerażeni upadkiem swojego sztandaru, który ich do boju prowadził, poszli w rozsypkę i bitwę przegrali. Dante, chociaż gibelin, potępia ten czyn nieszlachetny.</pr>?/ 
+Ty wyjesz, nie dosyć ci zgrzytać zębami,/ 
+Jakiż cię dręczy szatan tak zawzięty?»/ 
+--- «Teraz,» mówiłem, «milcz, zdrajco przeklęty!/ 
+Abyś twe imię powiedział, nie proszę,/ 
+Sam je na wieczną twą hańbę ogłoszę»./ 
+A on: «<begin id="b1315910600773-2693856363"/><motyw id="m1315910600773-2693856363">Zbrodnia, Grzech, Kara</motyw>Baj sobie, co masz na języku,/ 
+Lecz nie zapomnij, idąc z Antenory,/ 
+Tego, co język miał w mowie za skory<pe><slowo_obce>skory</slowo_obce> --- szybki, prędki.</pe>;/ 
+Tu złoto Franków wtrąciło młokosa,/ 
+Możesz powiedzieć: widziałem Buosa<pr><slowo_obce>Buoso da Duera</slowo_obce> --- rodem kremończyk, przekupiony przez generała francuskiego, Gwidona de Montfort, dał mu przejść przez rzekę Oglio.</pr>/ 
+Tam, gdzie grzeszników jest w lodach bez liku./ 
+Gdy, kto jest więcej, spytają i za co?/ 
+Patrz, oto stoi Bekeria ladaco<pr><slowo_obce>Bekeria</slowo_obce> --- Bekeria, rodem z Pawii, opat Walombrozy; gdy odkryto spisek, za pośrednictwem którego miał Florencję oddać w ręce gibelinów, głowę mu ścięto.</pr>/ 
+Któremu głowę Florencyja ścięła,/ 
+A trochę dalej: Gianni, Ganello,/ 
+Otwierający wrogom Tribaldello<pr><slowo_obce>A trochę dalej: Gianni, Ganello, (...) Tribaldello</slowo_obce> --- Wszyscy ci trzej zdrajcy swojego kraju.</pr>/ 
+Faencę, kiedy snem głuchym usnęła»./ 
+Gdy z miejsc okropnych szukamy przechodu,/ 
+Dwóch potępieńców ujrzałem w parowie,/ 
+Wyższy niższemu głową legł na głowie;/ 
+A jak łakomie szarpiemy chleb z głodu,/ 
+Tak on zatopił kły w ciało sąsiada,/ 
+Tam kędy czaszka do barków przypada./ 
+Nie z takim gniewem Tydej zemstą ślepy<pr><slowo_obce>Tydej zemstą ślepy</slowo_obce> --- Tydeusz podczas wojny tebańskiej wyzwał Menalipa na włócznie i obaj ranili siebie śmiertelnie; Tydeusz, według Stacjusza, konając, z wściekłością gryzł czaszkę swojego wroga, który tylko co skonał.</pr>/ 
+Menalipowej głowy gryzł czerepy,/ 
+Jak on swą zdobycz żuje i wysysa.<end id="e1315910600773-2693856363"/>/ 
+«Człowieku,» rzekłem, «co paszczą tygrysa/ 
+Mścisz się nad wroga nienawistną głową,/ 
+Powiedz mi, jakie masz zemsty powody?/ 
+A ja ci moją odpłacę wymową,/ 
+Kiedyś, pomiędzy ziemskimi narody,/ 
+Jeśli mnie Pan Bóg żywcem stąd wydźwignie,/ 
+A język w ustach moich nie zastygnie».</strofa> 
+
+
+
+
+
+<naglowek_rozdzial>Pieśń XXXIII</naglowek_rozdzial> 
+
+
+
+
+
+<akap>(Krąg IX. 2. Antenora --- ciąg dalszy. 3. Ptolomea. Zdrajcy przyjaciół.)</akap> 
+
+
+
+<strofa><begin id="b1315910834677-1850152533"/><motyw id="m1315910834677-1850152533">Duch, Kara</motyw>Od strawy dzikiej oderwał paszczękę/ 
+Ów potępieniec i krew z ust ocierał/ 
+Włosami czaszki, której mózg pożerał./ 
+I mówi: «Srogą chcesz odnawiać mękę,/ 
+Serce mi pęka, nim usta otwieram./ 
+Lecz gdy ze słów mych jak z nasion dojrzeje/ 
+Hańba dla zdrajcy, którego pożeram,<end id="e1315910834677-1850152533"/>/ 
+Słuchaj, wypowiem, wypłaczę me dzieje./ 
+Nie wiem, kto jesteś, przez jaki cud nowy/ 
+Zaszedłeś do nas, lecz po dźwięku mowy/ 
+Poznaję w tobie Włocha, florentyna./ 
+Widzisz przed sobą hrabię Ugolina<pr><slowo_obce>Hrabia Ugolino</slowo_obce> --- pochodził ze starożytnej familii pizańskiej hrabiów della Gherardesca. Będąc podestą i naczelnikiem siły zbrojnej w Pizie, podniósł tę rzeczpospolitą do szczytu potęgi i sławy; potem gdy przegrał bitwę morską z genueńczykami, dla podtrzymania dawnej potęgi swojego kraju wiązał się traktatami z Florencją, której wskutek wzajemnej umowy kilka mało znaczących zamków ustąpił. Zazdrosny jego władzy, a więcej jeszcze sławy długoletnim rządem Ugolina nabytej, arcybiskup Rugieri posądził go o zdradę stanu; potem wsparty współdziałaniem hrabiów Gwalandi, Lafranki i Sismondi ze zbuntowanym ludem natarł na straż przyboczną Ugolina, uwięził go z dwoma synami i dwoma wnukami, a wszystkich zamknął w wieży na placu zwanym <wyroznienie>Degli Anziani</wyroznienie>. Bramę więzienia zamurowawszy, klucze od niej kazał wrzucić w rzekę Arno. Wieża ta od głodowej śmierci Ugolina nosi nazwę <wyroznienie>Wieży głodu</wyroznienie>. Było to 1289 r.</pr>/ 
+A ten, co teraz jest mej zemsty łupem,/ 
+Zwał się Rudżieri, był arcybiskupem./ 
+Jak mnie w zdradzieckie usidlono słowa,/ 
+Jak nieostrożnie wpadłem w jego ręce,/ 
+Nie warto mówić, bo rzecz nie jest nowa./ 
+Lecz o mym zgonie, o mej strasznej męce,/ 
+Jeśli nikt wieścią uszu twych nie skaził,/ 
+Słuchaj i osądź, czy on mnie obraził./ 
+<begin id="b1315910929473-1779874050"/><motyw id="m1315910929473-1779874050">Śmierć, Grzech, Kara, Głód</motyw>Jest w głębi wieży podziemna pieczara,/ 
+Sławna mym zgonem; dziś może w niej jęczy/ 
+Na nowo jaka niewinna ofiara./ 
+Tam okiem witym z żelaznych obręczy,/ 
+Widziałem mnogich księżyców oblicze,/ 
+Aż mnie raz we śnie przywidziana mara/ 
+Zdarła przyszłości chmury tajemnicze./ 
+Przyśniło mi się, że biskup zawzięty/ 
+Polował wilka z małymi wilczęty,/ 
+Na owej górze, co wzniosłymi szranki/ 
+Z pizańską ziemią i Lukką graniczy<pr><slowo_obce>Na owej górze, co wzniosłymi szranki z pizańską ziemią i Lukką graniczy</slowo_obce> --- Góra San Gitiliano na pograniczu Lukki i Pizy. Tam właśnie uwięziono Ugolina. Polityczna działalność Ugolina, w której ocenę tu bliżej wchodzić nie możemy, oczom wielu przedstawiała się jako zdrada kraju. Dlatego umieścił go Dante, który widocznie podzielał takie zdanie, tu, w Antenorze.</pr>./ 
+Już chudą psiarnię zemkniono ze smyczy,/ 
+Hrabia Gwalandi, Sismondi, Lanfranki/ 
+Szczują na czele, zdobycz będzie łatwa:/ 
+Już wilk znużone zatrzymuje kroki,/ 
+Upada wreszcie i ojciec, i dziatwa:/ 
+I widzę kłami rozprute ich boki./ 
+Budzę się! Jeszcze noc nie zeszła z nieba,/ 
+Już moje dziatki, wspólniki niewoli,/ 
+Szlochają przez sen i wołają: »chleba!«./ 
+O! Jeśli dotąd serce ci nie boli,/ 
+Kiedy pomyślisz, co się we mnie działo/ 
+I co me serce nadal przeczuwało,/ 
+Jeśli nie płaczesz, któż ci łzy wyciśnie?/ 
+Budzą się dzieci, wkrótce chwila błyśnie,/ 
+W której nam zwykle udzielano strawy,/ 
+Lecz na sen pomnąc truchlałem z obawy./ 
+Wtem z bram więzienia łoskot mnie doleci,/ 
+Zamurowano! --- spojrzałem na dzieci,/ 
+Spojrzałem z niemej wyrazem rozpaczy;/ 
+A w głębi serca czułem mróz jak w grobie./ 
+Gwido mój mały wołał: »Co to znaczy,/ 
+Tak dziko patrzysz? Ojcze mój, co tobie?«/ 
+Nie mogłem mówić, ni łzy z oczu dostać,/ 
+Milczałem długo --- aż do nocy końca./ 
+Nazajutrz do nas zbłądził promyk słońca/
+I w twarzach dzieci ujrzałem mą postać./ 
+Natenczas z bólu gryzłem obie ręce./ 
+Synowie myśląc, że mnie głód tak pali,/ 
+Łamiąc rączęta ze łzami wołali:/ 
+»Ojcze kochany, ulżyj twojej męce,/ 
+Zjedz twoje dzieci, tyś nas ubrał w ciało,/ 
+Tobie nas biednych rozebrać przystało«. ---/ 
+Musiałem milczeć i ból w sobie morzyć,/ 
+Wkrótce i mowa w ustach nam zamarła!/ 
+Jęczeć nie śmiałem, by dzieci nie trwożyć./ 
+O ziemio, czemuś ty nas nie pożarła!/ 
+Weszło czwartego dnia światło zabójcze,/ 
+Anzelmek mały przywlekł się pod nogi/ 
+I przerażony wolał: »Ojcze drogi!/ 
+Ach! Czemu ty nas nie ratujesz, ojcze?«/ 
+Wołał i skonał! --- Jak mnie tu widzicie,/ 
+Tak ja widziałem wszystkie dzieci moje/ 
+Jedno po drugim --- wszystkich było troje,/ 
+Wszystkie u nóg mych zakończyły życie./ 
+Od zwłok jednego do drugiego biegłem,/ 
+Ślepy na trupach potknąwszy się ległem./ 
+Dzień jeszcze siódmy do słońca zachodu,/ 
+Krzyczałem z żalu, a na koniec --- z głodu,/ 
+Bo głód był jeszcze sroższy od żałości<pr><slowo_obce>Krzyczałem z żalu (...) sroższy od żałości</slowo_obce> --- Cały ten ustęp głodowej śmierci Ugolina i jego dzieci dziwnie jest piękny i dramatyczny. Poeta tu jak w ustępie Franczeski umiał w porę zakląć okropność i na tym właśnie cała tajemnica dramatycznej sztuki zawisła. Wielu krytyków rozbiorowi tego ustępu Ugolina pod względem estetycznym i poetycznym swoje pióra poświęciło.</pr>»./ 
+Skończył i dziko wywróciwszy oczy,/ 
+Na nowo usta w krwawą czaszkę broczy,/ 
+I jak pies zębem zgrzytając, rwie kości. <end id="e1315910929473-1779874050"/>/ 
+O Pizo! hańbo pięknej ziemi włoskiej,/ 
+Kędy <slowo_obce>si</slowo_obce> dźwięczy<pr><slowo_obce>Kędy ,,si" dźwięczy</slowo_obce> --- Wyrażenie powyższe odnosi się do tego, że w języku włoskim <slowo_obce>si</slowo_obce> znaczy ,,tak".</pr> tak miękkimi głoski;/ 
+Gdy cię nie karzą leniwe sąsiady,/ 
+Niechaj się nagle wzruszą z swej posady/ 
+Sąsiednie wyspy, Kapraja, Gorgona<pr><slowo_obce>Kapraja, Gorgona</slowo_obce> --- małe wyspy blisko ujścia Arna do morza.</pr>,/ 
+Zahaczą Arnę, gdzie jej ujście kona;/ 
+Niech tak szeroko roztoczy swe tonie,/ 
+Aż wszystkich twoich mieszkańców pochłonie./ 
+Bo jeśli wrogom Ugolino hrabia/ 
+Zdał twoje zamki i winien tej zdrady,/ 
+O nowe Teby! Cóż twa złość wyrabia?/ 
+Jakaż twą zemstę czarna pamięć szpeci,/ 
+Głodem niewinne morzyć jego dzieci!/ 
+<begin id="b1315911041380-177550501"/><motyw id="m1315911041380-177550501">Duch, Cierpienie</motyw>Widziałem drugich potępieńców z bliska<pr><slowo_obce>Widziałem drugich potępieńców z bliska</slowo_obce> --- Tu poeci wstępują w trzeci oddział tegoż kręgu, zwany <slowo_obce>Ptolomea</slowo_obce>, gdzie są karani ci, co zdradzili swoich krewnych i przyjaciół.</pr>,/ 
+Jak lód ich swymi bryłami naciska;/ 
+Nie stali w lodach, lecz wznak wywróceni/ 
+Na lodowatej leżeli przestrzeni./ 
+Tam łza zamarza w chwili, gdy wybłyska,/ 
+Boleść jak robak po ich wnętrzach toczy,/ 
+Bo jej nie mogą wypłakać przez oczy./ 
+Jak hełm z kryształu, skrzepła łez powłoka/ 
+Kryje pod rzęsą całą wklęsłość oka./ 
+Choć jak stwardniała z mrozu skóra muła/ 
+Twarz moja prawie stała się nieczuła,/ 
+Wiatr jakiś, czułem, obwiał moje ciało.<end id="e1315911041380-177550501"/>/ 
+«Mistrzu mój,» rzekłem, «mów, co tu powiało?/ 
+Czy wiatr w tym chłodzie jeszcze nie zastygnął?»/ 
+--- «Dowiesz się wkrótce, skąd wiatr aż tu śmignął,»/ 
+Mówił, «przyczynę gdy wzrok twój wyśledzi,/ 
+Oko wyręczy głos mej odpowiedzi»./ 
+<begin id="b1315911249556-852926870"/><motyw id="m1315911249556-852926870">Cierpienie, Duch</motyw>Z tych nieszczęśliwych jeden tymi słowy/ 
+Do nas przemówił z skorupy lodowej:/ 
+«Dusze uwięzłe w swego grzechu matni,/ 
+Który was wtrąca aż w ten krąg ostatni,/ 
+Zerwijcie z twarzy mej twardą zasłonę,/ 
+Ulżyjcie bólem serce przepełnione,/ 
+Niechaj wyleję choć jedną łzę ciepłą,/ 
+Bo już mi oko i serce zakrzepło».<end id="e1315911249556-852926870"/>/ 
+A ja: «Chcesz, abym ulżył ci w cierpieniu,/ 
+Kto jesteś, nazwij siebie po imieniu;/ 
+Gdy nie usunę twoich łez przeszkodę,/ 
+Bodajbym w lodach tych na wieki siedział!»/ 
+«Brat Alberigo jestem» odpowiedział<pr><slowo_obce>Brat Alberigo</slowo_obce> --- rodem z Faency, jeden z towarzystwa braci wesołych (zob. uwagę do pieśni XXIX), zaprosił krewnego swego z synkiem na ucztę i w chwili, kiedy podawano owoce i jagody, kazał ich zamordować. Stąd urosło włoskie przysłowie: ,,On kosztował owoców brata Alberigo".</pr>,/ 
+Zły owoc z mego wyrasta ogrodu/ 
+I tu za figę mam daktyl w nagrodę<pr><slowo_obce>I tu za figę mam daktyl w nagrodę</slowo_obce> --- Ironia. Daktyl jako owoc z cudzej krainy uchodził za mający wartość większą od krajowej figi.</pr>»./ 
+<begin id="b1333751257040-1120163977"/><motyw id="m1333751257040-1120163977">Zdrada, Piekło, Szatan, Śmierć, Upiór</motyw>--- «Czy już umarłeś?» rzekłem drżąc od chłodu,/ 
+A on: «Nic nie wiem, odkąd tu drętwieję,/ 
+Co z moim ciałem na świecie się dzieje./ 
+Bo Ptolomea ma te przywileje,/ 
+Że często dusza wpada w nią niebacznie,/ 
+Nim Parka przędzę rwać dni naszych zacznie./ 
+A gdybyś chętniej zdjął lód z moich powiek,/ 
+Wiedz, że gdy zdrady dopuści się człowiek<pr><slowo_obce>Wiedz, że gdy zdrady dopuści się człowiek</slowo_obce> --- Z dziwną ścisłością logiczną oznaczony tu stosunek między karą a występkiem. Kto zdradza tego, kto nam ufa, tego dusza w tej chwili staje się łupem kar piekielnych, do których jeszcze za życia wyrzuty sumienia, żal bezowocny i pogarda samego siebie wcześnie przygotowują. A ponieważ wynikająca stąd rozpacz zdrajcy często pędzi go na rozdroże jeszcze dzikszych i nikczemniejszych namiętności, przeto się zdaje, że w jego ciele już nie ludzka dusza, ale szatan zajął gospodę.</pr>,/ 
+Ciało dyjabłu odkazuje dusza,/ 
+Który nim rządzi, włada najzupełniej,/ 
+Nim się czas kary cielesnej wypełni;/ 
+Dusza zaś wpada aż w tę chłodną studnię./ 
+Może wam jeszcze jawi się w swym ciele/ 
+Cień, co tu ze mną w tych lodach się rusza:/ 
+Patrz, Branka d'Oria! Już lat przeszło wiele<pr><slowo_obce>Branka d'Oria</slowo_obce> --- zabił w czasie obiadu teścia swego, Michała Zankę, którego wyżej spotkaliśmy w smolnym jeziorze. Morderca ten rodem był genueńczyk.</pr>,/ 
+Odkąd tu siedzi, wśród brył tego lodu;/ 
+Musiałeś kiedyś znać jego za młodu»./ 
+«Kłamiesz!» doń rzekłem «lub świadczysz obłudnie,/ 
+Bo Branka d'Oria nie umarł, on żyje/ 
+Jeszcze na ziemi, dobrze je i pije»./ 
+On odpowiedział: «W jamie <slowo_obce>Złych Tłumoków</slowo_obce>,/ 
+Tam gdzie widziałeś war smolnych potoków,/ 
+Mógł jeszcze nie być cień Sanchy Michała/ 
+W chwili, gdy Branka d'Oria szatanowi/ 
+Ustąpił swego pomieszkanie ciała/ 
+Jako wiernemu zdrady spólnikowi./ 
+Teraz gdym całą ufność w tobie złożył,/ 
+Otwórz me oczy!» Jam ich nie otworzył,/ 
+Bo względem jego nieszczerość tą razą/ 
+Nie była żadną szczerości obrazą./ 
+Wrogi cnót wszystkich, o genueńczyki!/ 
+Wstyd wam, pomiędzy takimi grzeszniki/ 
+Jednego ziomka waszego spotkałem,/ 
+Który, co czyny jego świadczyć muszą,/ 
+Na dno Kocytu pogrążył się duszą,/ 
+Gdy jeszcze życie kłamie swoim ciałem.<end id="e1333751257040-1120163977"/></strofa> 
+
+
+
+<naglowek_rozdzial>Pieśń XXXIV</naglowek_rozdzial> 
+
+
+
+
+<akap>(Krąg IX. 4. Judekka. Najwięksi zdrajcy: Judasz, Brutus, Kasjusz, Szatan. Powrót do światła.)</akap> 
+
+
+
+
+
+<strofa>«<wyroznienie>Vexilla regis prodeunt inferni</wyroznienie><pr><slowo_obce>Vexilla regis prodeunt inferni</slowo_obce> --- co znaczy: Oto zbliżają się sztandary piekielnego króla. Słowa te (początek hymnu śpiewanego w Wielki Piątek) w tekście oryginału są po łacinie.</pr>/ 
+Wprost ku nam! Jeśli widzisz za pomroką»/ 
+Mistrz mówił, «patrzaj, wytężaj twe oko»./ 
+Kiedy noc naszą półsferę zaczerni/ 
+Lub ciemny tuman przedmioty powleka,/ 
+Myślim, że widzim młyn wietrzny z daleka;/ 
+Tak, zdało mi się, oko me postrzegło/ 
+Stojącą jakąś budowę odległą./ 
+<begin id="b1333751380702-1720694009"/><motyw id="m1333751380702-1720694009">Piekło, Szatan, Zima</motyw>Wtenczas od wiatru szukając ochrony,/ 
+Stanąłem za mym wodzem, bo zasłony/ 
+Nie było innej; tam wśród wiecznej zimy,/ 
+Już byłem w miejscu, gdzie cienie widziałem<pr><slowo_obce>Już byłem w miejscu, gdzie cienie widziałem</slowo_obce> --- Poeci wchodzą już w ostatni oddział kręgu ostatniego, nazwany od Judasza: <wyroznienie>Judykka</wyroznienie>, gdzie karani są ci, co dopuścili się zdrady przeciw swoim dobroczyńcom. W środku tego oddziału i całego wszechświata, spotykamy czterech głównych zdrajców tego rodzaju: Disa czyli Lucyfera, naczelnika zbuntowanych aniołów, Judasza Iskariotę, Kasjusza i Brutusa.</pr>,/ 
+A co z przestrachem wpisuję w te rymy/ 
+Oblane lodem jak ździebło kryształem./ 
+Ten w lodach leży na wznak rozciągnięty./ 
+Ci prosto stoją, drudzy wspak na głowie,/ 
+Ten łukiem twarz swą nagina do pięty./ 
+Gdyśmy do tyla zaszli w to pustkowie,/ 
+Że już mojemu mistrzowi się zdało,/ 
+Pokazać szpetne, niegdyś piękne ciało,/ 
+Zwrócił się do mnie i mówił z powagą/ 
+«Oto Lucyfer, oto krąg przeklęty!/ 
+Teraz się cały uzbrajaj odwagą».<end id="e1333751380702-1720694009"/>/ 
+<begin id="b1315911559022-544613790"/><motyw id="m1315911559022-544613790">Strach</motyw>Jakiem ja wtedy osłabnął i skolał<pe><slowo_obce>skolał</slowo_obce> --- skołowaciał; był niby zamieniony w kołek.</pe>,/ 
+Mój czytelniku, zamilczeć bym wolał,/ 
+Pod piórem moje zastygłoby słowo./ 
+Jeśli najmniejszy masz kwiat wyobraźni,/ 
+Wyobraź sobie, jak dręczy i drażni/ 
+Stan, w jakim całą duszą się zawarłem,/ 
+Zda się, pół żyłem, na poły umarłem.<end id="e1315911559022-544613790"/>/ 
+<begin id="b1315911576539-159505112"/><motyw id="m1315911576539-159505112">Szatan</motyw>Król piekielnego państwa jakby kawał/ 
+Głazu nad lody pół piersią wystawał;/ 
+Jak wzrost mój dosyć ogromny, nie kłamię,/ 
+Tak wielkie było jego jedno ramię./ 
+Zważ, jaka całość mogła być niemała,/ 
+Zastosowana do tej części ciała./ 
+Jeśli tak piękny był, jak teraz szpetny,/ 
+Kiedy od Stwórcy odwrócił wzrok świetny,/ 
+Grzew, wszelki zakał musi iść od niego./ 
+Dziw! Głowa jego kształtu potwornego,/ 
+Na trzech obliczach razem osadzona:<pr><slowo_obce>głowa jego kształtu potwornego, na trzech obliczach razem osadzona</slowo_obce> --- Dis albo Lucyfer, pierwszy rodzic grzechu. Widzimy go tutaj jako ukaranego i jako narzędzie kary. Ma on trzy głowy, które podług wykładu komentatorów Dantego symbolem są trzech ówcześnie znajomych części świata, a razem oznaczają powszechność grzechu i panowanie Lucyfera na ziemi. Barwa czerwona ma oznaczać Europę (której mieszkańcy mają cerę rumianą), żółta Azję, czarna Afrykę. Inni rozumieją przez potrójną barwę jego twarzy gniew, łakomstwo i lenistwo; wierzch zaś, czyli czub głowy, ma oznaczać pychę, przez którą szczególnie Lucyfer panowanie swoje rozszerzył i ugruntował.</pr>/ 
+Pierwsza twarz była jako żar czerwona,/ 
+Dwie zaś policzkiem do pierwszej przypadły;/ 
+Obie na środku dwóch ramion usiadły,/ 
+Schodząc się z sobą aż pod wierzchem głowy;/ 
+Oblicze prawe biało-żółtej barwy,/ 
+Jaką mieszkaniec dziwi nadnilowy./ 
+Pod każdą twarzą tej potwornej larwy,/ 
+Jak z okrętowych żagli płachta jaka,/ 
+<begin id="b1333753870142-1969947272"/><motyw id="m1333753870142-1969947272">Wiatr</motyw>W miarę wielkości tak dziwnego ptaka,/ 
+Sterczą dwa skrzydła, lecz bez piór, bez pierza,/ 
+Całe skórzane jak u nietoperza./ 
+I nieustannym swych skrzydeł trzepotem/ 
+Wiał na trzy strony trzy wiatry z łoskotem,/ 
+Od których marzły kocytowe lody./ 
+<end id="e1333753870142-1969947272"/>Sześcioro oczu miał, z tych każde oko/ 
+Nie łzami, krwawą płakało posoką,/ 
+Która spływała jak łza na trzy brody./ 
+I trzech grzeszników przeżuwał jak zwierze,/ 
+Każdego żuła osobna paszczęka,/ 
+Jako cierlica drze lniane paździerze./ 
+Lecz ząb łagodniej kąsał porównany/ 
+Z szponami, jakie zadawały rany,/ 
+Zdało się, skóra aż do kości pęka.<end id="e1315911576539-159505112"/>/ 
+«Duch, co największe bodaj cierpi męki./ 
+Którego wewnątrz czarnej paszczy głowa,/ 
+A sam na zewnątrz jej nogami miota»/ 
+Mistrz mówił, «oto Judasz Iskariota!/ 
+Dwaj, co głowami zwisają z paszczęki,/ 
+Pierwszy to Brutus! choć ból rzeczywisty/ 
+Szarpie go, jednak milczy jak niemowa;/ 
+Drugi, patrz dobrze, to Kasjusz barczysty<pr><slowo_obce>Pierwszy to Brutus (...) Drugi (...) to Kasjusz barczysty</slowo_obce> --- Brutus i Kasjusz, zdrajcy i zabójcy Cezara. Miejsce, jakie im poeta przed innymi zdrajcami tu oznacza, objaśnia własnym przekonaniem: że cesarstwo rzymskie ugruntowane było z bezpośredniej woli bożej, ażeby świeckie wszechwładztwo i poszanowanie dla niego zaszczepić na ziemi. To przekonanie poeta wyraził nie tylko w osobnym traktacie swoim o monarchii, ale i uczynił je jedną z myśli przewodnich swej <tytul_dziela>Boskiej komedii</tytul_dziela>.</pr>./ 
+<begin id="b1315911737980-303128279"/><motyw id="m1315911737980-303128279">Potwór, Diabeł</motyw>Noc już powraca, teraz czas iść dalej,/ 
+Bośmy już w piekle wszystko oglądali»./ 
+Jak chciał, jam jemu na szyi zawisnął./ 
+W chwili, gdy potwór swe skrzydła roztacza,/ 
+Szybki jak piorun, co już spadł, nim błysnął./ 
+Mistrz się uczepił do boków kudłacza,/ 
+Z kudłów na kudły śliznął się pięściami,/ 
+Między ich runem spadał a lodami./ 
+Gdyśmy już doszli do miejsca, o cudo!/ 
+Tam, gdzie pod biodra rozszerza się udo,/ 
+Mój wódz, jak gdyby wpadł na fortel nowy,/ 
+Gdzie były nogi, przewrócił wierzch głowy<pr><slowo_obce>Mistrz się uczepił do boków kudłacza (...) przewrócił wierzch głowy</slowo_obce> --- Sięgając wyobraźnią naszą aż do środka ziemi, znajdujemy stosowne, że Wergiliusz ślizgając się po kudłach Lucyfera, w tym punkcie obrócił tam nogi, gdzie był wierzch głowy, chociaż bez przerwy w tymże samym kierunku się wspinał. Równie znajdujemy stosowne, że środek Lucyfera jest zarazem środkowym punktem ziemi i że poeci wchodząc po nim, gdy doszli przeciwległego punktu, widzieli stopy Lucyfera wywrócone do góry. Moralne znaczenie tego plastycznego obrazu wygląda dość przeźroczysto: człowiek, który swój błąd lub grzech poznał, a potem pragnie z niego się wyzwolić, musi, chcąc dojść do pożądanego celu, w zupełnie przeciwległym kierunku postępować naprzód. Musi złą zasadę mieć pod sobą, a od chwili, w której ją poznał, wchodzić coraz wyżej.</pr>,/ 
+Piął się po kudłach, aż trzęsły mną dreszcze,/ 
+Myśląc, że nazad idę w piekło jeszcze.<end id="e1315911737980-303128279"/>/ 
+«Trzymaj się dobrze, tą chyba drabiną»/ 
+Mówił wódz, dysząc z trudu i pośpiechu,/ 
+«Możemy zstąpić z tego gniazda grzechu»./ 
+<begin id="b1315911771815-1415539463"/><motyw id="m1315911771815-1415539463">Strach</motyw>I wkrótce wyszedł skały rozpadliną,/ 
+Stanął, odetchnął piersią i co żywo/ 
+Roztropną stopę podstawił, i na nią/ 
+Rad mnie wysadził nad samą otchłanią./ 
+Podniosłem oczy i widziałem dziwo!/ 
+Wspak przewróconą postać Lucyfera,/ 
+Rzekłbyś, nogami w powietrze się wpiera./ 
+Czy byłem w strachu, niechaj ludzie prości/ 
+Zgadną, co nigdy z takiej wysokości/ 
+Schodzić nie mogli!<end id="e1315911771815-1415539463"/> Gdym ochłonął z trwogi,/ 
+Mistrz mówił do mnie: «Teraz wstań na nogi;/ 
+Droga daleka, a ścieżki nużące,/ 
+Już gwiazdy nocne, wschodząc, płoszy słońce»./ 
+Tam droga, którą miałem iść na nowo,/ 
+Nie była prostą ulicą zamkową,/ 
+Raczej jaskinią, co ma wejście krzywe,/ 
+Ściany chropawe, a światło wątpliwe./ 
+<begin id="b1333754529600-1431201780"/><motyw id="m1333754529600-1431201780">Szatan, Obraz świata</motyw>«Mistrzu mój,» rzekłem, «gdym wart twego względu,/ 
+O przemów do mnie, wyprowadź mnie z błędu,/ 
+Gdzie są te lody? Ich grubą powłoką/ 
+Jak tam Lucyfer zapadł tak głęboko?/ 
+I jak to słońce, szybkość niesłychana,/ 
+Przebiegło drogę od wczoraj do rana<pr><slowo_obce>jak to słońce, szybkość niesłychana, przebiegło drogę od wczoraj do rana</slowo_obce> --- Nim poeci przeszli środkowy punkt ziemi, Wergiliusz mówił: ,,Noc się przybliża" potem powiada: ,,I słońce wschodzące płoszy gwiazdy". Ta pozorna sprzeczność daje się tak tłumaczyć: uprzednio mówiąc, myślał Wergiliusz o wschodniej, a teraz, gdy przeszedł punkt środkowy ziemi, myśli o zachodniej półkuli, to jest o antypodach, u których już ranek świta, kiedy u nas noc nadchodzi.</pr>?»/ 
+A mistrz: «Myśl twoja jeszcze za punkt lata,/ 
+<begin id="b1333754362850-1149113674"/><motyw id="m1333754362850-1149113674">Robak</motyw>Gdzie stoi szczecią potwora kudłata,/ 
+Robak, co wierci i toczy rdzeń świata./ 
+<end id="e1333754362850-1149113674"/>Ilem w dół schodził, byłeś tam o tyle,/ 
+Gdym się obrócił, przeszedłeś w tę chwilę/ 
+Punkt, do którego ze wszech stron zebrane/ 
+Wszystkie ciężary ciężą pociągane./ 
+Ty pod półkulę zstąpiłeś z kolei,/ 
+Co przeciwległą jest względem Judei,/ 
+Wielkiej pustyni, wśród której oazy/ 
+Poczęty człowiek żył i zmarł bez zmazy<pr><slowo_obce>poczęty człowiek żył i zmarł bez zmazy</slowo_obce> --- Jezus Chrystus, którego święte imię poeta w piekle tylko przez peryfrazę wspomina.</pr>./ 
+Gdy tam jest wieczór, tu nam ranek świeci:/ 
+Ten, po którego szczeblowałem<pe><slowo_obce>szczeblować</slowo_obce> --- wchodzić po stopniach (szczeblach).</pe> szczeci<pe><slowo_obce>szczeć</slowo_obce> --- szczecina, sierść, włosy.</pe>,/
+Jak stał, tak stoi wbity między lody./ 
+Strącony, tędy snadź on z nieba spadał<pr><slowo_obce>Strącony, tędy snadź on z nieba spadał</slowo_obce> --- Tu Dante wyobraża sobie, że Lucyfer na nieznajomą jeszcze za jego czasów stronę kuli ziemskiej i której jasnowidzeniem swojego geniuszu mógł się domyślać, spadł z nieba, że ląd stały przestraszony jego upadkiem, skrył się pod powierzchnię Oceanu i wynurzył się z głębokości jego na wschodniej półkuli, na której Góra Syjon tworzy punkt przeciwległy. Niemniej wszakże poruszyła się przestraszona ziemia w swoich wnętrznościach, kiedy Lucyfer spadając aż do jej środka sam sobą ją przewiercił. Część ziemi, jaką wyrzucił wiercąc ją sobą, utworzyła górę czyśćcową, ląd jedyny, jaki według pomysłu poety na owej półkuli się znajduje. W środku zaś ziemi jest piekło, z którego poeci w tej chwili wychodzą.</pr>,/ 
+Ląd, co z tej strony pokazał się wprzódy,/ 
+Ze strachu pasem otoczył się wody;/ 
+Od Lucyfera uciekając może,/ 
+Gdy bliżej naszej półkuli osiadał,/ 
+Zostawił tutaj to próżne wydroże./ 
+Jest tam nieznane miejsce dla nas obu<pr><slowo_obce>Jest tam nieznane miejsce dla nas obu</slowo_obce> --- Tu wskazuje górę czyśćcową.</pr>,/ 
+Stąd tak odlegle, wzniesione wysoko,/ 
+Jak cały przestwór Belzebuba grobu./ 
+<end id="e1333754529600-1431201780"/>Kędy jest, trudno go poznać na oko,/ 
+Chyba po szmerze małego strumyka<pr><slowo_obce>Chyba po szmerze małego strumyka</slowo_obce> --- Ponieważ na przeciwległej stronie kuli ziemskiej nie ma innego lądu prócz góry czyśćcowej, więc stamtąd spływać musi ten strumień korytem pochyłym i krętym. W pieśni XIV widzieliśmy, że łzy spadają przez szczeliny olbrzymiego posągu stojącego w grocie góry Idy. Możemy stąd wnioskować, że z łez dusz pokutujących na górze czyśćcowej utworzył się ten strumień.</pr>,/ 
+Który otworem przez siebie wyrżniętym,/ 
+Środkiem tej skały swe fale pomyka,/ 
+Płynąc korytem pochyłym i krętym»./ 
+Wódz i ja w otwór wstąpiliśmy ciasny;/ 
+Zniecierpliwieni oglądać świat jasny,/ 
+Nic się nie troszcząc o trud naszej jazdy,/ 
+Szliśmy bez przerwy, on pierwszy, ja drugi;/ 
+Przez otwór błysły niebios piękne smugi,/ 
+W końcu wychodząc, witaliśmy gwiazdy<pr><slowo_obce>wychodząc witaliśmy gwiazdy</slowo_obce> --- Każda część <tytul_dziela>Boskiej komedii</tytul_dziela> kończy się słowem: <wyroznienie>gwiazda</wyroznienie>, która tu jest symbolem naszego nieśmiertelnego ducha wchodzącego coraz wyżej do najwyższego dobra i wiekuistej prawdy, jakimi są: Niebo i Bóg! Myśl ta w tym symbolu ukryta głównym jest celem i ostatnim wyrazem tej arcy chrześcijańskiej epopei Dantowskiej.</pr>.</strofa> 
+
+
+</liryka_l></utwor>
\ No newline at end of file
diff --git a/src/wiki/tests/xslt/auto/data/unknown_tag.html b/src/wiki/tests/xslt/auto/data/unknown_tag.html
new file mode 100644 (file)
index 0000000..9ce58f8
--- /dev/null
@@ -0,0 +1 @@
+<span xmlns="http://www.w3.org/1999/xhtml" class="a unknown-tag" x-node="a" x-attr-value-idp14066432="c" x-attr-name-idp14066432="b" data-wlf-b="c">ala</span>
\ No newline at end of file
diff --git a/src/wiki/tests/xslt/auto/data/unknown_tag.xml b/src/wiki/tests/xslt/auto/data/unknown_tag.xml
new file mode 100644 (file)
index 0000000..91e05ac
--- /dev/null
@@ -0,0 +1 @@
+<a b="c">d</a>
\ No newline at end of file
diff --git a/src/wiki/urls.py b/src/wiki/urls.py
new file mode 100644 (file)
index 0000000..0c73aed
--- /dev/null
@@ -0,0 +1,31 @@
+# -*- coding: utf-8
+from django.conf.urls import patterns, url
+
+
+urlpatterns = patterns('wiki.views',
+    url(r'^edit/(?P<slug>[^/]+)/(?:(?P<chunk>[^/]+)/)?$',
+        'editor', name="wiki_editor"),
+
+    url(r'^readonly/(?P<slug>[^/]+)/(?:(?P<chunk>[^/]+)/)?$',
+        'editor_readonly', name="wiki_editor_readonly"),
+
+    url(r'^gallery/(?P<directory>[^/]+)/$',
+        'gallery', name="wiki_gallery"),
+
+    url(r'^history/(?P<chunk_id>\d+)/$',
+        'history', name="wiki_history"),
+
+    url(r'^rev/(?P<chunk_id>\d+)/$',
+        'revision', name="wiki_revision"),
+
+    url(r'^text/(?P<chunk_id>\d+)/$',
+        'text', name="wiki_text"),
+
+    url(r'^revert/(?P<chunk_id>\d+)/$',
+        'revert', name='wiki_revert'),
+
+    url(r'^diff/(?P<chunk_id>\d+)/$', 'diff', name="wiki_diff"),
+    url(r'^pubmark/(?P<chunk_id>\d+)/$', 'pubmark', name="wiki_pubmark"),
+
+    url(r'^themes$', 'themes', name="themes"),
+)
diff --git a/src/wiki/views.py b/src/wiki/views.py
new file mode 100644 (file)
index 0000000..e1ef6ae
--- /dev/null
@@ -0,0 +1,315 @@
+# -*- coding: utf-8 -*-
+from datetime import datetime
+import os
+import logging
+from time import mktime
+import urllib
+
+from django.conf import settings
+from django.core.urlresolvers import reverse
+from django import http
+from django.http import Http404, HttpResponseForbidden
+from django.middleware.gzip import GZipMiddleware
+from django.utils.decorators import decorator_from_middleware
+from django.utils.encoding import smart_unicode
+from django.utils.formats import localize
+from django.utils.translation import ugettext as _
+from django.views.decorators.http import require_POST, require_GET
+from django.shortcuts import get_object_or_404, render
+
+from catalogue.models import Book, Chunk
+import nice_diff
+from wiki import forms
+from wiki.helpers import (JSONResponse, JSONFormInvalid, JSONServerError,
+                ajax_require_permission)
+from wiki.models import Theme
+
+#
+# Quick hack around caching problems, TODO: use ETags
+#
+from django.views.decorators.cache import never_cache
+
+logger = logging.getLogger("fnp.wiki")
+
+MAX_LAST_DOCS = 10
+
+
+@never_cache
+def editor(request, slug, chunk=None, template_name='wiki/document_details.html'):
+    try:
+        chunk = Chunk.get(slug, chunk)
+    except Chunk.MultipleObjectsReturned:
+        # TODO: choice page
+        raise Http404
+    except Chunk.DoesNotExist:
+        if chunk is None:
+            try:
+                book = Book.objects.get(slug=slug)
+            except Book.DoesNotExist:
+                return http.HttpResponseRedirect(reverse("catalogue_create_missing", args=[slug]))
+        else:
+            raise Http404
+    if not chunk.book.accessible(request):
+        return HttpResponseForbidden("Not authorized.")
+
+    access_time = datetime.now()
+    last_books = request.session.get("wiki_last_books", {})
+    last_books[reverse(editor, args=[chunk.book.slug, chunk.slug])] = {
+        'time': mktime(access_time.timetuple()),
+        'title': chunk.pretty_name(),
+        }
+
+    if len(last_books) > MAX_LAST_DOCS:
+        oldest_key = min(last_books, key=lambda x: last_books[x]['time'])
+        del last_books[oldest_key]
+    request.session['wiki_last_books'] = last_books
+
+    return render(request, template_name, {
+        'chunk': chunk,
+        'forms': {
+            "text_save": forms.DocumentTextSaveForm(user=request.user, prefix="textsave"),
+            "text_revert": forms.DocumentTextRevertForm(prefix="textrevert"),
+            "pubmark": forms.DocumentPubmarkForm(prefix="pubmark"),
+        },
+        'can_pubmark': request.user.has_perm('catalogue.can_pubmark'),
+        'REDMINE_URL': settings.REDMINE_URL,
+    })
+
+
+@require_GET
+def editor_readonly(request, slug, chunk=None, template_name='wiki/document_details_readonly.html'):
+    try:
+        chunk = Chunk.get(slug, chunk)
+        revision = request.GET['revision']
+    except (Chunk.MultipleObjectsReturned, Chunk.DoesNotExist, KeyError):
+        raise Http404
+    if not chunk.book.accessible(request):
+        return HttpResponseForbidden("Not authorized.")
+
+    access_time = datetime.now()
+    last_books = request.session.get("wiki_last_books", {})
+    last_books[slug, chunk.slug] = {
+        'time': mktime(access_time.timetuple()),
+        'title': chunk.book.title,
+        }
+
+    if len(last_books) > MAX_LAST_DOCS:
+        oldest_key = min(last_books, key=lambda x: last_books[x]['time'])
+        del last_books[oldest_key]
+    request.session['wiki_last_books'] = last_books
+
+    return render(request, template_name, {
+        'chunk': chunk,
+        'revision': revision,
+        'readonly': True,
+        'REDMINE_URL': settings.REDMINE_URL,
+    })
+
+
+@never_cache
+@decorator_from_middleware(GZipMiddleware)
+def text(request, chunk_id):
+    doc = get_object_or_404(Chunk, pk=chunk_id)
+    if not doc.book.accessible(request):
+        return HttpResponseForbidden("Not authorized.")
+
+    if request.method == 'POST':
+        form = forms.DocumentTextSaveForm(request.POST, user=request.user, prefix="textsave")
+        if form.is_valid():
+            if request.user.is_authenticated():
+                author = request.user
+            else:
+                author = None
+            text = form.cleaned_data['text']
+            parent_revision = form.cleaned_data['parent_revision']
+            if parent_revision is not None:
+                parent = doc.at_revision(parent_revision)
+            else:
+                parent = None
+            stage = form.cleaned_data['stage_completed']
+            tags = [stage] if stage else []
+            publishable = (form.cleaned_data['publishable'] and
+                    request.user.has_perm('catalogue.can_pubmark'))
+            doc.commit(author=author,
+                       text=text,
+                       parent=parent,
+                       description=form.cleaned_data['comment'],
+                       tags=tags,
+                       author_name=form.cleaned_data['author_name'],
+                       author_email=form.cleaned_data['author_email'],
+                       publishable=publishable,
+                       )
+            revision = doc.revision()
+            return JSONResponse({
+                'text': doc.materialize() if parent_revision != revision else None,
+                'meta': {},
+                'revision': revision,
+            })
+        else:
+            return JSONFormInvalid(form)
+    else:
+        revision = request.GET.get("revision", None)
+        
+        try:
+            revision = int(revision)
+        except (ValueError, TypeError):
+            revision = doc.revision()
+
+        if revision is not None:
+            text = doc.at_revision(revision).materialize()
+        else:
+            text = ''
+
+        return JSONResponse({
+            'text': text,
+            'meta': {},
+            'revision': revision,
+        })
+
+
+@never_cache
+@require_POST
+def revert(request, chunk_id):
+    form = forms.DocumentTextRevertForm(request.POST, prefix="textrevert")
+    if form.is_valid():
+        doc = get_object_or_404(Chunk, pk=chunk_id)
+        if not doc.book.accessible(request):
+            return HttpResponseForbidden("Not authorized.")
+
+        revision = form.cleaned_data['revision']
+
+        comment = form.cleaned_data['comment']
+        comment += "\n#revert to %s" % revision
+
+        if request.user.is_authenticated():
+            author = request.user
+        else:
+            author = None
+
+        before = doc.revision()
+        logger.info("Reverting %s to %s", chunk_id, revision)
+        doc.at_revision(revision).revert(author=author, description=comment)
+
+        return JSONResponse({
+            'text': doc.materialize() if before != doc.revision() else None,
+            'meta': {},
+            'revision': doc.revision(),
+        })
+    else:
+        return JSONFormInvalid(form)
+
+
+@never_cache
+def gallery(request, directory):
+    try:
+        base_url = ''.join((
+                        smart_unicode(settings.MEDIA_URL),
+                        smart_unicode(settings.IMAGE_DIR),
+                        smart_unicode(directory)))
+
+        base_dir = os.path.join(
+                    smart_unicode(settings.MEDIA_ROOT),
+                    smart_unicode(settings.IMAGE_DIR),
+                    smart_unicode(directory))
+
+        def map_to_url(filename):
+            return urllib.quote(("%s/%s" % (base_url, smart_unicode(filename))).encode('utf-8'))
+
+        def is_image(filename):
+            return os.path.splitext(filename)[1].lower() in (u'.jpg', u'.jpeg', u'.png')
+
+        images = [map_to_url(f) for f in map(smart_unicode, os.listdir(base_dir)) if is_image(f)]
+        images.sort()
+
+        books = Book.objects.filter(gallery=directory)
+
+        if not all(book.public for book in books) and not request.user.is_authenticated():
+            return HttpResponseForbidden("Not authorized.")
+
+        return JSONResponse(images)
+    except (IndexError, OSError):
+        logger.exception("Unable to fetch gallery")
+        raise http.Http404
+
+
+@never_cache
+def diff(request, chunk_id):
+    revA = int(request.GET.get('from', 0))
+    revB = int(request.GET.get('to', 0))
+
+    if revA > revB:
+        revA, revB = revB, revA
+
+    if revB == 0:
+        revB = None
+
+    doc = get_object_or_404(Chunk, pk=chunk_id)
+    if not doc.book.accessible(request):
+        return HttpResponseForbidden("Not authorized.")
+
+    # allow diff from the beginning
+    if revA:
+        docA = doc.at_revision(revA).materialize()
+    else:
+        docA = ""
+    docB = doc.at_revision(revB).materialize()
+
+    return http.HttpResponse(nice_diff.html_diff_table(docA.splitlines(),
+                                         docB.splitlines(), context=3))
+
+
+@never_cache
+def revision(request, chunk_id):
+    doc = get_object_or_404(Chunk, pk=chunk_id)
+    if not doc.book.accessible(request):
+        return HttpResponseForbidden("Not authorized.")
+    return http.HttpResponse(str(doc.revision()))
+
+
+@never_cache
+def history(request, chunk_id):
+    # TODO: pagination
+    doc = get_object_or_404(Chunk, pk=chunk_id)
+    if not doc.book.accessible(request):
+        return HttpResponseForbidden("Not authorized.")
+
+    changes = []
+    for change in doc.history().reverse():
+        changes.append({
+                "version": change.revision,
+                "description": change.description,
+                "author": change.author_str(),
+                "date": localize(change.created_at),
+                "publishable": _("Publishable") + "\n" if change.publishable else "",
+                "tag": ',\n'.join(unicode(tag) for tag in change.tags.all()),
+                "published": _("Published") + ": " + \
+                    localize(change.publish_log.order_by('-book_record__timestamp')[0].book_record.timestamp) \
+                    if change.publish_log.exists() else "",
+            })
+    return JSONResponse(changes)
+
+
+@require_POST
+@ajax_require_permission('catalogue.can_pubmark')
+def pubmark(request, chunk_id):
+    form = forms.DocumentPubmarkForm(request.POST, prefix="pubmark")
+    if form.is_valid():
+        doc = get_object_or_404(Chunk, pk=chunk_id)
+        if not doc.book.accessible(request):
+            return HttpResponseForbidden("Not authorized.")
+
+        revision = form.cleaned_data['revision']
+        publishable = form.cleaned_data['publishable']
+        change = doc.at_revision(revision)
+        if publishable != change.publishable:
+            change.set_publishable(publishable)
+            return JSONResponse({"message": _("Revision marked")})
+        else:
+            return JSONResponse({"message": _("Nothing changed")})
+    else:
+        return JSONFormInvalid(form)
+
+
+def themes(request):
+    prefix = request.GET.get('q', '')
+    return http.HttpResponse('\n'.join([str(t) for t in Theme.objects.filter(name__istartswith=prefix)]))
diff --git a/src/wiki_img/__init__.py b/src/wiki_img/__init__.py
new file mode 100644 (file)
index 0000000..c53f0e7
--- /dev/null
@@ -0,0 +1 @@
+  # pragma: no cover
diff --git a/src/wiki_img/forms.py b/src/wiki_img/forms.py
new file mode 100644 (file)
index 0000000..555f264
--- /dev/null
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+#
+# This file is part of FNP-Redakcja, 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 wiki.forms import DocumentTextSaveForm
+from catalogue.models import Image
+
+
+class ImageSaveForm(DocumentTextSaveForm):
+    """Form for saving document's text."""
+
+    stage_completed = forms.ModelChoiceField(
+        queryset=Image.tag_model.objects.all(),
+        required=False,
+        label=_(u"Completed"),
+        help_text=_(u"If you completed a life cycle stage, select it."),
+    )
diff --git a/src/wiki_img/locale/pl/LC_MESSAGES/django.mo b/src/wiki_img/locale/pl/LC_MESSAGES/django.mo
new file mode 100644 (file)
index 0000000..3987728
Binary files /dev/null and b/src/wiki_img/locale/pl/LC_MESSAGES/django.mo differ
diff --git a/src/wiki_img/locale/pl/LC_MESSAGES/django.po b/src/wiki_img/locale/pl/LC_MESSAGES/django.po
new file mode 100644 (file)
index 0000000..432d289
--- /dev/null
@@ -0,0 +1,307 @@
+# 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: Platforma Redakcyjna\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2011-12-14 15:25+0100\n"
+"PO-Revision-Date: 2011-12-14 15:26+0100\n"
+"Last-Translator: Radek Czajka <radoslaw.czajka@nowoczesnapolska.org.pl>\n"
+"Language-Team: Fundacja Nowoczesna Polska <fundacja@nowoczesnapolska.org.pl>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: forms.py:18
+msgid "Completed"
+msgstr "Ukończono"
+
+#: forms.py:19
+msgid "If you completed a life cycle stage, select it."
+msgstr "Jeśli został ukończony etap prac, wskaż go."
+
+#: views.py:126
+msgid "Publishable"
+msgstr "Gotowe do publikacji"
+
+#: templates/wiki_img/base.html:15
+msgid "Platforma Redakcyjna"
+msgstr ""
+
+#: templates/wiki_img/diff_table.html:5
+msgid "Old version"
+msgstr "Stara wersja"
+
+#: templates/wiki_img/diff_table.html:6
+msgid "New version"
+msgstr "Nowa wersja"
+
+#: templates/wiki_img/document_details_base.html:31
+msgid "Help"
+msgstr "Pomoc"
+
+#: templates/wiki_img/document_details_base.html:33
+msgid "Version"
+msgstr "Wersja"
+
+#: templates/wiki_img/document_details_base.html:33
+msgid "Unknown"
+msgstr "nieznana"
+
+#: templates/wiki_img/document_details_base.html:35
+#: templates/wiki_img/tag_dialog.html:15
+msgid "Save"
+msgstr "Zapisz"
+
+#: templates/wiki_img/document_details_base.html:36
+msgid "Save attempt in progress"
+msgstr "Trwa zapisywanie"
+
+#: templates/wiki_img/document_details_base.html:37
+msgid "There is a newer version of this document!"
+msgstr "Istnieje nowsza wersja tego dokumentu!"
+
+#: templates/wiki_img/tag_dialog.html:16
+msgid "Cancel"
+msgstr "Anuluj"
+
+#: templates/wiki_img/tabs/history_view.html:5
+msgid "Compare versions"
+msgstr "Porównaj wersje"
+
+#: templates/wiki_img/tabs/history_view.html:8
+msgid "Mark for publishing"
+msgstr "Oznacz do publikacji"
+
+#: templates/wiki_img/tabs/history_view.html:11
+msgid "Revert document"
+msgstr "Przywróć wersję"
+
+#: templates/wiki_img/tabs/history_view.html:14
+msgid "View version"
+msgstr "Zobacz wersję"
+
+#: templates/wiki_img/tabs/motifs_editor.html:4
+#: templates/wiki_img/tabs/motifs_editor_item.html:3
+msgid "Motifs"
+msgstr "Motywy"
+
+#: templates/wiki_img/tabs/motifs_editor.html:5
+#: templates/wiki_img/tabs/objects_editor.html:5
+msgid "Add"
+msgstr "Dodaj"
+
+#: templates/wiki_img/tabs/objects_editor.html:4
+msgid "Object name"
+msgstr "Nazwa obiektu"
+
+#: templates/wiki_img/tabs/objects_editor_item.html:3
+msgid "Objects"
+msgstr "Obiekty"
+
+#: templates/wiki_img/tabs/source_editor_item.html:5
+msgid "Source code"
+msgstr "Kod źródłowy"
+
+#: templates/wiki_img/tabs/summary_view.html:8
+msgid "Title"
+msgstr "Tytuł"
+
+#: templates/wiki_img/tabs/summary_view.html:13
+msgid "Document ID"
+msgstr "ID dokumentu"
+
+#: templates/wiki_img/tabs/summary_view.html:17
+msgid "Current version"
+msgstr "Aktualna wersja"
+
+#: templates/wiki_img/tabs/summary_view.html:20
+msgid "Last edited by"
+msgstr "Ostatnio edytowane przez"
+
+#: templates/wiki_img/tabs/summary_view_item.html:4
+msgid "Summary"
+msgstr "Podsumowanie"
+
+#~ msgid "First correction"
+#~ msgstr "Autokorekta"
+
+#~ msgid "Tagging"
+#~ msgstr "Tagowanie"
+
+#~ msgid "Initial Proofreading"
+#~ msgstr "Korekta"
+
+#~ msgid "Annotation Proofreading"
+#~ msgstr "Sprawdzenie przypisów źródła"
+
+#~ msgid "Modernisation"
+#~ msgstr "Uwspółcześnienie"
+
+#~ msgid "Annotations"
+#~ msgstr "Przypisy"
+
+#~ msgid "Themes"
+#~ msgstr "Motywy"
+
+#~ msgid "Editor's Proofreading"
+#~ msgstr "Ostateczna redakcja literacka"
+
+#~ msgid "Technical Editor's Proofreading"
+#~ msgstr "Ostateczna redakcja techniczna"
+
+#~ msgid "ZIP file"
+#~ msgstr "Plik ZIP"
+
+#~ msgid "Author"
+#~ msgstr "Autor"
+
+#~ msgid "Your name"
+#~ msgstr "Imię i nazwisko"
+
+#~ msgid "Author's email"
+#~ msgstr "E-mail autora"
+
+#~ msgid "Your email address, so we can show a gravatar :)"
+#~ msgstr "Adres e-mail, żebyśmy mogli pokazać gravatar :)"
+
+#~ msgid "Your comments"
+#~ msgstr "Twój komentarz"
+
+#~ msgid "Describe changes you made."
+#~ msgstr "Opisz swoje zmiany"
+
+#~ msgid "Finished stage: %s"
+#~ msgstr "Ukończony etap: %s"
+
+#~ msgid "name"
+#~ msgstr "nazwa"
+
+#~ msgid "theme"
+#~ msgstr "motyw"
+
+#~ msgid "themes"
+#~ msgstr "motywy"
+
+#~ msgid "Title already used for %s"
+#~ msgstr "Nazwa taka sama jak dla pliku %s"
+
+#~ msgid "Title already used in repository."
+#~ msgstr "Plik o tej nazwie już istnieje w repozytorium."
+
+#~ msgid "File should be UTF-8 encoded."
+#~ msgstr "Plik powinien mieć kodowanie UTF-8."
+
+#~ msgid "Tag added"
+#~ msgstr "Dodano tag"
+
+#~ msgid "Create document"
+#~ msgstr "Utwórz dokument"
+
+#~ msgid "Click to open/close gallery"
+#~ msgstr "Kliknij, aby (ro)zwinąć galerię"
+
+#~ msgid "Clear filter"
+#~ msgstr "Wyczyść filtr"
+
+#~ msgid "Your last edited documents"
+#~ msgstr "Twoje ostatnie edycje"
+
+#~ msgid "Bulk documents upload"
+#~ msgstr "Hurtowe dodawanie dokumentów"
+
+#~ msgid ""
+#~ "Please submit a ZIP with UTF-8 encoded XML files. Files not ending with "
+#~ "<code>.xml</code> will be ignored."
+#~ msgstr ""
+#~ "Proszę wskazać archiwum ZIP z plikami XML w kodowaniu UTF-8. Pliki nie "
+#~ "kończące się na <code>.xml</code> zostaną zignorowane."
+
+#~ msgid "Upload"
+#~ msgstr "Dodaj"
+
+#~ msgid ""
+#~ "There have been some errors. No files have been added to the repository."
+#~ msgstr "Wystąpiły błędy. Żadne pliki nie zostały dodane do repozytorium."
+
+#~ msgid "Offending files"
+#~ msgstr "Błędne pliki"
+
+#~ msgid "Correct files"
+#~ msgstr "Poprawne pliki"
+
+#~ msgid "Files have been successfully uploaded to the repository."
+#~ msgstr "Pliki zostały dodane do repozytorium."
+
+#~ msgid "Uploaded files"
+#~ msgstr "Dodane pliki"
+
+#~ msgid "Skipped files"
+#~ msgstr "Pominięte pliki"
+
+#~ msgid "Files skipped due to no <code>.xml</code> extension"
+#~ msgstr "Pliki pominięte z powodu braku rozszerzenia <code>.xml</code>."
+
+#~ msgid "Refresh"
+#~ msgstr "Odśwież"
+
+#~ msgid "Previous"
+#~ msgstr "Poprzednie"
+
+#~ msgid "Next"
+#~ msgstr "Następne"
+
+#~ msgid "Zoom in"
+#~ msgstr "Powiększ"
+
+#~ msgid "Zoom out"
+#~ msgstr "Zmniejsz"
+
+#~ msgid "Gallery"
+#~ msgstr "Galeria"
+
+#~ msgid "Mark version"
+#~ msgstr "Oznacz wersję"
+
+#~ msgid "History"
+#~ msgstr "Historia"
+
+#~ msgid "Search"
+#~ msgstr "Szukaj"
+
+#~ msgid "Replace with"
+#~ msgstr "Zamień na"
+
+#~ msgid "Replace"
+#~ msgstr "Zamień"
+
+#~ msgid "Options"
+#~ msgstr "Opcje"
+
+#~ msgid "Case sensitive"
+#~ msgstr "Rozróżniaj wielkość liter"
+
+#~ msgid "From cursor"
+#~ msgstr "Zacznij od kursora"
+
+#~ msgid "Search and replace"
+#~ msgstr "Znajdź i zamień"
+
+#~ msgid "Link to gallery"
+#~ msgstr "Link do galerii"
+
+#~ msgid "Insert theme"
+#~ msgstr "Wstaw motyw"
+
+#~ msgid "Insert annotation"
+#~ msgstr "Wstaw przypis"
+
+#~ msgid "Insert special character"
+#~ msgstr "Wstaw znak specjalny"
+
+#~ msgid "Visual editor"
+#~ msgstr "Edytor wizualny"
diff --git a/src/wiki_img/models.py b/src/wiki_img/models.py
new file mode 100644 (file)
index 0000000..b685324
--- /dev/null
@@ -0,0 +1,5 @@
+# -*- coding: utf-8 -*-
+#
+# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
diff --git a/src/wiki_img/templates/wiki_img/diff_table.html b/src/wiki_img/templates/wiki_img/diff_table.html
new file mode 100644 (file)
index 0000000..818c38c
--- /dev/null
@@ -0,0 +1,21 @@
+{% load i18n %}
+<table class="diff_table">
+       <thead>
+               <tr>
+                       <th colspan="2">{% trans "Old version" %}</th>
+                       <th colspan="2">{% trans "New version" %}</th>
+               </tr>
+       </thead>
+<tbody>
+{% for an, a, bn, b, has_change in changes %}
+
+<tr class="{% if has_change %}change{% endif %}">
+<td>{{an}}</td>
+<td class="left">{{ a|safe }}&nbsp;</td>
+<td>{{bn}}</td>
+<td class="right">{{ b|safe }}&nbsp;</td>
+</tr>
+
+{% endfor %}
+</tbody>
+</table>
\ No newline at end of file
diff --git a/src/wiki_img/templates/wiki_img/document_details.html b/src/wiki_img/templates/wiki_img/document_details.html
new file mode 100644 (file)
index 0000000..0dfc9ae
--- /dev/null
@@ -0,0 +1,38 @@
+{% extends "wiki_img/document_details_base.html" %}
+{% load i18n %}
+
+{% block extrabody %}
+{{ block.super }}
+<script src="{{ STATIC_URL }}js/lib/codemirror-0.8/codemirror.js" type="text/javascript" charset="utf-8">
+</script>
+<script src="{{ STATIC_URL }}js/wiki_img/loader.js" type="text/javascript" charset="utf-8"> </script>
+{% endblock %}
+
+{% block tabs-menu %}
+    {% include "wiki_img/tabs/summary_view_item.html" %}
+    {% include "wiki_img/tabs/motifs_editor_item.html" %}
+    {% include "wiki_img/tabs/objects_editor_item.html" %}
+    {% include "wiki_img/tabs/source_editor_item.html" %}
+    {% include "wiki/tabs/history_view_item.html" %}
+{% endblock %}
+
+{% block tabs-content %}
+    {% include "wiki_img/tabs/summary_view.html" %}
+    {% include "wiki_img/tabs/motifs_editor.html" %}
+    {% include "wiki_img/tabs/objects_editor.html" %}
+    {% include "wiki_img/tabs/source_editor.html" %}
+    {% include "wiki_img/tabs/history_view.html" %}
+{% endblock %}
+
+{% block dialogs %}
+    {% include "wiki/save_dialog.html" %}
+    {% include "wiki/revert_dialog.html" %}
+    {% if can_pubmark %}
+        {% include "wiki/pubmark_dialog.html" %}
+    {% endif %}
+{% endblock %}
+
+{% block editor-class %}
+    sideless
+{% endblock %}
+
diff --git a/src/wiki_img/templates/wiki_img/document_details_base.html b/src/wiki_img/templates/wiki_img/document_details_base.html
new file mode 100644 (file)
index 0000000..0bb58b2
--- /dev/null
@@ -0,0 +1,52 @@
+{% extends "base.html" %}
+{% load toolbar_tags i18n %}
+
+{% block title %}{{ document.title }} - {{ block.super }}{% endblock %}
+{% block extrahead %}
+{% load pipeline %}
+{% stylesheet 'detail' %}
+{% endblock %}
+
+{% block extrabody %}
+<script type="text/javascript">
+    var STATIC_URL = '{{STATIC_URL}}';
+</script>
+{% javascript 'wiki_img' %}
+{% endblock %}
+
+{% block maincontent %}
+<div id="document-meta"
+       data-object-id="{{ document.pk }}" style="display:none">
+
+       <span data-key="revision">{{ revision }}</span>
+    <span data-key="diff">{{ request.GET.diff }}</span>
+
+       {% block meta-extra %} {% endblock %}
+</div>
+
+<div id="header">
+    <h1><a href="{% url 'catalogue_document_list' %}"><img alt="Home" src="{{STATIC_URL}}icons/go-home.png"/></a><a href="{% url 'catalogue_document_list' %}">Strona<br/>główna</a></h1>
+    <div id="tools">
+        <a href="{{ REDMINE_URL }}projects/wl-publikacje/wiki/Pomoc" target="_blank">
+        {% trans "Help" %}</a>
+        | {% include "registration/head_login.html" %}
+        | {% trans "Version" %}: <span id="document-revision">{% trans "Unknown" %}</span>
+               {% if not readonly %}
+            | <button style="margin-left: 6px" id="save-button">{% trans "Save" %}</button>
+                       <span id='save-attempt-info'>{% trans "Save attempt in progress" %}</span>
+            <span id='out-of-date-info'>{% trans "There is a newer version of this document!" %}</span>
+               {% endif %}
+    </div>
+    <ol id="tabs" class="tabs">
+       {% block tabs-menu %} {% endblock %}
+    </ol>
+</div>
+<div id="splitter">
+    <div id="editor" class="{% block editor-class %} {% endblock %}">
+       {% block tabs-content %} {% endblock %}
+    </div>
+</div>
+
+{% block dialogs %} {% endblock %}
+
+{% endblock %}
diff --git a/src/wiki_img/templates/wiki_img/document_details_readonly.html b/src/wiki_img/templates/wiki_img/document_details_readonly.html
new file mode 100644 (file)
index 0000000..ca38838
--- /dev/null
@@ -0,0 +1,26 @@
+{% extends "wiki_img/document_details_base.html" %}
+{% load i18n %}
+
+{% block extrabody %}
+{{ block.super }}
+<script src="{{ STATIC_URL }}js/lib/codemirror-0.8/codemirror.js" type="text/javascript" charset="utf-8">
+</script>
+<script src="{{ STATIC_URL }}js/wiki_img/loader_readonly.js" type="text/javascript" charset="utf-8"> </script>
+{% endblock %}
+
+{% block tabs-menu %}
+    {% include "wiki_img/tabs/motifs_editor_item.html" %}
+    {% include "wiki_img/tabs/objects_editor_item.html" %}
+    {% include "wiki_img/tabs/source_editor_item.html" %}
+{% endblock %}
+
+{% block tabs-content %}
+    {% include "wiki_img/tabs/motifs_editor.html" %}
+    {% include "wiki_img/tabs/objects_editor.html" %}
+    {% include "wiki_img/tabs/source_editor.html" %}
+{% endblock %}
+
+{% block editor-class %}
+    sideless
+{% endblock %}
+
diff --git a/src/wiki_img/templates/wiki_img/tabs/history_view.html b/src/wiki_img/templates/wiki_img/tabs/history_view.html
new file mode 100755 (executable)
index 0000000..dcb62ec
--- /dev/null
@@ -0,0 +1,40 @@
+{% load i18n %}
+<div id="history-view-editor" class="editor" style="display: none">
+    <div class="toolbar">
+       <button type="button" id="make-diff-button"
+                       data-enabled-when="2" disabled="disabled">{% trans "Compare versions" %}</button>
+        {% if can_pubmark %}
+               <button type="button" id="pubmark-changeset-button"
+                       data-enabled-when="1" disabled="disabled">{% trans "Mark for publishing" %}</button>
+        {% endif %}
+               <button type="button" id="doc-revert-button"
+                       data-enabled-when="1" disabled="disabled">{% trans "Revert document" %}</button>
+               <button id="open-preview-button" disabled="disabled"
+                       data-enabled-when="1"
+                       data-basehref="{% url 'wiki_img_editor_readonly' document.slug %}">{% trans "View version" %}</button>
+
+       </div>
+    <div id="history-view">
+        <p class="message-box" style="display:none;"></p>
+
+               <table id="changes-list-container">
+        <tbody id="changes-list">
+        </tbody>
+               <tbody style="display: none;">
+                       <tr class="entry row-stub">
+                       <td data-stub-value="version"></td>
+                       <td>
+                <span data-stub-value="date"></span>
+               <br/><span data-stub-value="author"></span>
+                               <br />
+                               <span data-stub-value="description"></span>
+                       </td>
+                       <td>
+                <div data-stub-value="publishable"></div>
+                <div data-stub-value="tag"></div>
+                       </td>
+               </tr>
+               </tbody>
+               </table>
+    </div>
+</div>
diff --git a/src/wiki_img/templates/wiki_img/tabs/motifs_editor.html b/src/wiki_img/templates/wiki_img/tabs/motifs_editor.html
new file mode 100644 (file)
index 0000000..c064505
--- /dev/null
@@ -0,0 +1,17 @@
+{% load i18n %}
+<div id="motifs-editor" class="editor" style="display: none">
+    <div class="toolbar">
+        <input class='tag-name' title='{% trans "Motifs" %}' />
+        <button class='add'>{% trans "Add" %}</button>
+
+        <span class="objects-list">
+        </span>
+
+        <div class="toolbar-end">
+        </div>
+    </div>
+
+    <div class='scrolled'>
+        <img src="{{ document.image.url }}" class='area-selectable' alt="Tagged image" />
+    </div>
+</div>
diff --git a/src/wiki_img/templates/wiki_img/tabs/motifs_editor_item.html b/src/wiki_img/templates/wiki_img/tabs/motifs_editor_item.html
new file mode 100644 (file)
index 0000000..a5a3c34
--- /dev/null
@@ -0,0 +1,4 @@
+{% load i18n %}
+<li id="MotifsPerspective" data-ui-related="motifs-editor" data-ui-jsclass="MotifsPerspective">
+    <a href="#">{% trans "Motifs" %}</a>
+</li>
diff --git a/src/wiki_img/templates/wiki_img/tabs/objects_editor.html b/src/wiki_img/templates/wiki_img/tabs/objects_editor.html
new file mode 100644 (file)
index 0000000..b4149e2
--- /dev/null
@@ -0,0 +1,17 @@
+{% load i18n %}
+<div id="objects-editor" class="editor" style="display: none">
+    <div class="toolbar">
+        <input class='tag-name' title='{% trans "Object name" %}' />
+        <button class='add'>{% trans "Add" %}</button>
+
+        <span class="objects-list">
+        </span>
+
+        <div class="toolbar-end">
+        </div>
+    </div>
+
+    <div class='scrolled'>
+        <img src="{{ document.image.url }}" class='area-selectable' alt="Tagged image" />
+    </div>
+</div>
diff --git a/src/wiki_img/templates/wiki_img/tabs/objects_editor_item.html b/src/wiki_img/templates/wiki_img/tabs/objects_editor_item.html
new file mode 100644 (file)
index 0000000..9fc3af9
--- /dev/null
@@ -0,0 +1,4 @@
+{% load i18n %}
+<li id="ObjectsPerspective" data-ui-related="objects-editor" data-ui-jsclass="ObjectsPerspective">
+    <a href="#">{% trans "Objects" %}</a>
+</li>
diff --git a/src/wiki_img/templates/wiki_img/tabs/source_editor.html b/src/wiki_img/templates/wiki_img/tabs/source_editor.html
new file mode 100644 (file)
index 0000000..a1316a7
--- /dev/null
@@ -0,0 +1,4 @@
+{% load toolbar_tags i18n %}
+<div id="source-editor" class="editor">
+    <textarea id="codemirror_placeholder">&lt;br/&gt;</textarea>
+</div>
\ No newline at end of file
diff --git a/src/wiki_img/templates/wiki_img/tabs/source_editor_item.html b/src/wiki_img/templates/wiki_img/tabs/source_editor_item.html
new file mode 100644 (file)
index 0000000..22b6d66
--- /dev/null
@@ -0,0 +1,6 @@
+{% load i18n %}
+<li id="CodeMirrorPerspective"
+       data-ui-related="source-editor"
+       data-ui-jsclass="CodeMirrorPerspective">
+    <a href="#">{% trans "Source code" %}</a>
+</li>
\ No newline at end of file
diff --git a/src/wiki_img/templates/wiki_img/tabs/summary_view.html b/src/wiki_img/templates/wiki_img/tabs/summary_view.html
new file mode 100644 (file)
index 0000000..a908f55
--- /dev/null
@@ -0,0 +1,24 @@
+{% load i18n %}
+{% load wiki %}
+<div id="summary-view-editor" class="editor" style="display: none">
+    <!-- <div class="toolbar">
+    </div> -->
+    <div id="summary-view">
+               <h2>
+                       <label for="title">{% trans "Title" %}:</label>
+                       <span data-ui-editable="true" data-edit-target="meta.displayTitle"
+                       >{{ document.name|wiki_title }}</span>
+               </h2>
+               <p>
+                       <label>{% trans "Document ID" %}:</label>
+                       <span>{{ document.name }}</span>
+               </p>
+               <p>
+                       <label>{% trans "Current version" %}:</label>
+                       {{ document_info.revision }} ({{document_info.date}})
+               <p>
+                       <label>{% trans "Last edited by" %}:</label>
+                       {{document_info.author}}
+               </p>
+       </div>
+</div>
diff --git a/src/wiki_img/templates/wiki_img/tabs/summary_view_item.html b/src/wiki_img/templates/wiki_img/tabs/summary_view_item.html
new file mode 100644 (file)
index 0000000..bae3ea5
--- /dev/null
@@ -0,0 +1,5 @@
+{% load i18n %}
+{% load wiki %}
+<li id="SummaryPerspective" data-ui-related="summary-view-editor" data-ui-jsclass="SummaryPerspective">
+    <a href="#">{% trans "Summary" %}</a>
+</li>
diff --git a/src/wiki_img/tests.py b/src/wiki_img/tests.py
new file mode 100644 (file)
index 0000000..6577737
--- /dev/null
@@ -0,0 +1,19 @@
+from nose.tools import *
+import wiki.models as models
+import shutil
+import tempfile
+
+
+class TestStorageBase:
+    def setUp(self):
+        self.dirpath = tempfile.mkdtemp(prefix='nosetest_')
+
+    def tearDown(self):
+        shutil.rmtree(self.dirpath)
+
+
+class TestDocumentStorage(TestStorageBase):
+
+    def test_storage_empty(self):
+        storage = models.DocumentStorage(self.dirpath)
+        eq_(storage.all(), [])
diff --git a/src/wiki_img/urls.py b/src/wiki_img/urls.py
new file mode 100644 (file)
index 0000000..6a516f3
--- /dev/null
@@ -0,0 +1,24 @@
+# -*- coding: utf-8
+from django.conf.urls import patterns, url
+
+
+urlpatterns = patterns('wiki_img.views',
+    url(r'^edit/(?P<slug>[^/]+)/$',
+        'editor', name="wiki_img_editor"),
+
+    url(r'^readonly/(?P<slug>[^/]+)/$',
+        'editor_readonly', name="wiki_img_editor_readonly"),
+
+    url(r'^text/(?P<image_id>\d+)/$',
+        'text', name="wiki_img_text"),
+
+    url(r'^history/(?P<object_id>\d+)/$',
+        'history', name="wiki_img_history"),
+
+    url(r'^revert/(?P<object_id>\d+)/$',
+        'revert', name='wiki_img_revert'),
+
+    url(r'^diff/(?P<object_id>\d+)/$', 'diff', name="wiki_img_diff"),
+    url(r'^pubmark/(?P<object_id>\d+)/$', 'pubmark', name="wiki_img_pubmark"),
+
+)
diff --git a/src/wiki_img/views.py b/src/wiki_img/views.py
new file mode 100644 (file)
index 0000000..2b8dd67
--- /dev/null
@@ -0,0 +1,213 @@
+import os
+import functools
+import logging
+logger = logging.getLogger("fnp.wiki_img")
+
+from django.core.urlresolvers import reverse
+from wiki.helpers import (JSONResponse, JSONFormInvalid, JSONServerError,
+                ajax_require_permission)
+
+from django.http import Http404, HttpResponse, HttpResponseForbidden
+from django.shortcuts import get_object_or_404, render
+from django.views.decorators.http import require_GET, require_POST
+from django.conf import settings
+from django.utils.formats import localize
+from django.utils.translation import ugettext as _
+
+from catalogue.models import Image
+from wiki import forms
+from wiki import nice_diff
+from wiki_img.forms import ImageSaveForm
+
+#
+# Quick hack around caching problems, TODO: use ETags
+#
+from django.views.decorators.cache import never_cache
+
+
+@never_cache
+def editor(request, slug, template_name='wiki_img/document_details.html'):
+    doc = get_object_or_404(Image, slug=slug)
+
+    return render(request, template_name, {
+        'document': doc,
+        'forms': {
+            "text_save": ImageSaveForm(user=request.user, prefix="textsave"),
+            "text_revert": forms.DocumentTextRevertForm(prefix="textrevert"),
+            "pubmark": forms.DocumentPubmarkForm(prefix="pubmark"),
+        },
+        'can_pubmark': request.user.has_perm('catalogue.can_pubmark_image'),
+        'REDMINE_URL': settings.REDMINE_URL,
+    })
+
+
+@require_GET
+def editor_readonly(request, slug, template_name='wiki_img/document_details_readonly.html'):
+    doc = get_object_or_404(Image, slug=slug)
+    try:
+        revision = request.GET['revision']
+    except (KeyError):
+        raise Http404
+
+    return render(request, template_name, {
+        'document': doc,
+        'revision': revision,
+        'readonly': True,
+        'REDMINE_URL': settings.REDMINE_URL,
+    })
+
+
+@never_cache
+def text(request, image_id):
+    doc = get_object_or_404(Image, pk=image_id)
+    if request.method == 'POST':
+        form = ImageSaveForm(request.POST, user=request.user, prefix="textsave")
+        if form.is_valid():
+            if request.user.is_authenticated():
+                author = request.user
+            else:
+                author = None
+            text = form.cleaned_data['text']
+            parent_revision = form.cleaned_data['parent_revision']
+            if parent_revision is not None:
+                parent = doc.at_revision(parent_revision)
+            else:
+                parent = None
+            stage = form.cleaned_data['stage_completed']
+            tags = [stage] if stage else []
+            publishable = (form.cleaned_data['publishable'] and
+                    request.user.has_perm('catalogue.can_pubmark_image'))
+            doc.commit(author=author,
+                   text=text,
+                   parent=parent,
+                   description=form.cleaned_data['comment'],
+                   tags=tags,
+                   author_name=form.cleaned_data['author_name'],
+                   author_email=form.cleaned_data['author_email'],
+                   publishable=publishable,
+                )
+            revision = doc.revision()
+            return JSONResponse({
+                'text': doc.materialize() if parent_revision != revision else None,
+                'meta': {},
+                'revision': revision,
+            })
+        else:
+            return JSONFormInvalid(form)
+    else:
+        revision = request.GET.get("revision", None)
+        
+        try:
+            revision = int(revision)
+        except (ValueError, TypeError):
+            revision = doc.revision()
+
+        if revision is not None:
+            text = doc.at_revision(revision).materialize()
+        else:
+            text = ''
+
+        return JSONResponse({
+            'text': text,
+            'meta': {},
+            'revision': revision,
+        })
+
+
+@never_cache
+def history(request, object_id):
+    # TODO: pagination
+    doc = get_object_or_404(Image, pk=object_id)
+    if not doc.accessible(request):
+        return HttpResponseForbidden("Not authorized.")
+
+    changes = []
+    for change in doc.history().reverse():
+        changes.append({
+                "version": change.revision,
+                "description": change.description,
+                "author": change.author_str(),
+                "date": localize(change.created_at),
+                "publishable": _("Publishable") + "\n" if change.publishable else "",
+                "tag": ',\n'.join(unicode(tag) for tag in change.tags.all()),
+            })
+    return JSONResponse(changes)
+
+
+@never_cache
+@require_POST
+def revert(request, object_id):
+    form = forms.DocumentTextRevertForm(request.POST, prefix="textrevert")
+    if form.is_valid():
+        doc = get_object_or_404(Image, pk=object_id)
+        if not doc.accessible(request):
+            return HttpResponseForbidden("Not authorized.")
+
+        revision = form.cleaned_data['revision']
+
+        comment = form.cleaned_data['comment']
+        comment += "\n#revert to %s" % revision
+
+        if request.user.is_authenticated():
+            author = request.user
+        else:
+            author = None
+
+        before = doc.revision()
+        logger.info("Reverting %s to %s", object_id, revision)
+        doc.at_revision(revision).revert(author=author, description=comment)
+
+        return JSONResponse({
+            'text': doc.materialize() if before != doc.revision() else None,
+            'meta': {},
+            'revision': doc.revision(),
+        })
+    else:
+        return JSONFormInvalid(form)
+
+
+@never_cache
+def diff(request, object_id):
+    revA = int(request.GET.get('from', 0))
+    revB = int(request.GET.get('to', 0))
+
+    if revA > revB:
+        revA, revB = revB, revA
+
+    if revB == 0:
+        revB = None
+
+    doc = get_object_or_404(Image, pk=object_id)
+    if not doc.accessible(request):
+        return HttpResponseForbidden("Not authorized.")
+
+    # allow diff from the beginning
+    if revA:
+        docA = doc.at_revision(revA).materialize()
+    else:
+        docA = ""
+    docB = doc.at_revision(revB).materialize()
+
+    return HttpResponse(nice_diff.html_diff_table(docA.splitlines(),
+                                         docB.splitlines(), context=3))
+
+
+@require_POST
+@ajax_require_permission('catalogue.can_pubmark_image')
+def pubmark(request, object_id):
+    form = forms.DocumentPubmarkForm(request.POST, prefix="pubmark")
+    if form.is_valid():
+        doc = get_object_or_404(Image, pk=object_id)
+        if not doc.accessible(request):
+            return HttpResponseForbidden("Not authorized.")
+
+        revision = form.cleaned_data['revision']
+        publishable = form.cleaned_data['publishable']
+        change = doc.at_revision(revision)
+        if publishable != change.publishable:
+            change.set_publishable(publishable)
+            return JSONResponse({"message": _("Revision marked")})
+        else:
+            return JSONResponse({"message": _("Nothing changed")})
+    else:
+        return JSONFormInvalid(form)