- Added librarian as a submodule.
authorŁukasz Rekucki <lrekucki@gmail.com>
Tue, 15 Jun 2010 23:50:00 +0000 (01:50 +0200)
committerŁukasz Rekucki <lrekucki@gmail.com>
Tue, 15 Jun 2010 23:50:00 +0000 (01:50 +0200)
- Split tests in catalouge into a package.
- Don't write to actual MEDIA_ROOT during tests.
- Fixed trailing whitespace.

118 files changed:
.gitmodules [new file with mode: 0644]
apps/api/handlers.py
apps/catalogue/admin.py
apps/catalogue/fields.py
apps/catalogue/forms.py
apps/catalogue/management/commands/importbooks.py
apps/catalogue/migrations/0001_initial.py
apps/catalogue/migrations/0002_auto__add_bookstub__add_field_tag_death.py
apps/catalogue/migrations/0003_fix_book_count_on_shelves.py
apps/catalogue/migrations/0004_book_html_shorts_translations.py
apps/catalogue/migrations/0005_fragment_html_shorts_translations.py
apps/catalogue/migrations/0006_epub_tag_counters_and_ltags_descendants.py
apps/catalogue/migrations/0007_remove_empty_html.py
apps/catalogue/migrations/0008_unique_tag_category_slug.py
apps/catalogue/models.py
apps/catalogue/templatetags/catalogue_tags.py
apps/catalogue/templatetags/switch_tag.py
apps/catalogue/test_utils.py [new file with mode: 0644]
apps/catalogue/tests.py [deleted file]
apps/catalogue/tests/__init__.py [new file with mode: 0644]
apps/catalogue/tests/book_import.py [new file with mode: 0644]
apps/catalogue/tests/search.py [new file with mode: 0644]
apps/catalogue/tests/tags.py [new file with mode: 0644]
apps/catalogue/urls.py
apps/catalogue/utils.py
apps/catalogue/views.py
apps/chunks/models.py
apps/chunks/templatetags/chunks.py
apps/compress/filter_base.py
apps/compress/filters/csstidy/__init__.py
apps/compress/filters/csstidy_python/__init__.py
apps/compress/management/commands/synccompress.py
apps/compress/utils.py
apps/infopages/migrations/0001_initial.py
apps/infopages/models.py
apps/lessons/models.py
apps/lessons/urls.py
apps/lessons/views.py
apps/newtagging/admin.py
apps/newtagging/managers.py
apps/newtagging/models.py
apps/piston/authentication.py
apps/piston/decorator.py
apps/piston/doc.py
apps/piston/emitters.py
apps/piston/forms.py
apps/piston/handler.py
apps/piston/managers.py
apps/piston/models.py
apps/piston/oauth.py
apps/piston/resource.py
apps/piston/signals.py
apps/piston/store.py
apps/piston/templates/documentation.html
apps/piston/templates/piston/authorize_token.html
apps/piston/test.py
apps/piston/tests.py
apps/piston/utils.py
apps/sponsors/admin.py
apps/sponsors/fields.py
apps/sponsors/migrations/0001_initial.py
apps/sponsors/models.py
apps/sponsors/static/sponsors/js/footer_admin.js
apps/sponsors/templatetags/sponsor_tags.py
apps/sponsors/widgets.py
apps/suggest/migrations/0001_initial.py
apps/suggest/models.py
apps/suggest/urls.py
apps/suggest/views.py
fabfile.py
lib/librarian [new submodule]
lib/markupstring.py
lib/slughifi.py
requirements.txt
scripts/conv_genre_families.py
scripts/irename.py
scripts/setmainpage.py
wolnelektury/manage.py
wolnelektury/middleware.py
wolnelektury/settings.py
wolnelektury/static/js/book.js
wolnelektury/static/js/catalogue.js
wolnelektury/static/js/jquery.autocomplete.js
wolnelektury/static/js/jquery.countdown-de.js
wolnelektury/static/js/jquery.countdown-es.js
wolnelektury/static/js/jquery.countdown-fr.js
wolnelektury/static/js/jquery.countdown-pl.js
wolnelektury/static/js/jquery.countdown-ru.js
wolnelektury/static/js/jquery.countdown-uk.js
wolnelektury/static/js/jquery.countdown.js
wolnelektury/static/js/jquery.eventdelegation.js
wolnelektury/static/js/jquery.form.js
wolnelektury/static/js/jquery.highlightfade.js
wolnelektury/static/js/jquery.jqmodal.js
wolnelektury/static/js/jquery.labelify.js
wolnelektury/static/js/jquery.scrollto.js
wolnelektury/static/js/ordered_select_multiple.js
wolnelektury/static/sponsors/js/footer_admin.js
wolnelektury/templates/1percent.html
wolnelektury/templates/404.html
wolnelektury/templates/500.html
wolnelektury/templates/503.html
wolnelektury/templates/auth/login.html
wolnelektury/templates/base.html
wolnelektury/templates/catalogue/book_detail.html
wolnelektury/templates/catalogue/book_list.html
wolnelektury/templates/catalogue/book_stub_detail.html
wolnelektury/templates/catalogue/differentiate_tags.html
wolnelektury/templates/catalogue/folded_tag_list.html
wolnelektury/templates/catalogue/main_page.html
wolnelektury/templates/catalogue/pd_counter.html
wolnelektury/templates/catalogue/search_multiple_hits.html
wolnelektury/templates/catalogue/search_no_hits.html
wolnelektury/templates/catalogue/search_too_short.html
wolnelektury/templates/catalogue/tagged_object_list.html
wolnelektury/templates/lessons/document_detail.html
wolnelektury/templates/lessons/document_list.html
wolnelektury/urls.py

diff --git a/.gitmodules b/.gitmodules
new file mode 100644 (file)
index 0000000..56b9c42
--- /dev/null
@@ -0,0 +1,3 @@
+[submodule "lib/librarian"]
+       path = lib/librarian
+       url = git://github.com/fnp/librarian.git
index 799b608..40121d6 100644 (file)
@@ -16,14 +16,14 @@ staff_required = user_passes_test(lambda user: user.is_staff)
 class BookHandler(BaseHandler):
     model = Book
     fields = ('slug', 'title')
-    
+
     @staff_required
     def read(self, request, slug=None):
         if slug:
             return get_object_or_404(Book, slug=slug)
         else:
             return Book.objects.all()
-    
+
     @staff_required
     def create(self, request):
         form = BookImportForm(request.POST, request.FILES)
index b2744ee..0275094 100644 (file)
@@ -20,7 +20,7 @@ class TagAdmin(admin.ModelAdmin):
 
 class BookAdmin(TaggableModelAdmin):
     tag_model = Tag
-    
+
     list_display = ('title', 'slug', 'has_pdf_file', 'has_epub_file', 'has_odt_file', 'has_html_file', 'has_description',)
     search_fields = ('title',)
     ordering = ('title',)
@@ -30,14 +30,14 @@ class BookAdmin(TaggableModelAdmin):
 
 class FragmentAdmin(TaggableModelAdmin):
     tag_model = Tag
-    
+
     list_display = ('book', 'anchor',)
     ordering = ('book', 'anchor',)
 
 
 class BookStubAdmin(admin.ModelAdmin):
     # tag_model = Tag
-    
+
     list_display = ('title', 'author', 'slug','pd')
     search_fields = ('title','author')
     ordering = ('title',)
index ded6f0e..62ca29c 100644 (file)
@@ -123,7 +123,7 @@ try:
     (
         [JSONField], # Class(es) these apply to
         [], # Positional arguments (not used)
-        {}, # Keyword argument           
+        {}, # Keyword argument
     ), ], ["^catalogue\.fields\.JSONField"])
 except ImportError:
     pass
index 60a99cd..d217824 100644 (file)
@@ -21,7 +21,7 @@ class BookImportForm(forms.Form):
 class SearchForm(forms.Form):
     q = JQueryAutoCompleteField('/katalog/tags/', {'minChars': 2, 'selectFirst': True, 'cacheLength': 50, 'matchContains': "word"})
     tags = forms.CharField(widget=forms.HiddenInput, required=False)
-    
+
     def __init__(self, *args, **kwargs):
         tags = kwargs.pop('tags', [])
         super(SearchForm, self).__init__(*args, **kwargs)
@@ -39,7 +39,7 @@ class UserSetsForm(forms.Form):
 
 
 class ObjectSetsForm(forms.Form):
-    def __init__(self, obj, user, *args, **kwargs):        
+    def __init__(self, obj, user, *args, **kwargs):
         super(ObjectSetsForm, self).__init__(*args, **kwargs)
         self.fields['set_ids'] = forms.MultipleChoiceField(
             label=_('Shelves'),
@@ -48,20 +48,20 @@ class ObjectSetsForm(forms.Form):
             initial=[tag.id for tag in obj.tags.filter(category='set', user=user)],
             widget=forms.CheckboxSelectMultiple
         )
-        
+
 
 class NewSetForm(forms.Form):
     name = forms.CharField(max_length=50, required=True)
-    
+
     def __init__(self, *args, **kwargs):
         super(NewSetForm, self).__init__(*args, **kwargs)
         self.fields['name'].widget.attrs['title'] = _('Name of the new shelf')
-        
+
     def save(self, user, commit=True):
         name = self.cleaned_data['name']
         new_set = Tag(name=name, slug=utils.get_random_hash(name), sort_key=slughifi(name),
             category='set', user=user)
-        
+
         new_set.save()
         return new_set
 
@@ -78,7 +78,7 @@ FORMATS = (
 
 class DownloadFormatsForm(forms.Form):
     formats = forms.MultipleChoiceField(required=False, choices=FORMATS, widget=forms.CheckboxSelectMultiple)
-    
+
     def __init__(self, *args, **kwargs):
          super(DownloadFormatsForm, self).__init__(*args, **kwargs)
 
index c5fbb2e..cead75f 100644 (file)
@@ -39,7 +39,7 @@ class Command(BaseCommand):
 
         files_imported = 0
         files_skipped = 0
-        
+
         for dir_name in directories:
             if not os.path.isdir(dir_name):
                 print self.style.ERROR("%s: Not a directory. Skipping." % dir_name)
@@ -47,30 +47,30 @@ class Command(BaseCommand):
                 for file_name in os.listdir(dir_name):
                     file_path = os.path.join(dir_name, file_name)
                     file_base, ext = os.path.splitext(file_path)
-                    
+
                     # Skip files that are not XML files
                     if not ext == '.xml':
                         continue
-                    
+
                     if verbose > 0:
                         print "Parsing '%s'" % file_path
                     else:
                         sys.stdout.write('.')
                         sys.stdout.flush()
-                    
+
                     # Import book files
                     try:
                         book = Book.from_xml_file(file_path, overwrite=force)
                         files_imported += 1
-                        
+
                         if os.path.isfile(file_base + '.pdf'):
                             book.pdf_file.save('%s.pdf' % book.slug, File(file(file_base + '.pdf')))
                             if verbose:
-                                print "Importing %s.pdf" % file_base 
+                                print "Importing %s.pdf" % file_base
                         if os.path.isfile(file_base + '.epub'):
                             book.epub_file.save('%s.epub' % book.slug, File(file(file_base + '.epub')))
                             if verbose:
-                                print "Importing %s.epub" % file_base 
+                                print "Importing %s.epub" % file_base
                         if os.path.isfile(file_base + '.odt'):
                             book.odt_file.save('%s.odt' % book.slug, File(file(file_base + '.odt')))
                             if verbose:
@@ -87,20 +87,20 @@ class Command(BaseCommand):
                             book.ogg_file.save('%s.ogg' % book.slug, File(file(os.path.join(dir_name, book.slug + '.ogg'))))
                             if verbose:
                                 print "Importing %s.ogg" % book.slug
-                            
+
                         book.save()
-                    
+
                     except Book.AlreadyExists, msg:
                         print self.style.ERROR('%s: Book already imported. Skipping. To overwrite use --force.' %
                             file_path)
                         files_skipped += 1
-                        
+
         # Print results
         print
         print "Results: %d files imported, %d skipped, %d total." % (
             files_imported, files_skipped, files_imported + files_skipped)
         print
-                        
+
         transaction.commit()
         transaction.leave_transaction_management()
 
index 05ba18d..2aabb82 100644 (file)
@@ -5,9 +5,9 @@ from south.v2 import SchemaMigration
 from django.db import models
 
 class Migration(SchemaMigration):
-    
+
     def forwards(self, orm):
-        
+
         # Adding model 'Tag'
         db.create_table('catalogue_tag', (
             ('category', self.gf('django.db.models.fields.CharField')(max_length=50, db_index=True)),
@@ -69,10 +69,10 @@ class Migration(SchemaMigration):
             ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
         ))
         db.send_create_signal('catalogue', ['Fragment'])
-    
-    
+
+
     def backwards(self, orm):
-        
+
         # Deleting model 'Tag'
         db.delete_table('catalogue_tag')
 
@@ -87,8 +87,8 @@ class Migration(SchemaMigration):
 
         # Deleting model 'Fragment'
         db.delete_table('catalogue_fragment')
-    
-    
+
+
     models = {
         'auth.group': {
             'Meta': {'object_name': 'Group'},
@@ -178,5 +178,5 @@ class Migration(SchemaMigration):
             'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
         }
     }
-    
+
     complete_apps = ['catalogue']
index 508f957..c278a83 100644 (file)
@@ -5,9 +5,9 @@ from south.v2 import SchemaMigration
 from django.db import models
 
 class Migration(SchemaMigration):
-    
+
     def forwards(self, orm):
-        
+
         # Adding model 'BookStub'
         db.create_table('catalogue_bookstub', (
             ('author', self.gf('django.db.models.fields.CharField')(max_length=120)),
@@ -22,17 +22,17 @@ class Migration(SchemaMigration):
 
         # Adding field 'Tag.death'
         db.add_column('catalogue_tag', 'death', self.gf('django.db.models.fields.IntegerField')(null=True, blank=True), keep_default=False)
-    
-    
+
+
     def backwards(self, orm):
-        
+
         # Deleting model 'BookStub'
         db.delete_table('catalogue_bookstub')
 
         # Deleting field 'Tag.death'
         db.delete_column('catalogue_tag', 'death')
-    
-    
+
+
     models = {
         'auth.group': {
             'Meta': {'object_name': 'Group'},
@@ -133,5 +133,5 @@ class Migration(SchemaMigration):
             'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
         }
     }
-    
+
     complete_apps = ['catalogue']
index a161e27..8180311 100644 (file)
@@ -23,11 +23,11 @@ class Migration(DataMigration):
                 'tagged_item': qn(orm.TagRelation._meta.db_table),
                 'tag_id': tag.pk,
             }
-    
+
             cursor = connection.cursor()
             cursor.execute(query)
             book_count = (cursor.fetchone() or (0,))[0]
-            
+
             tag.book_count = book_count
             tag.save()
 
index 8d8e06a..d9e7b53 100644 (file)
@@ -5,9 +5,9 @@ from south.v2 import SchemaMigration
 from django.db import models
 
 class Migration(SchemaMigration):
-    
+
     def forwards(self, orm):
-        
+
         # Adding field 'Book._short_html_en'
         db.add_column('catalogue_book', '_short_html_en', self.gf('django.db.models.fields.TextField')(null=True, blank=True), keep_default=False)
 
@@ -31,10 +31,10 @@ class Migration(SchemaMigration):
 
         # Adding field 'Book._short_html_lt'
         db.add_column('catalogue_book', '_short_html_lt', self.gf('django.db.models.fields.TextField')(null=True, blank=True), keep_default=False)
-    
-    
+
+
     def backwards(self, orm):
-        
+
         # Deleting field 'Book._short_html_en'
         db.delete_column('catalogue_book', '_short_html_en')
 
@@ -58,8 +58,8 @@ class Migration(SchemaMigration):
 
         # Deleting field 'Book._short_html_lt'
         db.delete_column('catalogue_book', '_short_html_lt')
-    
-    
+
+
     models = {
         'auth.group': {
             'Meta': {'object_name': 'Group'},
@@ -168,5 +168,5 @@ class Migration(SchemaMigration):
             'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
         }
     }
-    
+
     complete_apps = ['catalogue']
index 4828d6f..8322b4c 100644 (file)
@@ -5,9 +5,9 @@ from south.v2 import SchemaMigration
 from django.db import models
 
 class Migration(SchemaMigration):
-    
+
     def forwards(self, orm):
-        
+
         # Adding field 'Fragment._short_html_de'
         db.add_column('catalogue_fragment', '_short_html_de', self.gf('django.db.models.fields.TextField')(null=True, blank=True), keep_default=False)
 
@@ -31,10 +31,10 @@ class Migration(SchemaMigration):
 
         # Adding field 'Fragment._short_html_uk'
         db.add_column('catalogue_fragment', '_short_html_uk', self.gf('django.db.models.fields.TextField')(null=True, blank=True), keep_default=False)
-    
-    
+
+
     def backwards(self, orm):
-        
+
         # Deleting field 'Fragment._short_html_de'
         db.delete_column('catalogue_fragment', '_short_html_de')
 
@@ -58,8 +58,8 @@ class Migration(SchemaMigration):
 
         # Deleting field 'Fragment._short_html_uk'
         db.delete_column('catalogue_fragment', '_short_html_uk')
-    
-    
+
+
     models = {
         'auth.group': {
             'Meta': {'object_name': 'Group'},
@@ -176,5 +176,5 @@ class Migration(SchemaMigration):
             'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
         }
     }
-    
+
     complete_apps = ['catalogue']
index 7b2f2c5..b990fc3 100644 (file)
@@ -14,7 +14,7 @@ def get_ltag(book, orm):
 
 
 class Migration(SchemaMigration):
-    
+
     def forwards(self, orm):
         """ Add _tag_counter and make sure all books carry their ancestors' l-tags """
 
@@ -32,7 +32,7 @@ class Migration(SchemaMigration):
             ltag = get_ltag(book, orm)
             for child in book.children.all():
                 ltag_descendants(child, ltags + [ltag])
-        
+
         if not db.dry_run:
             try:
                 book_ct = orm['contenttypes.contenttype'].objects.get(app_label='catalogue', model='book')
@@ -42,8 +42,8 @@ class Migration(SchemaMigration):
             orm.TagRelation.objects.filter(content_type=book_ct, tag__category='book').delete()
             for book in orm.Book.objects.filter(parent=None):
                 ltag_descendants(book)
-    
-    
+
+
     def backwards(self, orm):
         """ Delete _tag_counter and make sure books carry own l-tag. """
 
@@ -61,8 +61,8 @@ class Migration(SchemaMigration):
             orm.TagRelation.objects.filter(content_type=book_ct, tag__category='book').delete()
             for book in orm.Book.objects.filter(parent=None):
                 orm.TagRelation(object_id=book.pk, tag=get_ltag(book, orm), content_type=book_ct).save()
-    
-    
+
+
     models = {
         'auth.group': {
             'Meta': {'object_name': 'Group'},
@@ -182,5 +182,5 @@ class Migration(SchemaMigration):
             'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
         }
     }
-    
+
     complete_apps = ['catalogue']
index ca87ccb..5b6f453 100644 (file)
@@ -5,12 +5,12 @@ from south.v2 import DataMigration
 from django.db import models
 
 class Migration(DataMigration):
-    
+
     def forwards(self, orm):
         """ Look for HTML files without any real content and delete them """
         from lxml import etree
         from librarian.html import html_has_content
-        
+
         for book in orm.Book.objects.exclude(html_file=''):
             if not html_has_content(etree.parse(book.html_file)):
                 book.html_file.delete()
@@ -18,11 +18,11 @@ class Migration(DataMigration):
                 for key in filter(lambda x: x.startswith('_short_html'), book.__dict__):
                     book.__setattr__(key, '')
                 book.save()
-    
+
     def backwards(self, orm):
         """ Do nothing. We don't want empty HTML files anyway. """
         pass
-    
+
     models = {
         'auth.group': {
             'Meta': {'object_name': 'Group'},
@@ -142,5 +142,5 @@ class Migration(DataMigration):
             'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
         }
     }
-    
+
     complete_apps = ['catalogue']
index 876d0fd..d130819 100644 (file)
@@ -5,25 +5,25 @@ from south.v2 import SchemaMigration
 from django.db import models
 
 class Migration(SchemaMigration):
-    
+
     def forwards(self, orm):
-        
+
         # Removing unique constraint on 'Tag', fields ['slug']
         db.delete_unique('catalogue_tag', ['slug'])
 
         # Adding unique constraint on 'Tag', fields ['category', 'slug']
         db.create_unique('catalogue_tag', ['category', 'slug'])
-    
-    
+
+
     def backwards(self, orm):
-        
+
         # Adding unique constraint on 'Tag', fields ['slug']
         db.create_unique('catalogue_tag', ['slug'])
 
         # Removing unique constraint on 'Tag', fields ['category', 'slug']
         db.delete_unique('catalogue_tag', ['category', 'slug'])
-    
-    
+
+
     models = {
         'auth.group': {
             'Meta': {'object_name': 'Group'},
@@ -143,5 +143,5 @@ class Migration(SchemaMigration):
             'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
         }
     }
-    
+
     complete_apps = ['catalogue']
index 2008f09..367f382 100644 (file)
@@ -55,7 +55,7 @@ class Tag(TagBase):
     death = models.IntegerField(_(u'year of death'), blank=True, null=True)
     gazeta_link = models.CharField(blank=True, max_length=240)
     wiki_link = models.CharField(blank=True, max_length=240)
-    
+
     categories_rev = {
         'autor': 'author',
         'epoka': 'epoch',
@@ -117,7 +117,7 @@ class Tag(TagBase):
                             real_tags.append(Tag.objects.exclude(category='book').get(slug=name))
                         except Tag.MultipleObjectsReturned, e:
                             ambiguous_slugs.append(name)
-                            
+
             if category:
                 # something strange left off
                 raise Tag.DoesNotExist()
@@ -131,13 +131,13 @@ class Tag(TagBase):
                 return real_tags
         else:
             return TagBase.get_tag_list(tags)
-    
+
     @property
     def url_chunk(self):
         return '/'.join((Tag.categories_dict[self.category], self.slug))
 
 
-# TODO: why is this hard-coded ? 
+# TODO: why is this hard-coded ?
 def book_upload_path(ext):
     def get_dynamic_path(book, filename):
         return 'lektura/%s.%s' % (book.slug, ext)
@@ -171,7 +171,7 @@ class Book(models.Model):
     objects = models.Manager()
     tagged = managers.ModelTaggedItemManager(Tag)
     tags = managers.TagDescriptor(Tag)
-    
+
     _tag_counter = JSONField(null=True, editable=False)
     _theme_counter = JSONField(null=True, editable=False)
 
@@ -210,7 +210,7 @@ class Book(models.Model):
     @property
     def name(self):
         return self.title
-    
+
     def book_tag(self):
         slug = ('l-' + self.slug)[:120]
         book_tag, created = Tag.objects.get_or_create(slug=slug, category='book')
@@ -399,8 +399,8 @@ class Book(models.Model):
 
         book.save()
         return book
-    
-    
+
+
     def refresh_tag_counter(self):
         tags = {}
         for child in self.children.all().order_by():
@@ -411,7 +411,7 @@ class Book(models.Model):
         self.set__tag_counter_value(tags)
         self.save(reset_short_html=False, refresh_mp3=False)
         return tags
-    
+
     @property
     def tag_counter(self):
         if self._tag_counter is None:
@@ -426,13 +426,13 @@ class Book(models.Model):
         self.set__theme_counter_value(tags)
         self.save(reset_short_html=False, refresh_mp3=False)
         return tags
-    
+
     @property
     def theme_counter(self):
         if self._theme_counter is None:
             return self.refresh_theme_counter()
         return dict((int(k), v) for k, v in self.get__theme_counter_value().iteritems())
-    
+
 
 
 class Fragment(models.Model):
index 504ee69..7f2bf29 100644 (file)
@@ -54,7 +54,7 @@ def simple_title(tags):
         'kind': u'rodzaj',
         'set': u'półka',
     }
-    
+
     title = []
     for tag in tags:
         title.append("%s: %s" % (mapping[tag.category], tag.name))
@@ -68,46 +68,46 @@ def title_from_tags(tags):
         for tag in tags:
             result[tag.category] = tag
         return result
-    
+
     # TODO: Remove this after adding flection mechanism
     return simple_title(tags)
-    
+
     class Flection(object):
         def get_case(self, name, flection):
             return name
     flection = Flection()
-    
+
     self = split_tags(tags)
-    
+
     title = u''
-    
+
     # Specjalny przypadek oglądania wszystkich lektur na danej półce
     if len(self) == 1 and 'set' in self:
         return u'Półka %s' % self['set']
-    
+
     # Specjalny przypadek "Twórczość w pozytywizmie", wtedy gdy tylko epoka
     # jest wybrana przez użytkownika
     if 'epoch' in self and len(self) == 1:
         text = u'Twórczość w %s' % flection.get_case(unicode(self['epoch']), u'miejscownik')
         return capfirst(text)
-    
+
     # Specjalny przypadek "Dramat w twórczości Sofoklesa", wtedy gdy podane
     # są tylko rodzaj literacki i autor
     if 'kind' in self and 'author' in self and len(self) == 2:
-        text = u'%s w twórczości %s' % (unicode(self['kind']), 
+        text = u'%s w twórczości %s' % (unicode(self['kind']),
             flection.get_case(unicode(self['author']), u'dopełniacz'))
         return capfirst(text)
-    
+
     # Przypadki ogólniejsze
     if 'theme' in self:
         title += u'Motyw %s' % unicode(self['theme'])
-    
+
     if 'genre' in self:
         if 'theme' in self:
             title += u' w %s' % flection.get_case(unicode(self['genre']), u'miejscownik')
         else:
             title += unicode(self['genre'])
-            
+
     if 'kind' in self or 'author' in self or 'epoch' in self:
         if 'genre' in self or 'theme' in self:
             if 'kind' in self:
@@ -116,12 +116,12 @@ def title_from_tags(tags):
                 title += u' w twórczości '
         else:
             title += u'%s ' % unicode(self.get('kind', u'twórczość'))
-            
+
     if 'author' in self:
         title += flection.get_case(unicode(self['author']), u'dopełniacz')
     elif 'epoch' in self:
         title += flection.get_case(unicode(self['epoch']), u'dopełniacz')
-    
+
     return capfirst(title)
 
 
@@ -152,7 +152,7 @@ def breadcrumbs(tags, search_form=True):
 def catalogue_url(parser, token):
     bits = token.split_contents()
     tag_name = bits[0]
-    
+
     tags_to_add = []
     tags_to_remove = []
     for bit in bits[1:]:
@@ -160,7 +160,7 @@ def catalogue_url(parser, token):
             tags_to_remove.append(bit[1:])
         else:
             tags_to_add.append(bit)
-    
+
     return CatalogueURLNode(tags_to_add, tags_to_remove)
 
 
@@ -168,7 +168,7 @@ class CatalogueURLNode(Node):
     def __init__(self, tags_to_add, tags_to_remove):
         self.tags_to_add = [Variable(tag) for tag in tags_to_add]
         self.tags_to_remove = [Variable(tag) for tag in tags_to_remove]
-    
+
     def render(self, context):
         tags_to_add = []
         tags_to_remove = []
@@ -186,14 +186,14 @@ class CatalogueURLNode(Node):
                 tags_to_remove += [t for t in tag]
             else:
                 tags_to_remove.append(tag)
-            
+
         tag_slugs = [tag.url_chunk for tag in tags_to_add]
         for tag in tags_to_remove:
             try:
                 tag_slugs.remove(tag.url_chunk)
             except KeyError:
                 pass
-        
+
         if len(tag_slugs) > 0:
             return reverse('tagged_object_list', kwargs={'tags': '/'.join(tag_slugs)})
         else:
@@ -201,7 +201,7 @@ class CatalogueURLNode(Node):
 
 
 @register.inclusion_tag('catalogue/latest_blog_posts.html')
-def latest_blog_posts(feed_url, posts_to_show=5):    
+def latest_blog_posts(feed_url, posts_to_show=5):
     try:
         feed = feedparser.parse(str(feed_url))
         posts = []
@@ -234,7 +234,7 @@ def folded_tag_list(tags, choices=None):
         choices = []
     some_tags_hidden = False
     tag_count = len(tags)
-    
+
     if tag_count == 1:
         one_tag = tags[0]
     else:
index b91f872..72476be 100644 (file)
@@ -1,11 +1,11 @@
 # Source: http://djangosnippets.org/snippets/967/
 # Author: adurdin
 # Posted: August 13, 2008
-# 
-# 
+#
+#
 # We can use it based on djangosnippets Terms of Service:
 # (http://djangosnippets.org/about/tos/)
-# 
+#
 # 2. That you grant any third party who sees the code you post
 # a royalty-free, non-exclusive license to copy and distribute that code
 # and to make and distribute derivative works based on that code. You may
@@ -71,13 +71,13 @@ def do_switch(parser, token):
     got_else = False
     while token.contents != 'endswitch':
         nodelist = parser.parse(BlockTagList('case', 'else', 'endswitch'))
-        
+
         if got_else:
             raise template.TemplateSyntaxError("'else' must be last tag in '%s'." % tag_name)
 
         contents = token.contents.split()
         token_name, token_args = contents[0], contents[1:]
-        
+
         if token_name == 'case':
             tests = map(parser.compile_filter, token_args)
             case = (tests, nodelist)
@@ -122,7 +122,7 @@ class SwitchNode(Node):
         except VariableDoesNotExist:
             no_value = True
             value_missing = None
-        
+
         for tests, nodelist in self.cases:
             if tests is None:
                 return nodelist.render(context)
diff --git a/apps/catalogue/test_utils.py b/apps/catalogue/test_utils.py
new file mode 100644 (file)
index 0000000..3a8af57
--- /dev/null
@@ -0,0 +1,38 @@
+from django.conf import settings
+from django.test import TestCase
+import shutil
+import tempfile
+
+class WLTestCase(TestCase):
+    """
+        Generic base class for tests. Adds settings freeze and clears MEDIA_ROOT.
+    """
+    def setUp(self):
+        self._MEDIA_ROOT, settings.MEDIA_ROOT = settings.MEDIA_ROOT, tempfile.mkdtemp(prefix='djangotest_')
+
+    def tearDown(self):
+        shutil.rmtree(settings.MEDIA_ROOT, True)
+        settings.MEDIA_ROOT = self._MEDIA_ROOT
+
+class PersonStub(object):
+
+    def __init__(self, first_names, last_name):
+        self.first_names = first_names
+        self.last_name = last_name
+
+
+class BookInfoStub(object):
+
+    def __init__(self, **kwargs):
+        self.__dict = kwargs
+
+    def __setattr__(self, key, value):
+        if not key.startswith('_'):
+            self.__dict[key] = value
+        return object.__setattr__(self, key, value)
+
+    def __getattr__(self, key):
+        return self.__dict[key]
+
+    def to_dict(self):
+        return dict((key, unicode(value)) for key, value in self.__dict.items())
diff --git a/apps/catalogue/tests.py b/apps/catalogue/tests.py
deleted file mode 100644 (file)
index 829f8dc..0000000
+++ /dev/null
@@ -1,476 +0,0 @@
-# -*- coding: utf-8 -*-
-from django.test import TestCase
-from catalogue import models, views
-from django.core.files.base import ContentFile
-from django.contrib.auth.models import User, AnonymousUser
-from django.test.client import Client
-
-from nose.tools import raises
-from StringIO import StringIO
-
-class BasicSearchLogicTests(TestCase):
-
-    def setUp(self):
-        self.author_tag = models.Tag.objects.create(
-                                name=u'Adam Mickiewicz [SubWord]',
-                                category=u'author', slug="one")
-
-        self.unicode_tag = models.Tag.objects.create(
-                                name=u'Tadeusz Żeleński (Boy)',
-                                category=u'author', slug="two")
-
-        self.polish_tag = models.Tag.objects.create(
-                                name=u'ĘÓĄŚŁŻŹĆŃęóąśłżźćń',
-                                category=u'author', slug="three")
-
-    @raises(ValueError)
-    def test_empty_query(self):
-        """ Check that empty queries raise an error. """
-        views.find_best_matches(u'')
-
-    @raises(ValueError)
-    def test_one_letter_query(self):
-        """ Check that one letter queries aren't permitted. """
-        views.find_best_matches(u't')
-
-    def test_match_by_prefix(self):
-        """ Tags should be matched by prefix of words within it's name. """
-        self.assertEqual(views.find_best_matches(u'Ada'), (self.author_tag,))
-        self.assertEqual(views.find_best_matches(u'Mic'), (self.author_tag,))
-        self.assertEqual(views.find_best_matches(u'Mickiewicz'), (self.author_tag,))
-
-    def test_match_case_insensitive(self):
-        """ Tag names should match case insensitive. """
-        self.assertEqual(views.find_best_matches(u'adam mickiewicz'), (self.author_tag,))
-
-    def test_match_case_insensitive_unicode(self):
-        """ Tag names should match case insensitive (unicode). """
-        self.assertEqual(views.find_best_matches(u'tadeusz żeleński (boy)'), (self.unicode_tag,))
-
-    def test_word_boundary(self):
-        self.assertEqual(views.find_best_matches(u'SubWord'), (self.author_tag,))
-        self.assertEqual(views.find_best_matches(u'[SubWord'), (self.author_tag,))
-
-    def test_unrelated_search(self):
-        self.assertEqual(views.find_best_matches(u'alamakota'), tuple())
-        self.assertEqual(views.find_best_matches(u'Adama'), ())
-
-    def test_infix_doesnt_match(self):
-        """ Searching for middle of a word shouldn't match. """
-        self.assertEqual(views.find_best_matches(u'deusz'), tuple())
-
-    def test_diactricts_removal_pl(self):
-        """ Tags should match both with and without national characters. """
-        self.assertEqual(views.find_best_matches(u'ĘÓĄŚŁŻŹĆŃęóąśłżźćń'), (self.polish_tag,))
-        self.assertEqual(views.find_best_matches(u'EOASLZZCNeoaslzzcn'), (self.polish_tag,))
-        self.assertEqual(views.find_best_matches(u'eoaslzzcneoaslzzcn'), (self.polish_tag,))
-
-    def test_diactricts_query_removal_pl(self):
-        """ Tags without national characters shouldn't be matched by queries with them. """
-        self.assertEqual(views.find_best_matches(u'Adąm'), ())
-
-    def test_sloppy(self):
-        self.assertEqual(views.find_best_matches(u'Żelenski'), (self.unicode_tag,))
-        self.assertEqual(views.find_best_matches(u'zelenski'), (self.unicode_tag,))
-
-
-class PersonStub(object):
-
-    def __init__(self, first_names, last_name):
-        self.first_names = first_names
-        self.last_name = last_name
-
-from slughifi import slughifi
-
-class BookInfoStub(object):
-
-    def __init__(self, **kwargs):            
-        self.__dict = kwargs
-
-    def __setattr__(self, key, value):
-        if not key.startswith('_'):
-            self.__dict[key] = value
-        return object.__setattr__(self, key, value)
-
-    def __getattr__(self, key):
-        return self.__dict[key]
-
-    def to_dict(self):
-        return dict((key, unicode(value)) for key, value in self.__dict.items())
-
-def info_args(title):
-    """ generate some keywords for comfortable BookInfoCreation  """
-    slug = unicode(slughifi(title))
-    return {'title': unicode(title),
-            'slug': slug,
-            'url': u"http://wolnelektury.pl/example/%s" % slug,
-            'about': u"http://wolnelektury.pl/example/URI/%s" % slug,
-            }
-
-class BookImportLogicTests(TestCase):
-
-    def setUp(self):
-        self.book_info = BookInfoStub(
-            url=u"http://wolnelektury.pl/example/default_book",
-            about=u"http://wolnelektury.pl/example/URI/default_book",
-            title=u"Default Book",
-            author=PersonStub(("Jim",), "Lazy"),
-            kind="X-Kind",
-            genre="X-Genre",
-            epoch="X-Epoch",
-        )
-
-        self.expected_tags = [
-           ('author', 'jim-lazy'),
-           ('genre', 'x-genre'),
-           ('epoch', 'x-epoch'),
-           ('kind', 'x-kind'),
-        ]
-        self.expected_tags.sort()
-
-    def tearDown(self):
-        models.Book.objects.all().delete()
-
-    def test_empty_book(self):
-        BOOK_TEXT = "<utwor />"
-        book = models.Book.from_text_and_meta(ContentFile(BOOK_TEXT), self.book_info)
-
-        self.assertEqual(book.title, "Default Book")
-        self.assertEqual(book.slug, "default_book")
-        self.assert_(book.parent is None)
-        self.assertFalse(book.has_html_file())
-
-        # no fragments generated
-        self.assertEqual(book.fragments.count(), 0)
-
-        # TODO: this should be filled out probably...
-        self.assertEqual(book.wiki_link, '')
-        self.assertEqual(book.gazeta_link, '')
-        self.assertEqual(book._short_html, '')
-        self.assertEqual(book.description, '')
-
-        tags = [ (tag.category, tag.slug) for tag in book.tags ]
-        tags.sort()
-
-        self.assertEqual(tags, self.expected_tags)
-    
-    def test_not_quite_empty_book(self):
-        """ Not empty, but without any real text.
-        
-        Should work like any other non-empty book.
-        """
-        
-        BOOK_TEXT = """<utwor>
-        <liryka_l>
-            <nazwa_utworu>Nic</nazwa_utworu>
-        </liryka_l></utwor>
-        """
-        
-        book = models.Book.from_text_and_meta(ContentFile(BOOK_TEXT), self.book_info)
-        self.assertTrue(book.has_html_file())
-
-    def test_book_with_fragment(self):
-        BOOK_TEXT = """<utwor>
-        <opowiadanie>
-            <akap><begin id="m01" /><motyw id="m01">Love</motyw>Ala ma kota<end id="m01" /></akap>
-        </opowiadanie></utwor>
-        """
-
-        book = models.Book.from_text_and_meta(ContentFile(BOOK_TEXT), self.book_info)
-        self.assertTrue(book.has_html_file())
-
-        self.assertEqual(book.fragments.count(), 1)
-        self.assertEqual(book.fragments.all()[0].text, u'<p class="paragraph">Ala ma kota</p>\n')
-
-        self.assert_(('theme', 'love') in [ (tag.category, tag.slug) for tag in book.tags ])
-
-    def test_book_replace_title(self):
-        BOOK_TEXT = """<utwor />"""
-        self.book_info.title = u"Extraordinary"
-        book = models.Book.from_text_and_meta(ContentFile(BOOK_TEXT), self.book_info)
-
-        tags = [ (tag.category, tag.slug) for tag in book.tags ]
-        tags.sort()
-
-        self.assertEqual(tags, self.expected_tags)
-
-    def test_book_replace_author(self):
-        BOOK_TEXT = """<utwor />"""
-        self.book_info.author = PersonStub(("Hans", "Christian"), "Andersen")
-        book = models.Book.from_text_and_meta(ContentFile(BOOK_TEXT), self.book_info)
-
-        tags = [ (tag.category, tag.slug) for tag in book.tags ]
-        tags.sort()
-
-        self.expected_tags.remove(('author', 'jim-lazy'))
-        self.expected_tags.append(('author', 'hans-christian-andersen'))
-        self.expected_tags.sort()
-
-        self.assertEqual(tags, self.expected_tags)
-
-        # the old tag should disappear 
-        self.assertRaises(models.Tag.DoesNotExist, models.Tag.objects.get,
-                    slug="jim-lazy", category="author")
-
-
-    
-class BooksByTagTests(TestCase):
-    """ tests the /katalog/tag page for found books """
-    
-    def setUp(self):
-        author = PersonStub(("Common",), "Man")
-        tags = dict(genre='G', epoch='E', author=author, kind="K")
-
-        # grandchild
-        kwargs = info_args(u"GChild")
-        kwargs.update(tags)
-        gchild_info = BookInfoStub(**kwargs)
-        # child
-        kwargs = info_args(u"Child")
-        kwargs.update(tags)
-        child_info = BookInfoStub(parts=[gchild_info.url], **kwargs)
-        # other grandchild
-        kwargs = info_args(u"Different GChild")
-        kwargs.update(tags)
-        diffgchild_info = BookInfoStub(**kwargs)
-        # other child
-        kwargs = info_args(u"Different Child")
-        kwargs.update(tags)
-        kwargs['kind'] = 'K2'
-        diffchild_info = BookInfoStub(parts=[diffgchild_info.url], **kwargs)
-        # parent
-        kwargs = info_args(u"Parent")
-        kwargs.update(tags)
-        parent_info = BookInfoStub(parts=[child_info.url, diffchild_info.url], **kwargs)
-
-        # create the books
-        book_file = ContentFile('<utwor />')
-        for info in gchild_info, child_info, diffgchild_info, diffchild_info, parent_info:
-            book = models.Book.from_text_and_meta(book_file, info)
-
-        # useful tags
-        self.author = models.Tag.objects.get(name='Common Man', category='author')
-        tag_empty = models.Tag(name='Empty tag', slug='empty', category='author')
-        tag_empty.save()
-        
-        self.client = Client()
-    
-    
-    def tearDown(self):
-        models.Book.objects.all().delete()
-
-    
-    def test_nonexistent_tag(self):
-        """ Looking for a non-existent tag should yield 404 """
-        self.assertEqual(404, self.client.get('/katalog/czeslaw_milosz/').status_code)
-        
-    def test_book_tag(self):
-        """ Looking for a book tag isn't permitted """
-        self.assertEqual(404, self.client.get('/katalog/parent/').status_code)
-    
-    def test_tag_empty(self):
-        """ Tag with no books should return no books """
-        context = self.client.get('/katalog/empty/').context
-        self.assertEqual(0, len(context['object_list']))
-    
-    def test_tag_common(self):
-        """ Filtering by tag should only yield top-level books. """
-        context = self.client.get('/katalog/%s/' % self.author.slug).context
-        self.assertEqual([book.title for book in context['object_list']],
-                         ['Parent'])
-
-    def test_tag_child(self):
-        """ Filtering by child's tag should yield the child """
-        context = self.client.get('/katalog/k2/').context
-        self.assertEqual([book.title for book in context['object_list']],
-                         ['Different Child'])
-
-    def test_tag_child_jump(self):
-        """ Of parent and grandchild, only parent should be returned. """
-        context = self.client.get('/katalog/k/').context
-        self.assertEqual([book.title for book in context['object_list']],
-                         ['Parent'])
-        
-
-class TagRelatedTagsTests(TestCase):
-    """ tests the /katalog/tag/ page for related tags """
-    
-    def setUp(self):
-        author = PersonStub(("Common",), "Man")
-
-        gchild_info = BookInfoStub(author=author, genre="GchildGenre", epoch='Epoch', kind="Kind", 
-                                   **info_args(u"GChild"))
-        child1_info = BookInfoStub(author=author, genre="ChildGenre", epoch='Epoch', kind="ChildKind",
-                                   parts=[gchild_info.url],
-                                   **info_args(u"Child1"))
-        child2_info = BookInfoStub(author=author, genre="ChildGenre", epoch='Epoch', kind="ChildKind",
-                                   **info_args(u"Child2"))
-        parent_info = BookInfoStub(author=author, genre="Genre", epoch='Epoch', kind="Kind", 
-                                   parts=[child1_info.url, child2_info.url],
-                                   **info_args(u"Parent"))
-        
-        for info in gchild_info, child1_info, child2_info, parent_info:
-            book_text = """<utwor><opowiadanie><akap>
-                <begin id="m01" />
-                    <motyw id="m01">Theme, %sTheme</motyw>
-                    Ala ma kota
-                <end id="m01" />
-                </akap></opowiadanie></utwor>
-                """ % info.title.encode('utf-8')
-            book = models.Book.from_text_and_meta(ContentFile(book_text), info)
-            book.save()
-        
-        tag_empty = models.Tag(name='Empty tag', slug='empty', category='author')
-        tag_empty.save()
-
-        self.client = Client()
-    
-    
-    def tearDown(self):
-        models.Book.objects.all().delete()
-    
-    
-    def test_empty(self):
-        """ empty tag should have no related tags """
-        
-        cats = self.client.get('/katalog/empty/').context['categories']
-        self.assertEqual(cats, {}, 'tags related to empty tag')
-    
-    
-    def test_has_related(self):
-        """ related own and descendants' tags should be generated """
-        
-        cats = self.client.get('/katalog/kind/').context['categories']
-        self.assertTrue('Common Man' in [tag.name for tag in cats['author']],
-                        'missing `author` related tag')
-        self.assertTrue('Epoch' in [tag.name for tag in cats['epoch']],
-                        'missing `epoch` related tag')
-        self.assertTrue("ChildKind" in [tag.name for tag in cats['kind']],
-                        "missing `kind` related tag")
-        self.assertTrue("Genre" in [tag.name for tag in cats['genre']],
-                        'missing `genre` related tag')
-        self.assertTrue("ChildGenre" in [tag.name for tag in cats['genre']],
-                        "missing child's related tag")
-        self.assertTrue("GchildGenre" in [tag.name for tag in cats['genre']],
-                        "missing grandchild's related tag")
-        self.assertTrue('Theme' in [tag.name for tag in cats['theme']],
-                        "missing related theme")
-        self.assertTrue('Child1Theme' in [tag.name for tag in cats['theme']],
-                        "missing child's related theme")
-        self.assertTrue('GChildTheme' in [tag.name for tag in cats['theme']],
-                        "missing grandchild's related theme")
-    
-    
-    def test_related_differ(self):
-        """ related tags shouldn't include filtering tags """
-        
-        cats = self.client.get('/katalog/kind/').context['categories']
-        self.assertFalse('Kind' in [tag.name for tag in cats['kind']],
-                         'filtering tag wrongly included in related')
-        cats = self.client.get('/katalog/theme/').context['categories']
-        self.assertFalse('Theme' in [tag.name for tag in cats['theme']],
-                         'filtering theme wrongly included in related')
-    
-    
-    def test_parent_tag_once(self):
-        """ if parent and descendants have a common tag, count it only once """
-
-        cats = self.client.get('/katalog/kind/').context['categories']
-        self.assertEqual([(tag.name, tag.count) for tag in cats['epoch']],
-                         [('Epoch', 1)],
-                         'wrong related tag epoch tag on tag page')
-    
-    
-    def test_siblings_tags_add(self):
-        """ if children have tags and parent hasn't, count the children """
-        
-        cats = self.client.get('/katalog/epoch/').context['categories']
-        self.assertTrue(('ChildKind', 2) in [(tag.name, tag.count) for tag in cats['kind']],
-                    'wrong related kind tags on tag page')
-    
-    def test_themes_add(self):
-        """ all occurencies of theme should be counted """
-
-        cats = self.client.get('/katalog/epoch/').context['categories']
-        self.assertTrue(('Theme', 4) in [(tag.name, tag.count) for tag in cats['theme']],
-                    'wrong related theme count')
-    
-
-
-class CleanTagRelationTests(TestCase):
-    """ tests for tag relations cleaning after deleting things """
-    
-    def setUp(self):
-        author = PersonStub(("Common",), "Man")
-
-        book_info = BookInfoStub(author=author, genre="G", epoch='E', kind="K", 
-                                   **info_args(u"Book"))
-        book_text = """<utwor><opowiadanie><akap>
-            <begin id="m01" /><motyw id="m01">Theme</motyw>Ala ma kota
-            <end id="m01" />
-            </akap></opowiadanie></utwor>
-            """
-        book = models.Book.from_text_and_meta(ContentFile(book_text), book_info)
-        book.save()
-        
-        self.client = Client()
-    
-    
-    def tearDown(self):
-        models.Book.objects.all().delete()
-    
-    
-    def test_delete_objects(self):
-        """ there should be no related tags left after deleting some objects """
-        
-        models.Book.objects.all().delete()
-        cats = self.client.get('/katalog/k/').context['categories']
-        self.assertEqual({}, cats)
-
-
-    def test_deleted_tag(self):
-        """ there should be no tag relations left after deleting tags """
-        
-        models.Tag.objects.all().delete()
-        cats = self.client.get('/katalog/lektura/book/').context['categories']
-        self.assertEqual(cats, {})
-
-
-class TestIdenticalTag(TestCase):
-    
-    def setUp(self):
-        author = PersonStub(("A",), "B")
-
-        book_info = BookInfoStub(author=author, genre="A B", epoch='A B', kind="A B", 
-                                   **info_args(u"A B"))
-        book_text = """<utwor><opowiadanie><akap>
-            <begin id="m01" /><motyw id="m01">A B</motyw>Ala ma kota
-            <end id="m01" />
-            </akap></opowiadanie></utwor>
-            """
-        book = models.Book.from_text_and_meta(ContentFile(book_text), book_info)
-        book.save()
-        
-        self.client = Client()
-    
-    
-    def tearDown(self):
-        models.Book.objects.all().delete()
-    
-    
-    def test_book_tags(self):
-        """ there should be all related tags in relevant categories """
-        
-        cats = self.client.get('/katalog/lektura/a-b/').context['categories']
-        for category in 'author', 'kind', 'genre', 'epoch', 'theme':
-            self.assertTrue('A B' in [tag.name for tag in cats[category]],
-                            'missing related tag for %s' % category)
-
-    def test_qualified_url(self):
-        categories = {'author': 'autor', 'theme': 'motyw', 'epoch': 'epoka', 'kind':'rodzaj', 'genre':'gatunek'}
-        for cat, localcat in categories.iteritems():
-            context = self.client.get('/katalog/%s/a-b/' % localcat).context
-            self.assertEqual(1, len(context['object_list']))
-            self.assertNotEqual({}, context['categories'])
-            self.assertFalse(cat in context['categories'])
-
diff --git a/apps/catalogue/tests/__init__.py b/apps/catalogue/tests/__init__.py
new file mode 100644 (file)
index 0000000..d656d45
--- /dev/null
@@ -0,0 +1,3 @@
+from catalogue.tests.book_import import *
+from catalogue.tests.tags import *
+from catalogue.tests.search import *
diff --git a/apps/catalogue/tests/book_import.py b/apps/catalogue/tests/book_import.py
new file mode 100644 (file)
index 0000000..fed9922
--- /dev/null
@@ -0,0 +1,109 @@
+# -*- coding: utf-8 -*-
+from django.core.files.base import ContentFile
+from catalogue.test_utils import *
+from catalogue import models
+
+class BookImportLogicTests(WLTestCase):
+
+    def setUp(self):
+        WLTestCase.setUp(self)
+        self.book_info = BookInfoStub(
+            url=u"http://wolnelektury.pl/example/default_book",
+            about=u"http://wolnelektury.pl/example/URI/default_book",
+            title=u"Default Book",
+            author=PersonStub(("Jim",), "Lazy"),
+            kind="X-Kind",
+            genre="X-Genre",
+            epoch="X-Epoch",
+        )
+
+        self.expected_tags = [
+           ('author', 'jim-lazy'),
+           ('genre', 'x-genre'),
+           ('epoch', 'x-epoch'),
+           ('kind', 'x-kind'),
+        ]
+        self.expected_tags.sort()
+
+    def test_empty_book(self):
+        BOOK_TEXT = "<utwor />"
+        book = models.Book.from_text_and_meta(ContentFile(BOOK_TEXT), self.book_info)
+
+        self.assertEqual(book.title, "Default Book")
+        self.assertEqual(book.slug, "default_book")
+        self.assert_(book.parent is None)
+        self.assertFalse(book.has_html_file())
+
+        # no fragments generated
+        self.assertEqual(book.fragments.count(), 0)
+
+        # TODO: this should be filled out probably...
+        self.assertEqual(book.wiki_link, '')
+        self.assertEqual(book.gazeta_link, '')
+        self.assertEqual(book._short_html, '')
+        self.assertEqual(book.description, '')
+
+        tags = [ (tag.category, tag.slug) for tag in book.tags ]
+        tags.sort()
+
+        self.assertEqual(tags, self.expected_tags)
+
+    def test_not_quite_empty_book(self):
+        """ Not empty, but without any real text.
+
+        Should work like any other non-empty book.
+        """
+
+        BOOK_TEXT = """<utwor>
+        <liryka_l>
+            <nazwa_utworu>Nic</nazwa_utworu>
+        </liryka_l></utwor>
+        """
+
+        book = models.Book.from_text_and_meta(ContentFile(BOOK_TEXT), self.book_info)
+        self.assertTrue(book.has_html_file())
+
+    def test_book_with_fragment(self):
+        BOOK_TEXT = """<utwor>
+        <opowiadanie>
+            <akap><begin id="m01" /><motyw id="m01">Love</motyw>Ala ma kota<end id="m01" /></akap>
+        </opowiadanie></utwor>
+        """
+
+        book = models.Book.from_text_and_meta(ContentFile(BOOK_TEXT), self.book_info)
+        self.assertTrue(book.has_html_file())
+
+        self.assertEqual(book.fragments.count(), 1)
+        self.assertEqual(book.fragments.all()[0].text, u'<p class="paragraph">Ala ma kota</p>\n')
+
+        self.assert_(('theme', 'love') in [ (tag.category, tag.slug) for tag in book.tags ])
+
+    def test_book_replace_title(self):
+        BOOK_TEXT = """<utwor />"""
+        book = models.Book.from_text_and_meta(ContentFile(BOOK_TEXT), self.book_info)
+        self.book_info.title = u"Extraordinary"
+        book = models.Book.from_text_and_meta(ContentFile(BOOK_TEXT), self.book_info)
+
+        tags = [ (tag.category, tag.slug) for tag in book.tags ]
+        tags.sort()
+
+        self.assertEqual(tags, self.expected_tags)
+
+    def test_book_replace_author(self):
+        BOOK_TEXT = """<utwor />"""
+        book = models.Book.from_text_and_meta(ContentFile(BOOK_TEXT), self.book_info)
+        self.book_info.author = PersonStub(("Hans", "Christian"), "Andersen")
+        book = models.Book.from_text_and_meta(ContentFile(BOOK_TEXT), self.book_info)
+
+        tags = [ (tag.category, tag.slug) for tag in book.tags ]
+        tags.sort()
+
+        self.expected_tags.remove(('author', 'jim-lazy'))
+        self.expected_tags.append(('author', 'hans-christian-andersen'))
+        self.expected_tags.sort()
+
+        self.assertEqual(tags, self.expected_tags)
+
+        # the old tag should disappear
+        self.assertRaises(models.Tag.DoesNotExist, models.Tag.objects.get,
+                    slug="jim-lazy", category="author")
diff --git a/apps/catalogue/tests/search.py b/apps/catalogue/tests/search.py
new file mode 100644 (file)
index 0000000..93bec87
--- /dev/null
@@ -0,0 +1,71 @@
+# -*- coding: utf-8 -*-
+from catalogue import models, views
+from catalogue.test_utils import *
+
+from nose.tools import raises
+
+class BasicSearchLogicTests(WLTestCase):
+
+    def setUp(self):
+        WLTestCase.setUp(self)
+        self.author_tag = models.Tag.objects.create(
+                                name=u'Adam Mickiewicz [SubWord]',
+                                category=u'author', slug="one")
+
+        self.unicode_tag = models.Tag.objects.create(
+                                name=u'Tadeusz Żeleński (Boy)',
+                                category=u'author', slug="two")
+
+        self.polish_tag = models.Tag.objects.create(
+                                name=u'ĘÓĄŚŁŻŹĆŃęóąśłżźćń',
+                                category=u'author', slug="three")
+
+    @raises(ValueError)
+    def test_empty_query(self):
+        """ Check that empty queries raise an error. """
+        views.find_best_matches(u'')
+
+    @raises(ValueError)
+    def test_one_letter_query(self):
+        """ Check that one letter queries aren't permitted. """
+        views.find_best_matches(u't')
+
+    def test_match_by_prefix(self):
+        """ Tags should be matched by prefix of words within it's name. """
+        self.assertEqual(views.find_best_matches(u'Ada'), (self.author_tag,))
+        self.assertEqual(views.find_best_matches(u'Mic'), (self.author_tag,))
+        self.assertEqual(views.find_best_matches(u'Mickiewicz'), (self.author_tag,))
+
+    def test_match_case_insensitive(self):
+        """ Tag names should match case insensitive. """
+        self.assertEqual(views.find_best_matches(u'adam mickiewicz'), (self.author_tag,))
+
+    def test_match_case_insensitive_unicode(self):
+        """ Tag names should match case insensitive (unicode). """
+        self.assertEqual(views.find_best_matches(u'tadeusz żeleński (boy)'), (self.unicode_tag,))
+
+    def test_word_boundary(self):
+        self.assertEqual(views.find_best_matches(u'SubWord'), (self.author_tag,))
+        self.assertEqual(views.find_best_matches(u'[SubWord'), (self.author_tag,))
+
+    def test_unrelated_search(self):
+        self.assertEqual(views.find_best_matches(u'alamakota'), tuple())
+        self.assertEqual(views.find_best_matches(u'Adama'), ())
+
+    def test_infix_doesnt_match(self):
+        """ Searching for middle of a word shouldn't match. """
+        self.assertEqual(views.find_best_matches(u'deusz'), tuple())
+
+    def test_diactricts_removal_pl(self):
+        """ Tags should match both with and without national characters. """
+        self.assertEqual(views.find_best_matches(u'ĘÓĄŚŁŻŹĆŃęóąśłżźćń'), (self.polish_tag,))
+        self.assertEqual(views.find_best_matches(u'EOASLZZCNeoaslzzcn'), (self.polish_tag,))
+        self.assertEqual(views.find_best_matches(u'eoaslzzcneoaslzzcn'), (self.polish_tag,))
+
+    def test_diactricts_query_removal_pl(self):
+        """ Tags without national characters shouldn't be matched by queries with them. """
+        self.assertEqual(views.find_best_matches(u'Adąm'), ())
+
+    def test_sloppy(self):
+        self.assertEqual(views.find_best_matches(u'Żelenski'), (self.unicode_tag,))
+        self.assertEqual(views.find_best_matches(u'zelenski'), (self.unicode_tag,))
diff --git a/apps/catalogue/tests/tags.py b/apps/catalogue/tests/tags.py
new file mode 100644 (file)
index 0000000..f9102a7
--- /dev/null
@@ -0,0 +1,257 @@
+# -*- coding: utf-8 -*-
+from catalogue import models
+from catalogue.test_utils import *
+from django.core.files.base import ContentFile
+from slughifi import slughifi
+
+from nose.tools import raises
+
+def info_args(title):
+    """ generate some keywords for comfortable BookInfoCreation  """
+    slug = unicode(slughifi(title))
+    return {
+        'title': unicode(title),
+        'slug': slug,
+        'url': u"http://wolnelektury.pl/example/%s" % slug,
+        'about': u"http://wolnelektury.pl/example/URI/%s" % slug,
+    }
+
+
+class BooksByTagTests(WLTestCase):
+    """ tests the /katalog/tag page for found books """
+
+    def setUp(self):
+        WLTestCase.setUp(self)
+        author = PersonStub(("Common",), "Man")
+        tags = dict(genre='G', epoch='E', author=author, kind="K")
+
+        # grandchild
+        kwargs = info_args(u"GChild")
+        kwargs.update(tags)
+        gchild_info = BookInfoStub(**kwargs)
+        # child
+        kwargs = info_args(u"Child")
+        kwargs.update(tags)
+        child_info = BookInfoStub(parts=[gchild_info.url], **kwargs)
+        # other grandchild
+        kwargs = info_args(u"Different GChild")
+        kwargs.update(tags)
+        diffgchild_info = BookInfoStub(**kwargs)
+        # other child
+        kwargs = info_args(u"Different Child")
+        kwargs.update(tags)
+        kwargs['kind'] = 'K2'
+        diffchild_info = BookInfoStub(parts=[diffgchild_info.url], **kwargs)
+        # parent
+        kwargs = info_args(u"Parent")
+        kwargs.update(tags)
+        parent_info = BookInfoStub(parts=[child_info.url, diffchild_info.url], **kwargs)
+
+        # create the books
+        book_file = ContentFile('<utwor />')
+        for info in gchild_info, child_info, diffgchild_info, diffchild_info, parent_info:
+            book = models.Book.from_text_and_meta(book_file, info)
+
+        # useful tags
+        self.author = models.Tag.objects.get(name='Common Man', category='author')
+        models.Tag.objects.create(name='Empty tag', slug='empty', category='author')
+
+    def test_nonexistent_tag(self):
+        """ Looking for a non-existent tag should yield 404 """
+        # NOTE: this yields a false positive, 'cause of URL change
+        self.assertEqual(404, self.client.get('/katalog/czeslaw_milosz/').status_code)
+
+    def test_book_tag(self):
+        """ Looking for a book tag isn't permitted """
+        self.assertEqual(404, self.client.get('/katalog/parent/').status_code)
+
+    def test_tag_empty(self):
+        """ Tag with no books should return no books """
+        context = self.client.get('/katalog/empty/').context
+        self.assertEqual(0, len(context['object_list']))
+
+    def test_tag_common(self):
+        """ Filtering by tag should only yield top-level books. """
+        context = self.client.get('/katalog/%s/' % self.author.slug).context
+        self.assertEqual([book.title for book in context['object_list']],
+                         ['Parent'])
+
+    def test_tag_child(self):
+        """ Filtering by child's tag should yield the child """
+        context = self.client.get('/katalog/k2/').context
+        self.assertEqual([book.title for book in context['object_list']],
+                         ['Different Child'])
+
+    def test_tag_child_jump(self):
+        """ Of parent and grandchild, only parent should be returned. """
+        context = self.client.get('/katalog/k/').context
+        self.assertEqual([book.title for book in context['object_list']],
+                         ['Parent'])
+
+
+class TagRelatedTagsTests(WLTestCase):
+    """ tests the /katalog/tag/ page for related tags """
+
+    def setUp(self):
+        WLTestCase.setUp(self)
+        author = PersonStub(("Common",), "Man")
+
+        gchild_info = BookInfoStub(author=author, genre="GchildGenre", epoch='Epoch', kind="Kind",
+                                   **info_args(u"GChild"))
+        child1_info = BookInfoStub(author=author, genre="ChildGenre", epoch='Epoch', kind="ChildKind",
+                                   parts=[gchild_info.url],
+                                   **info_args(u"Child1"))
+        child2_info = BookInfoStub(author=author, genre="ChildGenre", epoch='Epoch', kind="ChildKind",
+                                   **info_args(u"Child2"))
+        parent_info = BookInfoStub(author=author, genre="Genre", epoch='Epoch', kind="Kind",
+                                   parts=[child1_info.url, child2_info.url],
+                                   **info_args(u"Parent"))
+
+        for info in gchild_info, child1_info, child2_info, parent_info:
+            book_text = """<utwor><opowiadanie><akap>
+                <begin id="m01" />
+                    <motyw id="m01">Theme, %sTheme</motyw>
+                    Ala ma kota
+                <end id="m01" />
+                </akap></opowiadanie></utwor>
+                """ % info.title.encode('utf-8')
+            book = models.Book.from_text_and_meta(ContentFile(book_text), info)
+            book.save()
+
+        tag_empty = models.Tag(name='Empty tag', slug='empty', category='author')
+        tag_empty.save()
+
+    def test_empty(self):
+        """ empty tag should have no related tags """
+
+        cats = self.client.get('/katalog/empty/').context['categories']
+        self.assertEqual(cats, {}, 'tags related to empty tag')
+
+    def test_has_related(self):
+        """ related own and descendants' tags should be generated """
+
+        cats = self.client.get('/katalog/kind/').context['categories']
+        self.assertTrue('Common Man' in [tag.name for tag in cats['author']],
+                        'missing `author` related tag')
+        self.assertTrue('Epoch' in [tag.name for tag in cats['epoch']],
+                        'missing `epoch` related tag')
+        self.assertTrue("ChildKind" in [tag.name for tag in cats['kind']],
+                        "missing `kind` related tag")
+        self.assertTrue("Genre" in [tag.name for tag in cats['genre']],
+                        'missing `genre` related tag')
+        self.assertTrue("ChildGenre" in [tag.name for tag in cats['genre']],
+                        "missing child's related tag")
+        self.assertTrue("GchildGenre" in [tag.name for tag in cats['genre']],
+                        "missing grandchild's related tag")
+        self.assertTrue('Theme' in [tag.name for tag in cats['theme']],
+                        "missing related theme")
+        self.assertTrue('Child1Theme' in [tag.name for tag in cats['theme']],
+                        "missing child's related theme")
+        self.assertTrue('GChildTheme' in [tag.name for tag in cats['theme']],
+                        "missing grandchild's related theme")
+
+
+    def test_related_differ(self):
+        """ related tags shouldn't include filtering tags """
+
+        cats = self.client.get('/katalog/kind/').context['categories']
+        self.assertFalse('Kind' in [tag.name for tag in cats['kind']],
+                         'filtering tag wrongly included in related')
+        cats = self.client.get('/katalog/theme/').context['categories']
+        self.assertFalse('Theme' in [tag.name for tag in cats['theme']],
+                         'filtering theme wrongly included in related')
+
+
+    def test_parent_tag_once(self):
+        """ if parent and descendants have a common tag, count it only once """
+
+        cats = self.client.get('/katalog/kind/').context['categories']
+        self.assertEqual([(tag.name, tag.count) for tag in cats['epoch']],
+                         [('Epoch', 1)],
+                         'wrong related tag epoch tag on tag page')
+
+
+    def test_siblings_tags_add(self):
+        """ if children have tags and parent hasn't, count the children """
+
+        cats = self.client.get('/katalog/epoch/').context['categories']
+        self.assertTrue(('ChildKind', 2) in [(tag.name, tag.count) for tag in cats['kind']],
+                    'wrong related kind tags on tag page')
+
+    def test_themes_add(self):
+        """ all occurencies of theme should be counted """
+
+        cats = self.client.get('/katalog/epoch/').context['categories']
+        self.assertTrue(('Theme', 4) in [(tag.name, tag.count) for tag in cats['theme']],
+                    'wrong related theme count')
+
+
+class CleanTagRelationTests(WLTestCase):
+    """ tests for tag relations cleaning after deleting things """
+
+    def setUp(self):
+        WLTestCase.setUp(self)
+        author = PersonStub(("Common",), "Man")
+
+        book_info = BookInfoStub(author=author, genre="G", epoch='E', kind="K",
+                                   **info_args(u"Book"))
+        book_text = """<utwor><opowiadanie><akap>
+            <begin id="m01" /><motyw id="m01">Theme</motyw>Ala ma kota
+            <end id="m01" />
+            </akap></opowiadanie></utwor>
+            """
+        book = models.Book.from_text_and_meta(ContentFile(book_text), book_info)
+
+    def test_delete_objects(self):
+        """ there should be no related tags left after deleting some objects """
+
+        models.Book.objects.all().delete()
+        cats = self.client.get('/katalog/k/').context['categories']
+        self.assertEqual(cats, {})
+
+    def test_deleted_tag(self):
+        """ there should be no tag relations left after deleting tags """
+
+        models.Tag.objects.all().delete()
+        cats = self.client.get('/katalog/lektura/book/').context['categories']
+        self.assertEqual(cats, {})
+
+
+class TestIdenticalTag(WLTestCase):
+
+    def setUp(self):
+        WLTestCase.setUp(self)
+        author = PersonStub((), "Tag")
+
+        self.book_info = BookInfoStub(author=author,
+                                 genre="tag",
+                                 epoch='tag',
+                                 kind="tag",
+                                   **info_args(u"tag"))
+        self.book_text = """<utwor>
+            <opowiadanie>
+            <akap>
+                <begin id="m01" /><motyw id="m01">tag</motyw>Ala ma kota<end id="m01" />
+            </akap>
+            </opowiadanie>
+            </utwor>
+        """
+
+
+    def test_book_tags(self):
+        """ there should be all related tags in relevant categories """
+        models.Book.from_text_and_meta(ContentFile(self.book_text), self.book_info)
+
+        cats = self.client.get('/katalog/lektura/tag/').context['categories']
+        for category in 'author', 'kind', 'genre', 'epoch', 'theme':
+            self.assertTrue('tag' in [tag.name for tag in cats[category]],
+                            'missing related tag for %s' % category)
+
+    def test_qualified_url(self):
+        models.Book.from_text_and_meta(ContentFile(self.book_text), self.book_info)
+        categories = {'author': 'autor', 'theme': 'motyw', 'epoch': 'epoka', 'kind':'rodzaj', 'genre':'gatunek'}
+        for cat, localcat in categories.iteritems():
+            context = self.client.get('/katalog/%s/tag/' % localcat).context
+            self.assertEqual(1, len(context['object_list']))
+            self.assertNotEqual({}, context['categories'])
+            self.assertFalse(cat in context['categories'])
index 25389f4..4d44dc6 100644 (file)
@@ -20,7 +20,7 @@ urlpatterns = patterns('catalogue.views',
 
     # tools
     url(r'^zegar', 'clock', name='clock'),
-    
+
     # Public interface. Do not change this URLs.
     url(r'^lektura/(?P<slug>[a-zA-Z0-9-]+)\.html$', 'book_text', name='book_text'),
     url(r'^lektura/(?P<slug>[a-zA-Z0-9-]+)/$', 'book_detail', name='book_detail'),
index 36bd9e7..e44b379 100644 (file)
@@ -19,7 +19,7 @@ MAX_SESSION_KEY = 18446744073709551616L     # 2 << 63
 
 
 def get_random_hash(seed):
-    sha_digest = sha_constructor('%s%s%s%s' % 
+    sha_digest = sha_constructor('%s%s%s%s' %
         (randrange(0, MAX_SESSION_KEY), time.time(), unicode(seed).encode('utf-8', 'replace'),
         settings.SECRET_KEY)).digest()
     return urlsafe_b64encode(sha_digest).replace('=', '').replace('_', '-').lower()
index 4476fd2..47abe63 100644 (file)
@@ -9,7 +9,7 @@ import pprint
 import traceback
 import re
 import itertools
-from operator import itemgetter 
+from operator import itemgetter
 
 from django.conf import settings
 from django.template import RequestContext
@@ -83,7 +83,7 @@ def differentiate_tags(request, tags, ambiguous_slugs):
             'tags': [tag]
         })
     return render_to_response('catalogue/differentiate_tags.html',
-                {'tags': tags, 'options': options, 'unparsed': ambiguous_slugs[1:]}, 
+                {'tags': tags, 'options': options, 'unparsed': ambiguous_slugs[1:]},
                 context_instance=RequestContext(request))
 
 
@@ -122,7 +122,7 @@ def tagged_object_list(request, tags=''):
             fragments = models.Fragment.tagged.with_any(l_tags, fragments)
 
         # newtagging goes crazy if we just try:
-        #related_tags = models.Tag.objects.usage_for_queryset(fragments, counts=True, 
+        #related_tags = models.Tag.objects.usage_for_queryset(fragments, counts=True,
         #                    extra={'where': ["catalogue_tag.category != 'book'"]})
         fragment_keys = [fragment.pk for fragment in fragments]
         if fragment_keys:
@@ -141,7 +141,7 @@ def tagged_object_list(request, tags=''):
         descendants_keys = [book.pk for book in models.Book.tagged.with_any(l_tags)]
         if descendants_keys:
             objects = objects.exclude(pk__in=descendants_keys)
-        
+
         # get related tags from `tag_counter` and `theme_counter`
         related_counts = {}
         tags_pks = [tag.pk for tag in tags]
@@ -154,7 +154,7 @@ def tagged_object_list(request, tags=''):
         related_tags = [tag for tag in related_tags if tag not in tags]
         for tag in related_tags:
             tag.count = related_counts[tag.pk]
-        
+
         categories = split_tags(related_tags)
         del related_tags
 
@@ -238,7 +238,7 @@ def book_text(request, slug):
 
 def _no_diacritics_regexp(query):
     """ returns a regexp for searching for a query without diacritics
-    
+
     should be locale-aware """
     names = {
         u'a':u'aąĄ', u'c':u'cćĆ', u'e':u'eęĘ', u'l': u'lłŁ', u'n':u'nńŃ', u'o':u'oóÓ', u's':u'sśŚ', u'z':u'zźżŹŻ',
@@ -256,16 +256,16 @@ def unicode_re_escape(query):
 def _word_starts_with(name, prefix):
     """returns a Q object getting models having `name` contain a word
     starting with `prefix`
-    
+
     We define word characters as alphanumeric and underscore, like in JS.
-    
+
     Works for MySQL, PostgreSQL, Oracle.
     For SQLite, _sqlite* version is substituted for this.
     """
     kwargs = {}
 
     prefix = _no_diacritics_regexp(unicode_re_escape(prefix))
-    # can't use [[:<:]] (word start), 
+    # can't use [[:<:]] (word start),
     # but we want both `xy` and `(xy` to catch `(xyz)`
     kwargs['%s__iregex' % name] = u"(^|[^[:alnum:]_])%s" % prefix
 
@@ -273,8 +273,8 @@ def _word_starts_with(name, prefix):
 
 
 def _sqlite_word_starts_with(name, prefix):
-    """ version of _word_starts_with for SQLite 
-    
+    """ version of _word_starts_with for SQLite
+
     SQLite in Django uses Python re module
     """
     kwargs = {}
@@ -320,12 +320,12 @@ def _get_result_type(match):
 
 def find_best_matches(query, user=None):
     """ Finds a Book, Tag or Bookstub best matching a query.
-    
+
     Returns a with:
       - zero elements when nothing is found,
       - one element when a best result is found,
       - more then one element on multiple exact matches
-    
+
     Raises a ValueError on too short a query.
     """
 
@@ -458,7 +458,7 @@ def download_shelf(request, slug):
     """"
     Create a ZIP archive on disk and transmit it in chunks of 8KB,
     without loading the whole file into memory. A similar approach can
-    be used for large dynamic PDF files.                                        
+    be used for large dynamic PDF files.
     """
     shelf = get_object_or_404(models.Tag, slug=slug, category='set')
 
index 86f0466..cd9cf4e 100644 (file)
@@ -15,7 +15,7 @@ class Chunk(models.Model):
         ordering = ('key',)
         verbose_name = _('chunk')
         verbose_name_plural = _('chunks')
-    
+
     def __unicode__(self):
         return self.key
 
@@ -23,7 +23,7 @@ class Chunk(models.Model):
 class Attachment(models.Model):
     key = models.CharField(_('key'), help_text=_('A unique name for this attachment'), primary_key=True, max_length=255)
     attachment = models.FileField(upload_to='chunks/attachment')
-    
+
     class Meta:
         ordering = ('key',)
         verbose_name, verbose_name_plural = _('attachment'), _('attachments')
index 595482f..083c48a 100644 (file)
@@ -30,7 +30,7 @@ class ChunkNode(template.Node):
     def __init__(self, key, cache_time=0):
        self.key = key
        self.cache_time = cache_time
-    
+
     def render(self, context):
         try:
             cache_key = 'chunk_' + self.key
@@ -44,7 +44,7 @@ class ChunkNode(template.Node):
             n.save()
             return ''
         return content
-        
+
 register.tag('chunk', do_get_chunk)
 
 
@@ -58,6 +58,6 @@ def attachment(key, cache_time=0):
         return c.attachment.url
     except Attachment.DoesNotExist:
         return ''
-    
+
 register.simple_tag(attachment)
 
index 9b98531..9bb23b2 100644 (file)
@@ -6,7 +6,7 @@ class FilterBase:
         raise NotImplementedError
     def filter_js(self, js):
         raise NotImplementedError
-        
+
 class FilterError(Exception):
     """
     This exception is raised when a filter fails
index d40e8ee..ae7c103 100644 (file)
@@ -18,16 +18,16 @@ class CSSTidyFilter(FilterBase):
         tmp_file.flush()
 
         output_file = tempfile.NamedTemporaryFile(mode='w+b')
-        
+
         command = '%s %s %s %s' % (BINARY, tmp_file.name, ARGUMENTS, output_file.name)
-        
+
         command_output = os.popen(command).read()
-        
+
         filtered_css = output_file.read()
         output_file.close()
         tmp_file.close()
-        
+
         if self.verbose:
             print command_output
-        
+
         return filtered_css
index 7d581ed..03a4ac0 100644 (file)
@@ -8,12 +8,12 @@ COMPRESS_CSSTIDY_SETTINGS = getattr(settings, 'COMPRESS_CSSTIDY_SETTINGS', {})
 class CSSTidyFilter(FilterBase):
     def filter_css(self, css):
         tidy = CSSTidy()
-        
+
         for k, v in COMPRESS_CSSTIDY_SETTINGS.items():
             tidy.setSetting(k, v)
 
         tidy.parse(css)
 
         r = tidy.Output('string')
-        
+
         return r
index 6e31d25..f5624f5 100644 (file)
@@ -14,7 +14,7 @@ class Command(NoArgsCommand):
     args = ''
 
     def handle_noargs(self, **options):
-        
+
         force = options.get('force', False)
         verbosity = int(options.get('verbosity', 1))
 
index 3c59728..89b04a5 100644 (file)
@@ -97,7 +97,7 @@ def get_version(version):
     except ValueError:
         return str(version)
 
-def remove_files(path, filename, verbosity=0):    
+def remove_files(path, filename, verbosity=0):
     regex = re.compile(r'^%s$' % (os.path.basename(get_output_filename(settings.COMPRESS_VERSION_PLACEHOLDER.join([re.escape(part) for part in filename.split(settings.COMPRESS_VERSION_PLACEHOLDER)]), r'\d+'))))
 
     for f in os.listdir(path):
index a1289a2..a28ce3a 100644 (file)
@@ -5,9 +5,9 @@ from south.v2 import SchemaMigration
 from django.db import models
 
 class Migration(SchemaMigration):
-    
+
     def forwards(self, orm):
-        
+
         # Adding model 'InfoPage'
         db.create_table('infopages_infopage', (
             ('title_de', self.gf('django.db.models.fields.CharField')(max_length=120, null=True, blank=True)),
@@ -55,14 +55,14 @@ class Migration(SchemaMigration):
             call_command("loaddata", "wl_data")
 
         db.send_create_signal('infopages', ['InfoPage'])
-    
-    
+
+
     def backwards(self, orm):
-        
+
         # Deleting model 'InfoPage'
         db.delete_table('infopages_infopage')
-    
-    
+
+
     models = {
         'infopages.infopage': {
             'Meta': {'object_name': 'InfoPage'},
@@ -106,5 +106,5 @@ class Migration(SchemaMigration):
             'title_uk': ('django.db.models.fields.CharField', [], {'max_length': '120', 'null': True, 'blank': True})
         }
     }
-    
+
     complete_apps = ['infopages']
index 5026da2..9fe7b32 100644 (file)
@@ -9,7 +9,7 @@ class InfoPage(models.Model):
     """
     An InfoPage is used to display a two-column flatpage
     """
-    
+
     page_title = models.CharField(_('page title'), max_length=120, blank=True)
     slug = models.SlugField(_('slug'), max_length=120, unique=True, db_index=True)
     title = models.CharField(_('title'), max_length=120, blank=True)
@@ -20,7 +20,7 @@ class InfoPage(models.Model):
         ordering = ('slug',)
         verbose_name = _('info page')
         verbose_name_plural = _('info pages')
-    
+
     def __unicode__(self):
         return self.title
 
index dc113ed..ffb2b6c 100644 (file)
@@ -16,14 +16,14 @@ class Document(models.Model):
     slideshare_id = models.CharField(_('slideshare ID'), blank=True, max_length=120)
     description = models.TextField(_('description'), blank=True)
     created_at = models.DateTimeField(auto_now_add=True)
-    
+
     def slideshare_player(self):
         base, ext = path.splitext(self.file.name)
         if ext in ('.ppt', '.pps', '.pot', '.pptx', '.potx', '.ppsx', '.odp', '.key', '.zip', '.pdf',):
             return 'ssplayer2.swf'
         else:
             return 'ssplayerd.swf'
-    
+
     class Meta:
         ordering = ['slug']
         verbose_name, verbose_name_plural = _("document"), _("documents")
index 69a47e3..6c89e5f 100644 (file)
@@ -15,7 +15,7 @@ urlpatterns = patterns('',
             'form': forms.SearchForm(),
         },
     }, name='lessons_document_list'),
-    
+
     url(r'^(?P<slug>[a-zA-Z0-9_-]+)/$', 'lessons.views.document_detail', name='lessons_document_detail'),
 )
 
index 69380d6..242526d 100644 (file)
@@ -11,7 +11,7 @@ def document_detail(request, slug):
     template_name = 'lessons/document_detail.html'
     if request.is_ajax():
         template_name = 'lessons/ajax_document_detail.html'
-    
+
     return object_detail(request,
         slug=slug,
         slug_field='slug',
index 956d2cf..a8f5110 100644 (file)
@@ -17,7 +17,7 @@ class FilteredSelectMultiple(forms.SelectMultiple):
         js = ['js/SelectBox.js' , 'js/SelectFilter2.js']
         return forms.Media(js=['%s%s' % (settings.ADMIN_MEDIA_PREFIX, url) for url in js])
     media = property(_media)
-    
+
     def __init__(self, verbose_name, is_stacked, attrs=None, choices=()):
         self.verbose_name = verbose_name
         self.is_stacked = is_stacked
@@ -44,7 +44,7 @@ class TaggableModelForm(forms.ModelForm):
             kwargs['initial']['tags'] = [tag.id for tag in self.tag_model.objects.get_for_object(kwargs['instance'])]
         super(TaggableModelForm, self).__init__(*args, **kwargs)
         self.fields['tags'].choices = [(tag.id, tag.name) for tag in self.tag_model.objects.all()]
-    
+
     def save(self, commit):
         obj = super(TaggableModelForm, self).save()
         tag_ids = self.cleaned_data['tags']
@@ -59,7 +59,7 @@ class TaggableModelForm(forms.ModelForm):
 
 class TaggableModelAdmin(admin.ModelAdmin):
     form = TaggableModelForm
-    
+
     def get_form(self, request, obj=None):
         form = super(TaggableModelAdmin, self).get_form(request, obj)
         form.tag_model = self.tag_model
index 7870092..3107070 100644 (file)
@@ -14,15 +14,15 @@ class ModelTagManager(models.Manager):
     def __init__(self, tag_model):
         super(ModelTagManager, self).__init__()
         self.tag_model = tag_model
-    
+
     def get_query_set(self):
         content_type = ContentType.objects.get_for_model(self.model)
         return self.tag_model.objects.filter(
             items__content_type__pk=content_type.pk).distinct()
-    
+
     def related(self, tags, *args, **kwargs):
         return self.tag_model.objects.related_for_model(tags, self.model, *args, **kwargs)
-    
+
     def usage(self, *args, **kwargs):
         return self.tag_model.objects.usage_for_model(self.model, *args, **kwargs)
 
@@ -62,7 +62,7 @@ class TagDescriptor(object):
     """
     def __init__(self, tag_model):
         self.tag_model = tag_model
-    
+
     def __get__(self, instance, owner):
         if not instance:
             tag_manager = ModelTagManager(self.tag_model)
index 2055ec3..1c35254 100644 (file)
@@ -6,7 +6,7 @@ Models and managers for generic tagging.
 # Python 2.3 compatibility
 try:
     set
-except NameError: 
+except NameError:
     from sets import Set as set
 
 from django.contrib.contenttypes import generic
@@ -45,7 +45,7 @@ class TagManager(models.Manager):
     def __init__(self, intermediary_table_model):
         super(TagManager, self).__init__()
         self.intermediary_table_model = intermediary_table_model
-    
+
     def update_tags(self, obj, tags):
         """
         Update tags associated with an object.
@@ -54,7 +54,7 @@ class TagManager(models.Manager):
         current_tags = list(self.filter(items__content_type__pk=content_type.pk,
                                         items__object_id=obj.pk))
         updated_tags = self.model.get_tag_list(tags)
-    
+
         # Remove tags which no longer apply
         tags_for_removal = [tag for tag in current_tags \
                             if tag not in updated_tags]
@@ -66,7 +66,7 @@ class TagManager(models.Manager):
         for tag in updated_tags:
             if tag not in current_tags:
                 self.intermediary_table_model._default_manager.create(tag=tag, content_object=obj)
-    
+
     def remove_tag(self, obj, tag):
         """
         Remove tag from an object.
@@ -83,7 +83,7 @@ class TagManager(models.Manager):
         ctype = ContentType.objects.get_for_model(obj)
         return self.filter(items__content_type__pk=ctype.pk,
                            items__object_id=obj.pk)
-    
+
     def _get_usage(self, model, counts=False, min_count=None, extra_joins=None, extra_criteria=None, params=None, extra=None):
         """
         Perform the custom SQL query for ``usage_for_model`` and
@@ -94,12 +94,12 @@ class TagManager(models.Manager):
         model_table = qn(model._meta.db_table)
         model_pk = '%s.%s' % (model_table, qn(model._meta.pk.column))
         tag_columns = self._get_tag_columns()
-        
+
         if extra is None: extra = {}
         extra_where = ''
         if 'where' in extra:
             extra_where = 'AND ' + ' AND '.join(extra['where'])
-        
+
         query = """
         SELECT DISTINCT %(tag_columns)s%(count_sql)s
         FROM
@@ -224,12 +224,12 @@ class TagManager(models.Manager):
         tag_count = len(tags)
         tagged_item_table = qn(self.intermediary_table_model._meta.db_table)
         tag_columns = self._get_tag_columns()
-        
+
         if extra is None: extra = {}
         extra_where = ''
         if 'where' in extra:
             extra_where = 'AND ' + ' AND '.join(extra['where'])
-        
+
         # Temporary table in this query is a hack to prevent MySQL from executing
         # inner query as dependant query (which could result in severe performance loss)
         query = """
@@ -301,7 +301,7 @@ class TaggedItemManager(models.Manager):
     def __init__(self, tag_model):
         super(TaggedItemManager, self).__init__()
         self.tag_model = tag_model
-    
+
     def get_by_model(self, queryset_or_model, tags):
         """
         Create a ``QuerySet`` containing instances of the specified
@@ -473,7 +473,7 @@ class TaggedItemManager(models.Manager):
 def create_intermediary_table_model(model):
     """Create an intermediary table model for the specific tag model"""
     name = model.__name__ + 'Relation'
-     
+
     class Meta:
         db_table = '%s_relation' % model._meta.db_table
         unique_together = (('tag', 'content_type', 'object_id'),)
@@ -483,8 +483,8 @@ def create_intermediary_table_model(model):
             return u'%s [%s]' % (self.content_type.get_object_for_this_type(pk=self.object_id), self.tag)
         except ObjectDoesNotExist:
             return u'<deleted> [%s]' % self.tag
-            
-    # Set up a dictionary to simulate declarations within a class    
+
+    # Set up a dictionary to simulate declarations within a class
     attrs = {
         '__module__': model.__module__,
         'Meta': Meta,
@@ -513,15 +513,15 @@ class TagMeta(ModelBase):
 class TagBase(models.Model):
     """Abstract class to be inherited by model classes."""
     __metaclass__ = TagMeta
-    
+
     class Meta:
         abstract = True
-    
+
     @staticmethod
     def get_tag_list(tag_list):
         """
         Utility function for accepting tag input in a flexible manner.
-        
+
         You should probably override this method in your subclass.
         """
         if isinstance(tag_list, TagBase):
index 7d09707..19b8e82 100644 (file)
@@ -26,7 +26,7 @@ class NoAuthentication(object):
 class HttpBasicAuthentication(object):
     """
     Basic HTTP authenticater. Synopsis:
-    
+
     Authentication handlers must implement two methods:
      - `is_authenticated`: Will be called when checking for
         authentication. Receives a `request` object, please
@@ -46,7 +46,7 @@ class HttpBasicAuthentication(object):
 
         if not auth_string:
             return False
-            
+
         try:
             (authmeth, auth) = auth_string.split(" ", 1)
 
@@ -57,12 +57,12 @@ class HttpBasicAuthentication(object):
             (username, password) = auth.split(':', 1)
         except (ValueError, binascii.Error):
             return False
-        
+
         request.user = self.auth_func(username=username, password=password) \
             or AnonymousUser()
-                
+
         return not request.user in (False, None, AnonymousUser())
-        
+
     def challenge(self):
         resp = HttpResponse("Authorization Required")
         resp['WWW-Authenticate'] = 'Basic realm="%s"' % self.realm
@@ -78,7 +78,7 @@ class HttpBasicSimple(HttpBasicAuthentication):
         self.password = password
 
         super(HttpBasicSimple, self).__init__(auth_func=self.hash, realm=realm)
-    
+
     def hash(self, username, password):
         if username == self.user.username and password == self.password:
             return self.user
@@ -122,17 +122,17 @@ def initialize_server_request(request):
     request.META['Authorization'] = request.META.get('HTTP_AUTHORIZATION', '')
 
     oauth_request = oauth.OAuthRequest.from_request(
-        request.method, request.build_absolute_uri(), 
+        request.method, request.build_absolute_uri(),
         headers=request.META, parameters=params,
         query_string=request.environ.get('QUERY_STRING', ''))
-        
+
     if oauth_request:
         oauth_server = oauth.OAuthServer(oauth_datastore(oauth_request))
         oauth_server.add_signature_method(oauth.OAuthSignatureMethod_PLAINTEXT())
         oauth_server.add_signature_method(oauth.OAuthSignatureMethod_HMAC_SHA1())
     else:
         oauth_server = None
-        
+
     return oauth_server, oauth_request
 
 def send_oauth_error(err=None):
@@ -152,7 +152,7 @@ def send_oauth_error(err=None):
 
 def oauth_request_token(request):
     oauth_server, oauth_request = initialize_server_request(request)
-    
+
     if oauth_server is None:
         return INVALID_PARAMS_RESPONSE
     try:
@@ -176,20 +176,20 @@ def oauth_auth_view(request, token, callback, params):
 @login_required
 def oauth_user_auth(request):
     oauth_server, oauth_request = initialize_server_request(request)
-    
+
     if oauth_request is None:
         return INVALID_PARAMS_RESPONSE
-        
+
     try:
         token = oauth_server.fetch_request_token(oauth_request)
     except oauth.OAuthError, err:
         return send_oauth_error(err)
-        
+
     try:
         callback = oauth_server.get_callback(oauth_request)
     except:
         callback = None
-    
+
     if request.method == "GET":
         params = oauth_request.get_normalized_parameters()
 
@@ -207,26 +207,26 @@ def oauth_user_auth(request):
             else:
                 args = '?error=%s' % 'Access not granted by user.'
                 print "FORM ERROR", form.errors
-            
+
             if not callback:
                 callback = getattr(settings, 'OAUTH_CALLBACK_VIEW')
                 return get_callable(callback)(request, token)
-                
+
             response = HttpResponseRedirect(callback+args)
-                
+
         except oauth.OAuthError, err:
             response = send_oauth_error(err)
     else:
         response = HttpResponse('Action not allowed.')
-            
+
     return response
 
 def oauth_access_token(request):
     oauth_server, oauth_request = initialize_server_request(request)
-    
+
     if oauth_request is None:
         return INVALID_PARAMS_RESPONSE
-        
+
     try:
         token = oauth_server.fetch_access_token(oauth_request)
         return HttpResponse(token.to_string())
@@ -234,7 +234,7 @@ def oauth_access_token(request):
         return send_oauth_error(err)
 
 INVALID_PARAMS_RESPONSE = send_oauth_error(oauth.OAuthError('Invalid request parameters.'))
-                
+
 class OAuthAuthentication(object):
     """
     OAuth authentication. Based on work by Leah Culver.
@@ -242,12 +242,12 @@ class OAuthAuthentication(object):
     def __init__(self, realm='API'):
         self.realm = realm
         self.builder = oauth.build_authenticate_header
-    
+
     def is_authenticated(self, request):
         """
         Checks whether a means of specifying authentication
         is provided, and if so, if it is a valid token.
-        
+
         Read the documentation on `HttpBasicAuthentication`
         for more information about what goes on here.
         """
@@ -263,14 +263,14 @@ class OAuthAuthentication(object):
                 request.consumer = consumer
                 request.throttle_extra = token.consumer.id
                 return True
-            
+
         return False
-        
+
     def challenge(self):
         """
         Returns a 401 response with a small bit on
         what OAuth is, and where to learn more about it.
-        
+
         When this was written, browsers did not understand
         OAuth authentication on the browser side, and hence
         the helpful template we render. Maybe some day in the
@@ -290,7 +290,7 @@ class OAuthAuthentication(object):
         response.content = tmpl
 
         return response
-        
+
     @staticmethod
     def is_valid_request(request):
         """
@@ -302,14 +302,14 @@ class OAuthAuthentication(object):
         must_have = [ 'oauth_'+s for s in [
             'consumer_key', 'token', 'signature',
             'signature_method', 'timestamp', 'nonce' ] ]
-        
+
         is_in = lambda l: all([ (p in l) for p in must_have ])
 
         auth_params = request.META.get("HTTP_AUTHORIZATION", "")
         req_params = request.REQUEST
-             
+
         return is_in(auth_params) or is_in(req_params)
-        
+
     @staticmethod
     def validate_token(request, check_timestamp=True, check_nonce=True):
         oauth_server, oauth_request = initialize_server_request(request)
index f8dc3b8..e173d01 100755 (executable)
@@ -28,7 +28,7 @@ def getinfo(func):
     - doc (the docstring : str)
     - module (the module name : str)
     - dict (the function __dict__ : str)
-    
+
     >>> def f(self, x=1, y=2, *args, **kw): pass
 
     >>> info = getinfo(f)
@@ -37,7 +37,7 @@ def getinfo(func):
     'f'
     >>> info["argnames"]
     ['self', 'x', 'y', 'args', 'kw']
-    
+
     >>> info["defaults"]
     (1, 2)
 
@@ -75,7 +75,7 @@ def update_wrapper(wrapper, model, infodict=None):
 def new_wrapper(wrapper, model):
     """
     An improvement over functools.update_wrapper. The wrapper is a generic
-    callable object. It works by generating a copy of the wrapper with the 
+    callable object. It works by generating a copy of the wrapper with the
     right signature and by updating the copy, not the original.
     Moreovoer, 'model' can be a dictionary with keys 'name', 'doc', 'module',
     'dict', 'defaults'.
@@ -126,7 +126,7 @@ def decorator(caller):
      def caller(func, *args, **kw):
          # do something
          return func(*args, **kw)
-    
+
     Here is an example of usage:
 
     >>> @decorator
@@ -136,7 +136,7 @@ def decorator(caller):
 
     >>> chatty.__name__
     'chatty'
-    
+
     >>> @chatty
     ... def f(): pass
     ...
@@ -164,13 +164,13 @@ if __name__ == "__main__":
     import doctest; doctest.testmod()
 
 ##########################     LEGALESE    ###############################
-      
-##   Redistributions of source code must retain the above copyright 
+
+##   Redistributions of source code must retain the above copyright
 ##   notice, this list of conditions and the following disclaimer.
 ##   Redistributions in bytecode form must reproduce the above copyright
 ##   notice, this list of conditions and the following disclaimer in
 ##   the documentation and/or other materials provided with the
-##   distribution. 
+##   distribution.
 
 ##   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 ##   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
index 63f89ec..fc14add 100644 (file)
@@ -15,14 +15,14 @@ def generate_doc(handler_cls):
     """
     if not type(handler_cls) is handler.HandlerMetaClass:
         raise ValueError("Give me handler, not %s" % type(handler_cls))
-        
+
     return HandlerDocumentation(handler_cls)
-    
+
 class HandlerMethod(object):
     def __init__(self, method, stale=False):
         self.method = method
         self.stale = stale
-        
+
     def iter_args(self):
         args, _, _, defaults = inspect.getargspec(self.method)
 
@@ -36,34 +36,34 @@ class HandlerMethod(object):
                 yield (arg, str(defaults[-didx]))
             else:
                 yield (arg, None)
-        
+
     @property
     def signature(self, parse_optional=True):
         spec = ""
 
         for argn, argdef in self.iter_args():
             spec += argn
-            
+
             if argdef:
                 spec += '=%s' % argdef
-            
+
             spec += ', '
-            
+
         spec = spec.rstrip(", ")
-        
+
         if parse_optional:
             return spec.replace("=None", "=<optional>")
-            
+
         return spec
-        
+
     @property
     def doc(self):
         return inspect.getdoc(self.method)
-    
+
     @property
     def name(self):
         return self.method.__name__
-    
+
     @property
     def http_name(self):
         if self.name == 'read':
@@ -74,21 +74,21 @@ class HandlerMethod(object):
             return 'DELETE'
         elif self.name == 'update':
             return 'PUT'
-    
+
     def __repr__(self):
         return "<Method: %s>" % self.name
-    
+
 class HandlerDocumentation(object):
     def __init__(self, handler):
         self.handler = handler
-        
+
     def get_methods(self, include_default=False):
         for method in "read create update delete".split():
             met = getattr(self.handler, method, None)
 
             if not met:
                 continue
-                
+
             stale = inspect.getmodule(met) is handler
 
             if not self.handler.is_anonymous:
@@ -97,64 +97,64 @@ class HandlerDocumentation(object):
             else:
                 if not stale or met.__name__ == "read" \
                     and 'GET' in self.allowed_methods:
-                    
+
                     yield HandlerMethod(met, stale)
-        
+
     def get_all_methods(self):
         return self.get_methods(include_default=True)
-        
+
     @property
     def is_anonymous(self):
         return handler.is_anonymous
 
     def get_model(self):
         return getattr(self, 'model', None)
-            
+
     @property
     def has_anonymous(self):
         return self.handler.anonymous
-            
+
     @property
     def anonymous(self):
         if self.has_anonymous:
             return HandlerDocumentation(self.handler.anonymous)
-            
+
     @property
     def doc(self):
         return self.handler.__doc__
-    
+
     @property
     def name(self):
         return self.handler.__name__
-    
+
     @property
     def allowed_methods(self):
         return self.handler.allowed_methods
-    
+
     def get_resource_uri_template(self):
         """
         URI template processor.
-        
+
         See http://bitworking.org/projects/URI-Templates/
         """
         def _convert(template, params=[]):
             """URI template converter"""
             paths = template % dict([p, "{%s}" % p] for p in params)
             return u'%s%s' % (get_script_prefix(), paths)
-        
+
         try:
             resource_uri = self.handler.resource_uri()
-            
+
             components = [None, [], {}]
 
             for i, value in enumerate(resource_uri):
                 components[i] = value
-        
+
             lookup_view, args, kwargs = components
             lookup_view = get_callable(lookup_view, True)
 
             possibilities = get_resolver(None).reverse_dict.getlist(lookup_view)
-            
+
             for possibility, pattern in possibilities:
                 for result, params in possibility:
                     if args:
@@ -167,9 +167,9 @@ class HandlerDocumentation(object):
                         return _convert(result, params)
         except:
             return None
-        
+
     resource_uri_template = property(get_resource_uri_template)
-    
+
     def __repr__(self):
         return u'<Documentation for "%s">' % self.name
 
@@ -180,16 +180,16 @@ def documentation_view(request):
     """
     docs = [ ]
 
-    for handler in handler_tracker: 
+    for handler in handler_tracker:
         docs.append(generate_doc(handler))
 
-    def _compare(doc1, doc2): 
+    def _compare(doc1, doc2):
        #handlers and their anonymous counterparts are put next to each other.
        name1 = doc1.name.replace("Anonymous", "")
        name2 = doc2.name.replace("Anonymous", "")
-       return cmp(name1, name2)    
+       return cmp(name1, name2)
+
     docs.sort(_compare)
-       
-    return render_to_response('documentation.html', 
+
+    return render_to_response('documentation.html',
         { 'docs': docs }, RequestContext(request))
index 1ff0a52..c2116d2 100644 (file)
@@ -58,7 +58,7 @@ class Emitter(object):
     as the methods on the handler. Issue58 says that's no good.
     """
     EMITTERS = { }
-    RESERVED_FIELDS = set([ 'read', 'update', 'create', 
+    RESERVED_FIELDS = set([ 'read', 'update', 'create',
                             'delete', 'model', 'anonymous',
                             'allowed_methods', 'fields', 'exclude' ])
 
@@ -68,16 +68,16 @@ class Emitter(object):
         self.handler = handler
         self.fields = fields
         self.anonymous = anonymous
-        
+
         if isinstance(self.data, Exception):
             raise
-    
+
     def method_fields(self, handler, fields):
         if not handler:
             return { }
 
         ret = dict()
-            
+
         for field in fields - Emitter.RESERVED_FIELDS:
             t = getattr(handler, str(field), None)
 
@@ -85,13 +85,13 @@ class Emitter(object):
                 ret[field] = t
 
         return ret
-    
+
     def construct(self):
         """
         Recursively serialize a lot of types, and
         in cases where it doesn't recognize the type,
         it will fall back to Django's `smart_unicode`.
-        
+
         Returns `dict`.
         """
         def _any(thing, fields=()):
@@ -99,7 +99,7 @@ class Emitter(object):
             Dispatch, all types are routed through here.
             """
             ret = None
-            
+
             if isinstance(thing, QuerySet):
                 ret = _qs(thing, fields=fields)
             elif isinstance(thing, (tuple, list)):
@@ -131,19 +131,19 @@ class Emitter(object):
             Foreign keys.
             """
             return _any(getattr(data, field.name))
-        
+
         def _related(data, fields=()):
             """
             Foreign keys.
             """
             return [ _model(m, fields) for m in data.iterator() ]
-        
+
         def _m2m(data, field, fields=()):
             """
             Many to many (re-route to `_model`.)
             """
             return [ _model(m, fields) for m in getattr(data, field.name).iterator() ]
-        
+
         def _model(data, fields=()):
             """
             Models. Will respect the `fields` and/or
@@ -152,7 +152,7 @@ class Emitter(object):
             ret = { }
             handler = self.in_typemapper(type(data), self.anonymous)
             get_absolute_uri = False
-            
+
             if handler or fields:
                 v = lambda f: getattr(data, f.attname)
 
@@ -167,26 +167,26 @@ class Emitter(object):
 
                     if 'absolute_uri' in get_fields:
                         get_absolute_uri = True
-                
+
                     if not get_fields:
                         get_fields = set([ f.attname.replace("_id", "", 1)
                             for f in data._meta.fields ])
-                
+
                     # sets can be negated.
                     for exclude in exclude_fields:
                         if isinstance(exclude, basestring):
                             get_fields.discard(exclude)
-                            
+
                         elif isinstance(exclude, re._pattern_type):
                             for field in get_fields.copy():
                                 if exclude.match(field):
                                     get_fields.discard(field)
-                                    
+
                 else:
                     get_fields = set(fields)
 
                 met_fields = self.method_fields(handler, get_fields)
-                           
+
                 for f in data._meta.local_fields:
                     if f.serialize and not any([ p in met_fields for p in [ f.attname, f.name ]]):
                         if not f.rel:
@@ -197,13 +197,13 @@ class Emitter(object):
                             if f.attname[:-3] in get_fields:
                                 ret[f.name] = _fk(data, f)
                                 get_fields.remove(f.name)
-                
+
                 for mf in data._meta.many_to_many:
                     if mf.serialize and mf.attname not in met_fields:
                         if mf.attname in get_fields:
                             ret[mf.name] = _m2m(data, mf)
                             get_fields.remove(mf.name)
-                
+
                 # try to get the remainder of fields
                 for maybe_field in get_fields:
                     if isinstance(maybe_field, (list, tuple)):
@@ -225,7 +225,7 @@ class Emitter(object):
                         # using different names.
                         ret[maybe_field] = _any(met_fields[maybe_field](data))
 
-                    else:                    
+                    else:
                         maybe = getattr(data, maybe_field, None)
                         if maybe:
                             if callable(maybe):
@@ -242,13 +242,13 @@ class Emitter(object):
             else:
                 for f in data._meta.fields:
                     ret[f.attname] = _any(getattr(data, f.attname))
-                
+
                 fields = dir(data.__class__) + ret.keys()
                 add_ons = [k for k in dir(data) if k not in fields]
-                
+
                 for k in add_ons:
                     ret[k] = _any(getattr(data, k))
-            
+
             # resouce uri
             if self.in_typemapper(type(data), self.anonymous):
                 handler = self.in_typemapper(type(data), self.anonymous)
@@ -259,51 +259,51 @@ class Emitter(object):
                         ret['resource_uri'] = reverser( lambda: (url_id, fields) )()
                     except NoReverseMatch, e:
                         pass
-            
+
             if hasattr(data, 'get_api_url') and 'resource_uri' not in ret:
                 try: ret['resource_uri'] = data.get_api_url()
                 except: pass
-            
+
             # absolute uri
             if hasattr(data, 'get_absolute_url') and get_absolute_uri:
                 try: ret['absolute_uri'] = data.get_absolute_url()
                 except: pass
-            
+
             return ret
-        
+
         def _qs(data, fields=()):
             """
             Querysets.
             """
             return [ _any(v, fields) for v in data ]
-                
+
         def _list(data):
             """
             Lists.
             """
             return [ _any(v) for v in data ]
-            
+
         def _dict(data):
             """
             Dictionaries.
             """
             return dict([ (k, _any(v)) for k, v in data.iteritems() ])
-            
+
         # Kickstart the seralizin'.
         return _any(self.data, self.fields)
-    
+
     def in_typemapper(self, model, anonymous):
         for klass, (km, is_anon) in self.typemapper.iteritems():
             if model is km and is_anon is anonymous:
                 return klass
-        
+
     def render(self):
         """
         This super emitter does not implement `render`,
         this is a job for the specific emitter below.
         """
         raise NotImplementedError("Please implement render.")
-        
+
     def stream_render(self, request, stream=True):
         """
         Tells our patched middleware not to look
@@ -312,7 +312,7 @@ class Emitter(object):
         more memory friendly for large datasets.
         """
         yield self.render(request)
-        
+
     @classmethod
     def get(cls, format):
         """
@@ -322,19 +322,19 @@ class Emitter(object):
             return cls.EMITTERS.get(format)
 
         raise ValueError("No emitters found for type %s" % format)
-    
+
     @classmethod
     def register(cls, name, klass, content_type='text/plain'):
         """
         Register an emitter.
-        
+
         Parameters::
          - `name`: The name of the emitter ('json', 'xml', 'yaml', ...)
          - `klass`: The emitter class.
          - `content_type`: The content type to serve response as.
         """
         cls.EMITTERS[name] = (klass, content_type)
-        
+
     @classmethod
     def unregister(cls, name):
         """
@@ -342,7 +342,7 @@ class Emitter(object):
         want to provide output in one of the built-in emitters.
         """
         return cls.EMITTERS.pop(name, None)
-    
+
 class XMLEmitter(Emitter):
     def _to_xml(self, xml, data):
         if isinstance(data, (list, tuple)):
@@ -360,16 +360,16 @@ class XMLEmitter(Emitter):
 
     def render(self, request):
         stream = StringIO.StringIO()
-        
+
         xml = SimplerXMLGenerator(stream, "utf-8")
         xml.startDocument()
         xml.startElement("response", {})
-        
+
         self._to_xml(xml, self.construct())
-        
+
         xml.endElement("response")
         xml.endDocument()
-        
+
         return stream.getvalue()
 
 Emitter.register('xml', XMLEmitter, 'text/xml; charset=utf-8')
@@ -388,10 +388,10 @@ class JSONEmitter(Emitter):
             return '%s(%s)' % (cb, seria)
 
         return seria
-    
+
 Emitter.register('json', JSONEmitter, 'application/json; charset=utf-8')
 Mimer.register(simplejson.loads, ('application/json',))
-    
+
 class YAMLEmitter(Emitter):
     """
     YAML emitter, uses `safe_dump` to omit the
@@ -410,7 +410,7 @@ class PickleEmitter(Emitter):
     """
     def render(self, request):
         return pickle.dumps(self.construct())
-        
+
 Emitter.register('pickle', PickleEmitter, 'application/python-pickle')
 
 """
@@ -437,5 +437,5 @@ class DjangoEmitter(Emitter):
             response = serializers.serialize(format, self.data, indent=True)
 
         return response
-        
+
 Emitter.register('django', DjangoEmitter, 'text/xml; charset=utf-8')
index 351df7c..36bd6ff 100644 (file)
@@ -5,7 +5,7 @@ from django.conf import settings
 
 class Form(forms.Form):
     pass
-    
+
 class ModelForm(forms.ModelForm):
     """
     Subclass of `forms.ModelForm` which makes sure
index 2d28bb3..a128d7a 100644 (file)
@@ -19,17 +19,17 @@ class HandlerMetaClass(type):
             for k, (m, a) in typemapper.iteritems():
                 if model == m and anon == a:
                     return k
-        
+
         if hasattr(new_cls, 'model'):
             if already_registered(new_cls.model, new_cls.is_anonymous):
                 if not getattr(settings, 'PISTON_IGNORE_DUPE_MODELS', False):
                     warnings.warn("Handler already registered for model %s, "
                         "you may experience inconsistent results." % new_cls.model.__name__)
-                
+
             typemapper[new_cls] = (new_cls.model, new_cls.is_anonymous)
         else:
             typemapper[new_cls] = (None, new_cls.is_anonymous)
-        
+
         if name not in ('BaseHandler', 'AnonymousBaseHandler'):
             handler_tracker.append(new_cls)
 
@@ -40,43 +40,43 @@ class BaseHandler(object):
     Basehandler that gives you CRUD for free.
     You are supposed to subclass this for specific
     functionality.
-    
+
     All CRUD methods (`read`/`update`/`create`/`delete`)
     receive a request as the first argument from the
     resource. Use this for checking `request.user`, etc.
     """
     __metaclass__ = HandlerMetaClass
-    
+
     allowed_methods = ('GET', 'POST', 'PUT', 'DELETE')
     anonymous = is_anonymous = False
     exclude = ( 'id', )
     fields =  ( )
-    
+
     def flatten_dict(self, dct):
         return dict([ (str(k), dct.get(k)) for k in dct.keys() ])
-    
+
     def has_model(self):
         return hasattr(self, 'model') or hasattr(self, 'queryset')
 
     def queryset(self, request):
         return self.model.objects.all()
-    
+
     def value_from_tuple(tu, name):
         for int_, n in tu:
             if n == name:
                 return int_
         return None
-    
+
     def exists(self, **kwargs):
         if not self.has_model():
             raise NotImplementedError
-        
+
         try:
             self.model.objects.get(**kwargs)
             return True
         except self.model.DoesNotExist:
             return False
-    
+
     def read(self, request, *args, **kwargs):
         if not self.has_model():
             return rc.NOT_IMPLEMENTED
@@ -92,13 +92,13 @@ class BaseHandler(object):
                 return rc.BAD_REQUEST
         else:
             return self.queryset(request).filter(*args, **kwargs)
-    
+
     def create(self, request, *args, **kwargs):
         if not self.has_model():
             return rc.NOT_IMPLEMENTED
-        
+
         attrs = self.flatten_dict(request.POST)
-        
+
         try:
             inst = self.queryset(request).get(**attrs)
             return rc.DUPLICATE_ENTRY
@@ -108,7 +108,7 @@ class BaseHandler(object):
             return inst
         except self.model.MultipleObjectsReturned:
             return rc.DUPLICATE_ENTRY
-    
+
     def update(self, request, *args, **kwargs):
         if not self.has_model():
             return rc.NOT_IMPLEMENTED
@@ -132,7 +132,7 @@ class BaseHandler(object):
 
         inst.save()
         return rc.ALL_OK
-    
+
     def delete(self, request, *args, **kwargs):
         if not self.has_model():
             raise NotImplementedError
@@ -147,7 +147,7 @@ class BaseHandler(object):
             return rc.DUPLICATE_ENTRY
         except self.model.DoesNotExist:
             return rc.NOT_HERE
-        
+
 class AnonymousBaseHandler(BaseHandler):
     """
     Anonymous handler.
index 5694d0e..a1c9d49 100644 (file)
@@ -48,15 +48,15 @@ class ResourceManager(models.Manager):
         if not self._default_resource:
             self._default_resource = self.get(name=name)
 
-        return self._default_resource        
+        return self._default_resource
 
 class TokenManager(KeyManager):
     def create_token(self, consumer, token_type, timestamp, user=None):
         """
         Shortcut to create a token with random key/secret.
         """
-        token, created = self.get_or_create(consumer=consumer, 
-                                            token_type=token_type, 
+        token, created = self.get_or_create(consumer=consumer,
+                                            token_type=token_type,
                                             timestamp=timestamp,
                                             user=user)
 
@@ -65,4 +65,4 @@ class TokenManager(KeyManager):
             token.save()
 
         return token
-        
+
index 9c93af8..d205930 100644 (file)
@@ -29,7 +29,7 @@ class Nonce(models.Model):
     token_key = models.CharField(max_length=KEY_SIZE)
     consumer_key = models.CharField(max_length=KEY_SIZE)
     key = models.CharField(max_length=255)
-    
+
     def __unicode__(self):
         return u"Nonce %s for %s" % (self.key, self.consumer_key)
 
@@ -46,17 +46,17 @@ class Consumer(models.Model):
     user = models.ForeignKey(User, null=True, blank=True, related_name='consumers')
 
     objects = ConsumerManager()
-        
+
     def __unicode__(self):
         return u"Consumer %s with key %s" % (self.name, self.key)
 
     def generate_random_codes(self):
         """
         Used to generate random key/secret pairings. Use this after you've
-        added the other data in place of save(). 
+        added the other data in place of save().
 
         c = Consumer()
-        c.name = "My consumer" 
+        c.name = "My consumer"
         c.description = "An app that makes ponies from the API."
         c.user = some_user_object
         c.generate_random_codes()
@@ -77,28 +77,28 @@ class Token(models.Model):
     REQUEST = 1
     ACCESS = 2
     TOKEN_TYPES = ((REQUEST, u'Request'), (ACCESS, u'Access'))
-    
+
     key = models.CharField(max_length=KEY_SIZE)
     secret = models.CharField(max_length=SECRET_SIZE)
     verifier = models.CharField(max_length=VERIFIER_SIZE)
     token_type = models.IntegerField(choices=TOKEN_TYPES)
     timestamp = models.IntegerField(default=long(time.time()))
     is_approved = models.BooleanField(default=False)
-    
+
     user = models.ForeignKey(User, null=True, blank=True, related_name='tokens')
     consumer = models.ForeignKey(Consumer)
-    
+
     callback = models.CharField(max_length=255, null=True, blank=True)
     callback_confirmed = models.BooleanField(default=False)
-    
+
     objects = TokenManager()
-    
+
     def __unicode__(self):
         return u"%s Token %s for %s" % (self.get_token_type_display(), self.key, self.consumer)
 
     def to_string(self, only_key=False):
         token_dict = {
-            'oauth_token': self.key, 
+            'oauth_token': self.key,
             'oauth_token_secret': self.secret,
             'oauth_callback_confirmed': 'true',
         }
@@ -121,7 +121,7 @@ class Token(models.Model):
         self.key = key
         self.secret = secret
         self.save()
-        
+
     # -- OAuth 1.0a stuff
 
     def get_callback_url(self):
@@ -136,13 +136,13 @@ class Token(models.Model):
             return urlparse.urlunparse((scheme, netloc, path, params,
                 query, fragment))
         return self.callback
-    
+
     def set_callback(self, callback):
         if callback != "oob": # out of band, says "we can't do this!"
             self.callback = callback
             self.callback_confirmed = True
             self.save()
-        
+
 admin.site.register(Token)
 
 # Attach our signals
index 3a42e20..8c430ea 100644 (file)
@@ -87,7 +87,7 @@ class OAuthConsumer(object):
 class OAuthToken(object):
     """OAuthToken is a data type that represents an End User via either an access
     or request token.
-    
+
     key -- the token
     secret -- the token secret
 
@@ -133,7 +133,7 @@ class OAuthToken(object):
         if self.callback_confirmed is not None:
             data['oauth_callback_confirmed'] = self.callback_confirmed
         return urllib.urlencode(data)
+
     def from_string(s):
         """ Returns a token from something like:
         oauth_token_secret=xxx&oauth_token=xxx
@@ -157,11 +157,11 @@ class OAuthRequest(object):
     """OAuthRequest represents the request and can be serialized.
 
     OAuth parameters:
-        - oauth_consumer_key 
+        - oauth_consumer_key
         - oauth_token
         - oauth_signature_method
-        - oauth_signature 
-        - oauth_timestamp 
+        - oauth_signature
+        - oauth_timestamp
         - oauth_nonce
         - oauth_version
         - oauth_verifier
@@ -436,7 +436,7 @@ class OAuthServer(object):
     def get_callback(self, oauth_request):
         """Get the callback URL."""
         return oauth_request.get_parameter('oauth_callback')
+
     def build_authenticate_header(self, realm=''):
         """Optional support for the authenticate header."""
         return {'WWW-Authenticate': 'OAuth realm="%s"' % realm}
@@ -482,7 +482,7 @@ class OAuthServer(object):
         if not token:
             raise OAuthError('Invalid %s token: %s' % (token_type, token_field))
         return token
-    
+
     def _get_verifier(self, oauth_request):
         return oauth_request.get_parameter('oauth_verifier')
 
@@ -601,7 +601,7 @@ class OAuthSignatureMethod_HMAC_SHA1(OAuthSignatureMethod):
 
     def get_name(self):
         return 'HMAC-SHA1'
-        
+
     def build_signature_base_string(self, oauth_request, consumer, token):
         sig = (
             escape(oauth_request.get_normalized_http_method()),
index 40f065d..ee78b0e 100644 (file)
@@ -26,22 +26,22 @@ class Resource(object):
     is an authentication handler. If not specified,
     `NoAuthentication` will be used by default.
     """
-    callmap = { 'GET': 'read', 'POST': 'create', 
+    callmap = { 'GET': 'read', 'POST': 'create',
                 'PUT': 'update', 'DELETE': 'delete' }
-    
+
     def __init__(self, handler, authentication=None):
         if not callable(handler):
             raise AttributeError, "Handler not callable."
-        
+
         self.handler = handler()
-        
+
         if not authentication:
             self.authentication = (NoAuthentication(),)
         elif isinstance(authentication, (list, tuple)):
             self.authentication = authentication
         else:
             self.authentication = (authentication,)
-            
+
         # Erroring
         self.email_errors = getattr(settings, 'PISTON_EMAIL_ERRORS', True)
         self.display_errors = getattr(settings, 'PISTON_DISPLAY_ERRORS', True)
@@ -58,12 +58,12 @@ class Resource(object):
         that as well.
         """
         em = kwargs.pop('emitter_format', None)
-        
+
         if not em:
             em = request.GET.get('format', 'json')
 
         return em
-    
+
     @property
     def anonymous(self):
         """
@@ -74,16 +74,16 @@ class Resource(object):
         """
         if hasattr(self.handler, 'anonymous'):
             anon = self.handler.anonymous
-            
+
             if callable(anon):
                 return anon
 
             for klass in typemapper.keys():
                 if anon == klass.__name__:
                     return klass
-            
+
         return None
-    
+
     def authenticate(self, request, rm):
         actor, anonymous = False, True
 
@@ -97,9 +97,9 @@ class Resource(object):
                     actor, anonymous = authenticator.challenge, CHALLENGE
             else:
                 return self.handler, self.handler.is_anonymous
-        
+
         return actor, anonymous
-    
+
     @vary_on_headers('Authorization')
     def __call__(self, request, *args, **kwargs):
         """
@@ -119,19 +119,19 @@ class Resource(object):
             return actor()
         else:
             handler = actor
-        
+
         # Translate nested datastructs into `request.data` here.
         if rm in ('POST', 'PUT'):
             try:
                 translate_mime(request)
             except MimerDataException:
                 return rc.BAD_REQUEST
-        
+
         if not rm in handler.allowed_methods:
             return HttpResponseNotAllowed(handler.allowed_methods)
-        
+
         meth = getattr(handler, self.callmap.get(rm), None)
-        
+
         if not meth:
             raise Http404
 
@@ -139,18 +139,18 @@ class Resource(object):
         em_format = self.determine_emitter(request, *args, **kwargs)
 
         kwargs.pop('emitter_format', None)
-        
+
         # Clean up the request object a bit, since we might
         # very well have `oauth_`-headers in there, and we
         # don't want to pass these along to the handler.
         request = self.cleanup_request(request)
-        
+
         try:
             result = meth(request, *args, **kwargs)
         except FormValidationError, e:
             resp = rc.BAD_REQUEST
             resp.write(' '+str(e.form.errors))
-            
+
             return resp
         except TypeError, e:
             result = rc.BAD_REQUEST
@@ -158,15 +158,15 @@ class Resource(object):
             sig = hm.signature
 
             msg = 'Method signature does not match.\n\n'
-            
+
             if sig:
                 msg += 'Signature should be: %s' % sig
             else:
                 msg += 'Resource does not expect any parameters.'
 
-            if self.display_errors:                
+            if self.display_errors:
                 msg += '\n\nException was: %s' % str(e)
-                
+
             result.content = format_error(msg)
         except Http404:
             return rc.NOT_FOUND
@@ -177,13 +177,13 @@ class Resource(object):
             On errors (like code errors), we'd like to be able to
             give crash reports to both admins and also the calling
             user. There's two setting parameters for this:
-            
+
             Parameters::
              - `PISTON_EMAIL_ERRORS`: Will send a Django formatted
                error email to people in `settings.ADMINS`.
              - `PISTON_DISPLAY_ERRORS`: Will return a simple traceback
                to the caller, so he can tell you what error they got.
-               
+
             If `PISTON_DISPLAY_ERRORS` is not enabled, the caller will
             receive a basic "500 Internal Server Error" message.
             """
@@ -237,17 +237,17 @@ class Resource(object):
 
             if True in [ k.startswith("oauth_") for k in block.keys() ]:
                 sanitized = block.copy()
-                
+
                 for k in sanitized.keys():
                     if k.startswith("oauth_"):
                         sanitized.pop(k)
-                        
+
                 setattr(request, method_type, sanitized)
 
         return request
-        
-    # -- 
-    
+
+    # --
+
     def email_exception(self, reporter):
         subject = "Piston crash report"
         html = reporter.get_traceback_html()
@@ -255,6 +255,6 @@ class Resource(object):
         message = EmailMessage(settings.EMAIL_SUBJECT_PREFIX+subject,
                                 html, settings.SERVER_EMAIL,
                                 [ admin[1] for admin in settings.ADMINS ])
-        
+
         message.content_subtype = 'html'
         message.send(fail_silently=True)
index 133be13..a302d1b 100644 (file)
@@ -1,5 +1,5 @@
 # Django imports
-import django.dispatch 
+import django.dispatch
 
 # Piston imports
 from utils import send_consumer_mail
index 787791a..13377d6 100644 (file)
@@ -23,7 +23,7 @@ class DataStore(oauth.OAuthDataStore):
         elif token_type == 'access':
             token_type = Token.ACCESS
         try:
-            self.request_token = Token.objects.get(key=token, 
+            self.request_token = Token.objects.get(key=token,
                                                    token_type=token_type)
             return self.request_token
         except Token.DoesNotExist:
@@ -32,7 +32,7 @@ class DataStore(oauth.OAuthDataStore):
     def lookup_nonce(self, oauth_consumer, oauth_token, nonce):
         if oauth_token is None:
             return None
-        nonce, created = Nonce.objects.get_or_create(consumer_key=oauth_consumer.key, 
+        nonce, created = Nonce.objects.get_or_create(consumer_key=oauth_consumer.key,
                                                      token_key=oauth_token.key,
                                                      key=nonce)
         if created:
@@ -45,10 +45,10 @@ class DataStore(oauth.OAuthDataStore):
             self.request_token = Token.objects.create_token(consumer=self.consumer,
                                                             token_type=Token.REQUEST,
                                                             timestamp=self.timestamp)
-            
+
             if oauth_callback:
                 self.request_token.set_callback(oauth_callback)
-            
+
             return self.request_token
         return None
 
index d7b1830..8fdfb8f 100644 (file)
        </head>
        <body>
                <h1>API Documentation</h1>
-               
+
                {% for doc in docs %}
-               
+
                        <h3>{{ doc.name|cut:"Handler" }}:</h3>
 
                        <p>
                                {{ doc.get_doc|default:""|restructuredtext }}
                        </p>
-                       
+
                        <p>
                                URL: <b>{{ doc.get_resource_uri_template }}</b>
                        </p>
-                       
+
                        <p>
                                Accepted methods: {% for meth in doc.allowed_methods %}<b>{{ meth }}</b>{% if not forloop.last %}, {% endif %}{% endfor %}
                        </p>
-                                       
+
                        <dl>
                                {% for method in doc.get_all_methods %}
-                               
+
                                        <dt>
                                                method <i>{{ method.name }}</i>({{ method.signature }}){% if method.stale %} <i>- inherited</i>{% else %}:{% endif %}
-                                               
-                                       </dt>                           
-                                                                               
+
+                                       </dt>
+
                                        {% if method.get_doc %}
                                                <dd>
                                                        {{ method.get_doc|default:""|restructuredtext }}
                                                <dd>
                                        {% endif %}
-                               
+
                                {% endfor %}
                        </dl>
-               
+
                {% endfor %}
        </body>
 </html>
index dae840e..59c09e3 100644 (file)
@@ -6,7 +6,7 @@
        </head>
        <body>
                <h1>Authorize Token</h1>
-               
+
         <form action="{% url piston.authentication.oauth_user_auth %}" method="POST">
             {{ form.as_table }}
         </form>
index 57eda72..f19fb21 100644 (file)
@@ -32,8 +32,8 @@ class OAuthClient(client.Client):
         url = "http://testserver" + request['PATH_INFO']
 
         req = oauth.OAuthRequest.from_consumer_and_token(
-            self.consumer, token=self.token, 
-            http_method=request['REQUEST_METHOD'], http_url=url, 
+            self.consumer, token=self.token,
+            http_method=request['REQUEST_METHOD'], http_url=url,
             parameters=params
         )
 
@@ -49,7 +49,7 @@ class OAuthClient(client.Client):
 
         if isinstance(data, dict):
             data = urlencode(data)
-        
+
         return super(OAuthClient, self).post(path, data, content_type, follow, **extra)
 
 class TestCase(test.TestCase):
index 92d14ee..99f7028 100644 (file)
@@ -19,7 +19,7 @@ class ConsumerTest(TestCase):
 
     def test_create_pending(self):
         """ Ensure creating a pending Consumer sends proper emails """
-        # If it's pending we should have two messages in the outbox; one 
+        # If it's pending we should have two messages in the outbox; one
         # to the consumer and one to the site admins.
         if len(settings.ADMINS):
             self.assertEquals(len(mail.outbox), 2)
@@ -36,8 +36,8 @@ class ConsumerTest(TestCase):
         mail.outbox = []
 
         # Delete the consumer, which should fire off the cancel email.
-        self.consumer.delete() 
-        
+        self.consumer.delete()
+
         self.assertEquals(len(mail.outbox), 1)
         expected = "Your API Consumer for example.com has been canceled."
         self.assertEquals(mail.outbox[0].subject, expected)
index 8d0cce8..9128cbc 100644 (file)
@@ -39,7 +39,7 @@ class rc_factory(object):
 
     def __getattr__(self, attr):
         """
-        Returns a fresh `HttpResponse` when getting 
+        Returns a fresh `HttpResponse` when getting
         an "attribute". This is backwards compatible
         with 0.2, which is important.
         """
@@ -49,9 +49,9 @@ class rc_factory(object):
             raise AttributeError(attr)
 
         return HttpResponse(r, content_type='text/plain', status=c)
-    
+
 rc = rc_factory()
-    
+
 class FormValidationError(Exception):
     def __init__(self, form):
         self.form = form
@@ -64,7 +64,7 @@ def validate(v_form, operation='POST'):
     @decorator
     def wrap(f, self, request, *a, **kwa):
         form = v_form(getattr(request, operation))
-    
+
         if form.is_valid():
             return f(self, request, *a, **kwa)
         else:
@@ -75,11 +75,11 @@ def throttle(max_requests, timeout=60*60, extra=''):
     """
     Simple throttling decorator, caches
     the amount of requests made in cache.
-    
+
     If used on a view where users are required to
     log in, the username is used, otherwise the
     IP address of the originating request is used.
-    
+
     Parameters::
      - `max_requests`: The maximum number of requests
      - `timeout`: The timeout for the cache entry (default: 1 hour)
@@ -90,7 +90,7 @@ def throttle(max_requests, timeout=60*60, extra=''):
             ident = request.user.username
         else:
             ident = request.META.get('REMOTE_ADDR', None)
-    
+
         if hasattr(request, 'throttle_extra'):
             """
             Since we want to be able to throttle on a per-
@@ -99,7 +99,7 @@ def throttle(max_requests, timeout=60*60, extra=''):
             object. If so, append the identifier name with it.
             """
             ident += ':%s' % str(request.throttle_extra)
-        
+
         if ident:
             """
             Preferrably we'd use incr/decr here, since they're
@@ -108,7 +108,7 @@ def throttle(max_requests, timeout=60*60, extra=''):
             stable, you can change it here.
             """
             ident += ':%s' % extra
-    
+
             now = time.time()
             count, expiration = cache.get(ident, (1, None))
 
@@ -123,7 +123,7 @@ def throttle(max_requests, timeout=60*60, extra=''):
                 return t
 
             cache.set(ident, (count+1, expiration), (expiration - now))
-    
+
         return f(self, request, *args, **kwargs)
     return wrap
 
@@ -133,7 +133,7 @@ def coerce_put_post(request):
     In case we send data over PUT, Django won't
     actually look at the data and load it. We need
     to twist its arm here.
-    
+
     The try/except abominiation here is due to a bug
     in mod_python. This should fix it.
     """
@@ -146,7 +146,7 @@ def coerce_put_post(request):
             request.META['REQUEST_METHOD'] = 'POST'
             request._load_post_and_files()
             request.META['REQUEST_METHOD'] = 'PUT'
-            
+
         request.PUT = request.POST
 
 
@@ -158,10 +158,10 @@ class MimerDataException(Exception):
 
 class Mimer(object):
     TYPES = dict()
-    
+
     def __init__(self, request):
         self.request = request
-        
+
     def is_multipart(self):
         content_type = self.content_type()
 
@@ -179,7 +179,7 @@ class Mimer(object):
             for mime in mimes:
                 if ctype.startswith(mime):
                     return loadee
-                    
+
     def content_type(self):
         """
         Returns the content type of the request in all cases where it is
@@ -188,10 +188,10 @@ class Mimer(object):
         type_formencoded = "application/x-www-form-urlencoded"
 
         ctype = self.request.META.get('CONTENT_TYPE', type_formencoded)
-        
+
         if type_formencoded in ctype:
             return None
-        
+
         return ctype
 
     def translate(self):
@@ -202,21 +202,21 @@ class Mimer(object):
         key-value (and maybe just a list), the data will be placed on
         `request.data` instead, and the handler will have to read from
         there.
-        
+
         It will also set `request.content_type` so the handler has an easy
         way to tell what's going on. `request.content_type` will always be
         None for form-encoded and/or multipart form data (what your browser sends.)
-        """    
+        """
         ctype = self.content_type()
         self.request.content_type = ctype
-        
+
         if not self.is_multipart() and ctype:
             loadee = self.loader_for_type(ctype)
-            
+
             if loadee:
                 try:
                     self.request.data = loadee(self.request.raw_post_data)
-                        
+
                     # Reset both POST and PUT from request, as its
                     # misleading having their presence around.
                     self.request.POST = self.request.PUT = dict()
@@ -227,18 +227,18 @@ class Mimer(object):
                 self.request.data = None
 
         return self.request
-                
+
     @classmethod
     def register(cls, loadee, types):
         cls.TYPES[loadee] = types
-        
+
     @classmethod
     def unregister(cls, loadee):
         return cls.TYPES.pop(loadee)
 
 def translate_mime(request):
     request = Mimer(request).translate()
-    
+
 def require_mime(*mimes):
     """
     Decorator requiring a certain mimetype. There's a nifty
@@ -265,7 +265,7 @@ def require_mime(*mimes):
     return wrap
 
 require_extended = require_mime('json', 'yaml', 'xml', 'pickle')
-    
+
 def send_consumer_mail(consumer):
     """
     Send a consumer an email depending on what their status is.
@@ -280,20 +280,20 @@ def send_consumer_mail(consumer):
             subject += "has been canceled."
         elif consumer.status == "rejected":
             subject += "has been rejected."
-        else: 
+        else:
             subject += "is awaiting approval."
 
-    template = "piston/mails/consumer_%s.txt" % consumer.status    
-    
+    template = "piston/mails/consumer_%s.txt" % consumer.status
+
     try:
-        body = loader.render_to_string(template, 
+        body = loader.render_to_string(template,
             { 'consumer' : consumer, 'user' : consumer.user })
     except TemplateDoesNotExist:
-        """ 
+        """
         They haven't set up the templates, which means they might not want
         these emails sent.
         """
-        return 
+        return
 
     try:
         sender = settings.PISTON_FROM_EMAIL
index f8e2801..897c221 100644 (file)
@@ -19,7 +19,7 @@ class SponsorAdmin(admin.ModelAdmin):
 class SponsorPageAdmin(admin.ModelAdmin):
     formfield_overrides = {
         fields.JSONField: {'widget': widgets.SponsorPageWidget},
-    }   
+    }
     list_display = ('name',)
     search_fields = ('name',)
     ordering = ('name',)
index b3fa361..c2098ec 100644 (file)
@@ -32,7 +32,7 @@ def loads(str):
 
 class JSONFormField(forms.CharField):
     widget = forms.Textarea
-    
+
     def clean(self, value):
         try:
             loads(value)
@@ -55,7 +55,7 @@ class JSONField(models.TextField):
 
     def contribute_to_class(self, cls, name):
         super(JSONField, self).contribute_to_class(cls, name)
-        
+
         def get_value(model_instance):
             return loads(getattr(model_instance, self.attname, None))
         setattr(cls, 'get_%s_value' % self.name, get_value)
index 5721bcc..daccec5 100644 (file)
@@ -5,9 +5,9 @@ from south.v2 import SchemaMigration
 from django.db import models
 
 class Migration(SchemaMigration):
-    
+
     def forwards(self, orm):
-        
+
         # Adding model 'Sponsor'
         db.create_table('sponsors_sponsor', (
             ('url', self.gf('django.db.models.fields.URLField')(max_length=200, blank=True)),
@@ -26,17 +26,17 @@ class Migration(SchemaMigration):
             ('name', self.gf('django.db.models.fields.CharField')(max_length=120)),
         ))
         db.send_create_signal('sponsors', ['SponsorPage'])
-    
-    
+
+
     def backwards(self, orm):
-        
+
         # Deleting model 'Sponsor'
         db.delete_table('sponsors_sponsor')
 
         # Deleting model 'SponsorPage'
         db.delete_table('sponsors_sponsorpage')
-    
-    
+
+
     models = {
         'sponsors.sponsor': {
             'Meta': {'object_name': 'Sponsor'},
@@ -54,5 +54,5 @@ class Migration(SchemaMigration):
             'sponsors': ('sponsors.fields.JSONField', [], {'default': '{}'})
         }
     }
-    
+
     complete_apps = ['sponsors']
index 28ef76e..3c2ca02 100644 (file)
@@ -22,7 +22,7 @@ class Sponsor(models.Model):
             'options': ['pad', 'detail'],
         })
     url = models.URLField(_('url'), blank=True, verify_exists=False)
-    
+
     def __unicode__(self):
         return self.name
 
@@ -37,7 +37,7 @@ class SponsorPage(models.Model):
     name = models.CharField(_('name'), max_length=120)
     sponsors = JSONField(_('sponsors'), default={})
     _html = models.TextField(blank=True, editable=False)
-    
+
     def populated_sponsors(self):
         result = []
         for column in self.get_sponsors_value():
@@ -50,7 +50,7 @@ class SponsorPage(models.Model):
                     pass
             result.append(result_group)
         return result
-    
+
     def html(self):
         return self._html
     html = property(fget=html)
@@ -60,7 +60,7 @@ class SponsorPage(models.Model):
             'sponsors': self.populated_sponsors(),
         })
         return super(SponsorPage, self).save(*args, **kwargs)
-    
+
     def __unicode__(self):
         return self.name
 
index 4cb5eb6..33c794a 100644 (file)
@@ -4,12 +4,12 @@
       sponsors: []
     };
     $.extend(settings, options);
-    
+
     var input = $(this).hide();
-    
+
     var container = $('<div class="sponsors"></div>').appendTo(input.parent());
     var groups = $.evalJSON(input.val());
-    
+
     var unusedDiv = $('<div class="sponsors-sponsor-group sponsors-unused-sponsor-group"></div>')
       .appendTo(container)
       .append('<p class="sponsors-sponsor-group-name sponsors-unused-sponsor-group-name">dostępni sponsorzy</p>');
         .sortable({
           connectWith: '.sponsors-sponsor-group-list'
                });
-    
+
     // Edit group name inline
     function editNameInline(name) {
       name.unbind('click.sponsorsFooter');
       var inlineInput = $('<input></input>').val(name.html());
       name.html('');
-      
+
       function endEditing() {
         name.html(inlineInput.val());
         inlineInput.remove();
         input.parents('form').unbind('submit.sponsorsFooter', endEditing);
         return false;
       }
-      
+
       inlineInput.appendTo(name).focus().blur(endEditing);
       input.parents('form').bind('submit.sponsorsFooter', endEditing);
     }
-    
+
     // Remove sponsor with passed id from sponsors array and return it
     function popSponsor(id) {
       for (var i=0; i < settings.sponsors.length; i++) {
       }
       return null;
     }
-    
+
     // Create sponsor group and bind events
     function createGroup(name, sponsors) {
       if (!sponsors) {
         sponsors = [];
       }
-      
+
       var groupDiv = $('<div class="sponsors-sponsor-group"></div>');
-      
+
       $('<a class="sponsors-remove-sponsor-group">X</a>')
         .click(function() {
           groupDiv.fadeOut('slow', function() {
             groupDiv.remove();
           });
         }).appendTo(groupDiv);
-      
+
       $('<p class="sponsors-sponsor-group-name">' + name + '</p>')
         .bind('click.sponsorsFooter', function() {
           editNameInline($(this));
         }).appendTo(groupDiv);
-      
+
       var groupList = $('<ol class="sponsors-sponsor-group-list"></ol>')
         .appendTo(groupDiv)
         .sortable({
           connectWith: '.sponsors-sponsor-group-list'
                });
-      
-      
+
+
       for (var i = 0; i < sponsors.length; i++) {
         $('<li class="sponsors-sponsor"><img src="' + sponsors[i].image + '" alt="' + sponsors[i].name + '"/></li>')
           .data('obj_id', sponsors[i].id)
       }
       return groupDiv;
     }
-    
+
     // Create groups from data in input value
     for (var i = 0; i < groups.length; i++) {
       var group = groups[i];
       var sponsors = [];
-      
+
       for (var j = 0; j < group.sponsors.length; j++) {
         var s = popSponsor(group.sponsors[j]);
         if (s) {
       }
       createGroup(group.name, sponsors).appendTo(container);
     }
-    
+
     // Serialize input value before submiting form
     input.parents('form').submit(function(event) {
       var groups = [];
       });
       input.val($.toJSON(groups));
     });
-    
+
     for (i = 0; i < settings.sponsors.length; i++) {
       $('<li class="sponsors-sponsor"><img src="' + settings.sponsors[i].image + '" alt="' + settings.sponsors[i].name + '"/></li>')
         .data('obj_id', settings.sponsors[i].id)
         .appendTo(unusedList);
     }
-    
+
     $('<button type="button">Dodaj nową grupę</button>')
       .click(function() {
         var newGroup = createGroup('').appendTo(container);
         editNameInline($('.sponsors-sponsor-group-name', newGroup));
       }).prependTo(input.parent());
-    
+
     input.parent().append('<div style="clear: both"></div>');
   };
 })(jQuery);
index fb8e6b3..3670123 100644 (file)
@@ -17,5 +17,5 @@ def sponsor_page(name):
     except:
         return u''
     return mark_safe(page.html)
-    
+
 sponsor_page = register.simple_tag(sponsor_page)
index 70ddde6..e4b30bb 100644 (file)
@@ -28,6 +28,6 @@ class SponsorPageWidget(forms.Textarea):
         output.append(u'<script type="text/javascript">addEvent(window, "load", function(e) {')
         # TODO: "id_" is hard-coded here. This should instead use the correct
         # API to determine the ID dynamically.
-        output.append(u'$("#id_%s").sponsorsFooter({sponsors: [%s]}); });</script>\n' % 
+        output.append(u'$("#id_%s").sponsorsFooter({sponsors: [%s]}); });</script>\n' %
             (name, sponsors_js))
         return mark_safe(u''.join(output))
index 4c4a118..a25297a 100644 (file)
@@ -5,9 +5,9 @@ from south.v2 import SchemaMigration
 from django.db import models
 
 class Migration(SchemaMigration):
-    
+
     def forwards(self, orm):
-        
+
         # Adding model 'Suggestion'
         db.create_table('suggest_suggestion', (
             ('description', self.gf('django.db.models.fields.TextField')(blank=True)),
@@ -18,14 +18,14 @@ class Migration(SchemaMigration):
             ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
         ))
         db.send_create_signal('suggest', ['Suggestion'])
-    
-    
+
+
     def backwards(self, orm):
-        
+
         # Deleting model 'Suggestion'
         db.delete_table('suggest_suggestion')
-    
-    
+
+
     models = {
         'auth.group': {
             'Meta': {'object_name': 'Group'},
@@ -73,5 +73,5 @@ class Migration(SchemaMigration):
             'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'})
         }
     }
-    
+
     complete_apps = ['suggest']
index f303872..e24afae 100644 (file)
@@ -12,12 +12,11 @@ class Suggestion(models.Model):
     created_at = models.DateTimeField(_('creation date'), auto_now=True)
     ip = models.IPAddressField(_('IP address'))
     user = models.ForeignKey(User, blank=True, null=True)
-    
+
     class Meta:
         ordering = ('-created_at',)
         verbose_name = _('suggestion')
         verbose_name_plural = _('suggestions')
-    
+
     def __unicode__(self):
         return unicode(self.created_at)
-    
\ No newline at end of file
index 44d606e..d5982cf 100644 (file)
@@ -7,7 +7,7 @@ from django.views.generic.simple import direct_to_template
 from suggest.forms import SuggestForm
 
 urlpatterns = patterns('',
-    url(r'^$', 'django.views.generic.simple.direct_to_template', 
+    url(r'^$', 'django.views.generic.simple.direct_to_template',
         {'template': 'suggest.html', 'extra_context': {'form': SuggestForm }}, name='suggest'),
     url(r'^wyslij/$', 'suggest.views.report', name='report'),
 )
index 4ad1704..5d007f6 100644 (file)
@@ -11,7 +11,7 @@ from suggest import forms
 from suggest.models import Suggestion
 
 # FIXME - shouldn't be in catalogue
-from catalogue.views import LazyEncoder 
+from catalogue.views import LazyEncoder
 
 
 #@require_POST
index 1305177..1995fd3 100644 (file)
@@ -21,7 +21,7 @@ def staging():
     env.python = '/usr/bin/python'
     env.virtualenv = '/usr/bin/virtualenv'
     env.pip = '/usr/bin/pip'
-    
+
 def production():
     """Use production server"""
     env.hosts = ['wolnelektury.pl:22123']
@@ -54,8 +54,8 @@ def setup():
 
 def deploy():
     """
-    Deploy the latest version of the site to the servers, 
-    install any required third party modules, 
+    Deploy the latest version of the site to the servers,
+    install any required third party modules,
     install the virtual host and then restart the webserver
     """
     require('hosts', 'path', provided_by=[staging, production])
diff --git a/lib/librarian b/lib/librarian
new file mode 160000 (submodule)
index 0000000..d43d874
--- /dev/null
@@ -0,0 +1 @@
+Subproject commit d43d87400dcc19851442a42ff20af9fc0b549087
index 2ecf4cf..0e273f2 100644 (file)
@@ -7,27 +7,27 @@ import xml.sax
 
 class simpleHandler(xml.sax.ContentHandler):
     """A simple handler that provides us with indices of marked up content."""
-    def __init__(self):        
+    def __init__(self):
         self.elements = [] #this will contain a list of elements and their start/end indices
         self.open_elements = [] #this holds info on open elements while we wait for their close
         self.content = ""
 
-    def startElement(self,name,attrs):
-        if name=='foobar': return # we require an outer wrapper, which we promptly ignore.
+    def startElement(self, name, attrs):
+        if name == 'foobar': return # we require an outer wrapper, which we promptly ignore.
         self.open_elements.append({'name':name,
                                    'attrs':attrs.copy(),
                                    'start':len(self.content),
                                    })
 
     def endElement(self, name):
-        if name=='foobar': return # we require an outer wrapper, which we promptly ignore.
+        if name == 'foobar': return # we require an outer wrapper, which we promptly ignore.
         for i in range(len(self.open_elements)):
             e = self.open_elements[i]
-            if e['name']==name:
+            if e['name'] == name:
                 # append a  (start,end), name, attrs
                 self.elements.append(((e['start'], #start position
-                                       len(self.content)),# current (end) position
-                                      e['name'],e['attrs'])
+                                       len(self.content)), # current (end) position
+                                      e['name'], e['attrs'])
                                      )
                 del self.open_elements[i]
                 return
@@ -39,14 +39,14 @@ class simpleHandler(xml.sax.ContentHandler):
 class MarkupString(unicode):
     """A simple class for dealing with marked up strings. When we are sliced, we return
     valid marked up strings, preserving markup."""
-    def __init__(self, string):        
-        unicode.__init__(self, string)
+    def __init__(self, string):
+        unicode.__init__(self)
         self.handler = simpleHandler()
         xml.sax.parseString((u"<foobar>%s</foobar>" % string).encode('utf-8'), self.handler)
         self.raw = self.handler.content
 
     def __getitem__(self, n):
-        return self.__getslice__(n,n+1)
+        return self.__getslice__(n, n + 1)
 
     def __getslice__(self, s, e):
         # only include relevant elements
@@ -64,21 +64,21 @@ class MarkupString(unicode):
             name = el[1]
             attrs = el[2]
             # write our start tag <stag att="val"...>
-            stag = "<%s"%name
-            for k,v in attrs.items(): stag += " %s=%s"%(k,xml.sax.saxutils.quoteattr(v))
+            stag = "<%s" % name
+            for k, v in attrs.items(): stag += " %s=%s" % (k, xml.sax.saxutils.quoteattr(v))
             stag += ">"
-            etag = "</%s>"%name # simple end tag
+            etag = "</%s>" % name # simple end tag
             spos = pos[0]
             epos = pos[1]
-            if spos < s: spos=s
-            if epos > e: epos=e
+            if spos < s: spos = s
+            if epos > e: epos = e
             if epos != spos: # we don't care about tags that don't markup any text
-                if not starts.has_key(spos): starts[spos]=[]
+                if not starts.has_key(spos): starts[spos] = []
                 starts[spos].append(stag)
-                if not ends.has_key(epos): ends[epos]=[]
+                if not ends.has_key(epos): ends[epos] = []
                 ends[epos].append(etag)
         outbuf = "" # our actual output string
-        for pos in range(s,e): # we move through positions
+        for pos in range(s, e): # we move through positions
             char = self.raw[pos]
             if ends.has_key(pos):  # if there are endtags to insert...
                 for et in ends[pos]: outbuf += et
@@ -89,7 +89,7 @@ class MarkupString(unicode):
                 for st in mystarts: outbuf += st
             outbuf += char
         if ends.has_key(e):
-            for et in ends[e]: outbuf+= et
+            for et in ends[e]: outbuf += et
         return MarkupString(outbuf)
 
     def __len__(self):
index e7fa221..fe5c9e3 100644 (file)
@@ -5,7 +5,7 @@ from types import UnicodeType
 from django.template.defaultfilters import slugify
 
 # default unicode character mapping ( you may not see some chars, leave as is )
-char_map = {u'À': 'A', u'Á': 'A', u'Â': 'A', u'Ã': 'A', u'Ä': 'Ae', u'Å': 'A', u'Æ': 'A', u'Ā': 'A', u'Ą': 'A', u'Ă': 'A', u'Ç': 'C', u'Ć': 'C', u'Č': 'C', u'Ĉ': 'C', u'Ċ': 'C', u'Ď': 'D', u'Đ': 'D', u'È': 'E', u'É': 'E', u'Ê': 'E', u'Ë': 'E', u'Ē': 'E', u'Ę': 'E', u'Ě': 'E', u'Ĕ': 'E', u'Ė': 'E', u'Ĝ': 'G', u'Ğ': 'G', u'Ġ': 'G', u'Ģ': 'G', u'Ĥ': 'H', u'Ħ': 'H', u'Ì': 'I', u'Í': 'I', u'Î': 'I', u'Ï': 'I', u'Ī': 'I', u'Ĩ': 'I', u'Ĭ': 'I', u'Į': 'I', u'İ': 'I', u'IJ': 'IJ', u'Ĵ': 'J', u'Ķ': 'K', u'Ľ': 'K', u'Ĺ': 'K', u'Ļ': 'K', u'Ŀ': 'K', u'Ł': 'L', u'Ñ': 'N', u'Ń': 'N', u'Ň': 'N', u'Ņ': 'N', u'Ŋ': 'N', u'Ò': 'O', u'Ó': 'O', u'Ô': 'O', u'Õ': 'O', u'Ö': 'Oe', u'Ø': 'O', u'Ō': 'O', u'Ő': 'O', u'Ŏ': 'O', u'Œ': 'OE', u'Ŕ': 'R', u'Ř': 'R', u'Ŗ': 'R', u'Ś': 'S', u'Ş': 'S', u'Ŝ': 'S', u'Ș': 'S', u'Š': 'S', u'Ť': 'T', u'Ţ': 'T', u'Ŧ': 'T', u'Ț': 'T', u'Ù': 'U', u'Ú': 'U', u'Û': 'U', u'Ü': 'Ue', u'Ū': 'U', u'Ů': 'U', u'Ű': 'U', u'Ŭ': 'U', u'Ũ': 'U', u'Ų': 'U', u'Ŵ': 'W', u'Ŷ': 'Y', u'Ÿ': 'Y', u'Ý': 'Y', u'Ź': 'Z', u'Ż': 'Z', u'Ž': 'Z', u'à': 'a', u'á': 'a', u'â': 'a', u'ã': 'a', u'ä': 'ae', u'ā': 'a', u'ą': 'a', u'ă': 'a', u'å': 'a', u'æ': 'ae', u'ç': 'c', u'ć': 'c', u'č': 'c', u'ĉ': 'c', u'ċ': 'c', u'ď': 'd', u'đ': 'd', u'è': 'e', u'é': 'e', u'ê': 'e', u'ë': 'e', u'ē': 'e', u'ę': 'e', u'ě': 'e', u'ĕ': 'e', u'ė': 'e', u'ƒ': 'f', u'ĝ': 'g', u'ğ': 'g', u'ġ': 'g', u'ģ': 'g', u'ĥ': 'h', u'ħ': 'h', u'ì': 'i', u'í': 'i', u'î': 'i', u'ï': 'i', u'ī': 'i', u'ĩ': 'i', u'ĭ': 'i', u'į': 'i', u'ı': 'i', u'ij': 'ij', u'ĵ': 'j', u'ķ': 'k', u'ĸ': 'k', u'ł': 'l', u'ľ': 'l', u'ĺ': 'l', u'ļ': 'l', u'ŀ': 'l', u'ñ': 'n', u'ń': 'n', u'ň': 'n', u'ņ': 'n', u'ʼn': 'n', u'ŋ': 'n', u'ò': 'o', u'ó': 'o', u'ô': 'o', u'õ': 'o', u'ö': 'oe', u'ø': 'o', u'ō': 'o', u'ő': 'o', u'ŏ': 'o', u'œ': 'oe', u'ŕ': 'r', u'ř': 'r', u'ŗ': 'r', u'ś': 's', u'š': 's', u'ť': 't', u'ù': 'u', u'ú': 'u', u'û': 'u', u'ü': 'ue', u'ū': 'u', u'ů': 'u', u'ű': 'u', u'ŭ': 'u', u'ũ': 'u', u'ų': 'u', u'ŵ': 'w', u'ÿ': 'y', u'ý': 'y', u'ŷ': 'y', u'ż': 'z', u'ź': 'z', u'ž': 'z', u'ß': 'ss', u'ſ': 'ss', u'Α': 'A', u'Ά': 'A', u'Ἀ': 'A', u'Ἁ': 'A', u'Ἂ': 'A', u'Ἃ': 'A', u'Ἄ': 'A', u'Ἅ': 'A', u'Ἆ': 'A', u'Ἇ': 'A', u'ᾈ': 'A', u'ᾉ': 'A', u'ᾊ': 'A', u'ᾋ': 'A', u'ᾌ': 'A', u'ᾍ': 'A', u'ᾎ': 'A', u'ᾏ': 'A', u'Ᾰ': 'A', u'Ᾱ': 'A', u'Ὰ': 'A', u'Ά': 'A', u'ᾼ': 'A', u'Β': 'B', u'Γ': 'G', u'Δ': 'D', u'Ε': 'E', u'Έ': 'E', u'Ἐ': 'E', u'Ἑ': 'E', u'Ἒ': 'E', u'Ἓ': 'E', u'Ἔ': 'E', u'Ἕ': 'E', u'Έ': 'E', u'Ὲ': 'E', u'Ζ': 'Z', u'Η': 'I', u'Ή': 'I', u'Ἠ': 'I', u'Ἡ': 'I', u'Ἢ': 'I', u'Ἣ': 'I', u'Ἤ': 'I', u'Ἥ': 'I', u'Ἦ': 'I', u'Ἧ': 'I', u'ᾘ': 'I', u'ᾙ': 'I', u'ᾚ': 'I', u'ᾛ': 'I', u'ᾜ': 'I', u'ᾝ': 'I', u'ᾞ': 'I', u'ᾟ': 'I', u'Ὴ': 'I', u'Ή': 'I', u'ῌ': 'I', u'Θ': 'TH', u'Ι': 'I', u'Ί': 'I', u'Ϊ': 'I', u'Ἰ': 'I', u'Ἱ': 'I', u'Ἲ': 'I', u'Ἳ': 'I', u'Ἴ': 'I', u'Ἵ': 'I', u'Ἶ': 'I', u'Ἷ': 'I', u'Ῐ': 'I', u'Ῑ': 'I', u'Ὶ': 'I', u'Ί': 'I', u'Κ': 'K', u'Λ': 'L', u'Μ': 'M', u'Ν': 'N', u'Ξ': 'KS', u'Ο': 'O', u'Ό': 'O', u'Ὀ': 'O', u'Ὁ': 'O', u'Ὂ': 'O', u'Ὃ': 'O', u'Ὄ': 'O', u'Ὅ': 'O', u'Ὸ': 'O', u'Ό': 'O', u'Π': 'P', u'Ρ': 'R', u'Ῥ': 'R', u'Σ': 'S', u'Τ': 'T', u'Υ': 'Y', u'Ύ': 'Y', u'Ϋ': 'Y', u'Ὑ': 'Y', u'Ὓ': 'Y', u'Ὕ': 'Y', u'Ὗ': 'Y', u'Ῠ': 'Y', u'Ῡ': 'Y', u'Ὺ': 'Y', u'Ύ': 'Y', u'Φ': 'F', u'Χ': 'X', u'Ψ': 'PS', u'Ω': 'O', u'Ώ': 'O', u'Ὠ': 'O', u'Ὡ': 'O', u'Ὢ': 'O', u'Ὣ': 'O', u'Ὤ': 'O', u'Ὥ': 'O', u'Ὦ': 'O', u'Ὧ': 'O', u'ᾨ': 'O', u'ᾩ': 'O', u'ᾪ': 'O', u'ᾫ': 'O', u'ᾬ': 'O', u'ᾭ': 'O', u'ᾮ': 'O', u'ᾯ': 'O', u'Ὼ': 'O', u'Ώ': 'O', u'ῼ': 'O', u'α': 'a', u'ά': 'a', u'ἀ': 'a', u'ἁ': 'a', u'ἂ': 'a', u'ἃ': 'a', u'ἄ': 'a', u'ἅ': 'a', u'ἆ': 'a', u'ἇ': 'a', u'ᾀ': 'a', u'ᾁ': 'a', u'ᾂ': 'a', u'ᾃ': 'a', u'ᾄ': 'a', u'ᾅ': 'a', u'ᾆ': 'a', u'ᾇ': 'a', u'ὰ': 'a', u'ά': 'a', u'ᾰ': 'a', u'ᾱ': 'a', u'ᾲ': 'a', u'ᾳ': 'a', u'ᾴ': 'a', u'ᾶ': 'a', u'ᾷ': 'a', u'β': 'b', u'γ': 'g', u'δ': 'd', u'ε': 'e', u'έ': 'e', u'ἐ': 'e', u'ἑ': 'e', u'ἒ': 'e', u'ἓ': 'e', u'ἔ': 'e', u'ἕ': 'e', u'ὲ': 'e', u'έ': 'e', u'ζ': 'z', u'η': 'i', u'ή': 'i', u'ἠ': 'i', u'ἡ': 'i', u'ἢ': 'i', u'ἣ': 'i', u'ἤ': 'i', u'ἥ': 'i', u'ἦ': 'i', u'ἧ': 'i', u'ᾐ': 'i', u'ᾑ': 'i', u'ᾒ': 'i', u'ᾓ': 'i', u'ᾔ': 'i', u'ᾕ': 'i', u'ᾖ': 'i', u'ᾗ': 'i', u'ὴ': 'i', u'ή': 'i', u'ῂ': 'i', u'ῃ': 'i', u'ῄ': 'i', u'ῆ': 'i', u'ῇ': 'i', u'θ': 'th', u'ι': 'i', u'ί': 'i', u'ϊ': 'i', u'ΐ': 'i', u'ἰ': 'i', u'ἱ': 'i', u'ἲ': 'i', u'ἳ': 'i', u'ἴ': 'i', u'ἵ': 'i', u'ἶ': 'i', u'ἷ': 'i', u'ὶ': 'i', u'ί': 'i', u'ῐ': 'i', u'ῑ': 'i', u'ῒ': 'i', u'ΐ': 'i', u'ῖ': 'i', u'ῗ': 'i', u'κ': 'k', u'λ': 'l', u'μ': 'm', u'ν': 'n', u'ξ': 'ks', u'ο': 'o', u'ό': 'o', u'ὀ': 'o', u'ὁ': 'o', u'ὂ': 'o', u'ὃ': 'o', u'ὄ': 'o', u'ὅ': 'o', u'ὸ': 'o', u'ό': 'o', u'π': 'p', u'ρ': 'r', u'ῤ': 'r', u'ῥ': 'r', u'σ': 's', u'ς': 's', u'τ': 't', u'υ': 'y', u'ύ': 'y', u'ϋ': 'y', u'ΰ': 'y', u'ὐ': 'y', u'ὑ': 'y', u'ὒ': 'y', u'ὓ': 'y', u'ὔ': 'y', u'ὕ': 'y', u'ὖ': 'y', u'ὗ': 'y', u'ὺ': 'y', u'ύ': 'y', u'ῠ': 'y', u'ῡ': 'y', u'ῢ': 'y', u'ΰ': 'y', u'ῦ': 'y', u'ῧ': 'y', u'φ': 'f', u'χ': 'x', u'ψ': 'ps', u'ω': 'o', u'ώ': 'o', u'ὠ': 'o', u'ὡ': 'o', u'ὢ': 'o', u'ὣ': 'o', u'ὤ': 'o', u'ὥ': 'o', u'ὦ': 'o', u'ὧ': 'o', u'ᾠ': 'o', u'ᾡ': 'o', u'ᾢ': 'o', u'ᾣ': 'o', u'ᾤ': 'o', u'ᾥ': 'o', u'ᾦ': 'o', u'ᾧ': 'o', u'ὼ': 'o', u'ώ': 'o', u'ῲ': 'o', u'ῳ': 'o', u'ῴ': 'o', u'ῶ': 'o', u'ῷ': 'o', u'¨': '', u'΅': '', u'᾿': '', u'῾': '', u'῍': '', u'῝': '', u'῎': '', u'῞': '', u'῏': '', u'῟': '', u'῀': '', u'῁': '', u'΄': '', u'΅': '', u'`': '', u'῭': '', u'ͺ': '', u'᾽': '', u'А': 'A', u'Б': 'B', u'В': 'V', u'Г': 'G', u'Д': 'D', u'Е': 'E', u'Ё': 'E', u'Ж': 'ZH', u'З': 'Z', u'И': 'I', u'Й': 'I', u'К': 'K', u'Л': 'L', u'М': 'M', u'Н': 'N', u'О': 'O', u'П': 'P', u'Р': 'R', u'С': 'S', u'Т': 'T', u'У': 'U', u'Ф': 'F', u'Х': 'KH', u'Ц': 'TS', u'Ч': 'CH', u'Ш': 'SH', u'Щ': 'SHCH', u'Ы': 'Y', u'Э': 'E', u'Ю': 'YU', u'Я': 'YA', u'а': 'A', u'б': 'B', u'в': 'V', u'г': 'G', u'д': 'D', u'е': 'E', u'ё': 'E', u'ж': 'ZH', u'з': 'Z', u'и': 'I', u'й': 'I', u'к': 'K', u'л': 'L', u'м': 'M', u'н': 'N', u'о': 'O', u'п': 'P', u'р': 'R', u'с': 'S', u'т': 'T', u'у': 'U', u'ф': 'F', u'х': 'KH', u'ц': 'TS', u'ч': 'CH', u'ш': 'SH', u'щ': 'SHCH', u'ы': 'Y', u'э': 'E', u'ю': 'YU', u'я': 'YA', u'Ъ': '', u'ъ': '', u'Ь': '', u'ь': '', u'ð': 'd', u'Ð': 'D', u'þ': 'th', u'Þ': 'TH', 
+char_map = {u'À': 'A', u'Á': 'A', u'Â': 'A', u'Ã': 'A', u'Ä': 'Ae', u'Å': 'A', u'Æ': 'A', u'Ā': 'A', u'Ą': 'A', u'Ă': 'A', u'Ç': 'C', u'Ć': 'C', u'Č': 'C', u'Ĉ': 'C', u'Ċ': 'C', u'Ď': 'D', u'Đ': 'D', u'È': 'E', u'É': 'E', u'Ê': 'E', u'Ë': 'E', u'Ē': 'E', u'Ę': 'E', u'Ě': 'E', u'Ĕ': 'E', u'Ė': 'E', u'Ĝ': 'G', u'Ğ': 'G', u'Ġ': 'G', u'Ģ': 'G', u'Ĥ': 'H', u'Ħ': 'H', u'Ì': 'I', u'Í': 'I', u'Î': 'I', u'Ï': 'I', u'Ī': 'I', u'Ĩ': 'I', u'Ĭ': 'I', u'Į': 'I', u'İ': 'I', u'IJ': 'IJ', u'Ĵ': 'J', u'Ķ': 'K', u'Ľ': 'K', u'Ĺ': 'K', u'Ļ': 'K', u'Ŀ': 'K', u'Ł': 'L', u'Ñ': 'N', u'Ń': 'N', u'Ň': 'N', u'Ņ': 'N', u'Ŋ': 'N', u'Ò': 'O', u'Ó': 'O', u'Ô': 'O', u'Õ': 'O', u'Ö': 'Oe', u'Ø': 'O', u'Ō': 'O', u'Ő': 'O', u'Ŏ': 'O', u'Œ': 'OE', u'Ŕ': 'R', u'Ř': 'R', u'Ŗ': 'R', u'Ś': 'S', u'Ş': 'S', u'Ŝ': 'S', u'Ș': 'S', u'Š': 'S', u'Ť': 'T', u'Ţ': 'T', u'Ŧ': 'T', u'Ț': 'T', u'Ù': 'U', u'Ú': 'U', u'Û': 'U', u'Ü': 'Ue', u'Ū': 'U', u'Ů': 'U', u'Ű': 'U', u'Ŭ': 'U', u'Ũ': 'U', u'Ų': 'U', u'Ŵ': 'W', u'Ŷ': 'Y', u'Ÿ': 'Y', u'Ý': 'Y', u'Ź': 'Z', u'Ż': 'Z', u'Ž': 'Z', u'à': 'a', u'á': 'a', u'â': 'a', u'ã': 'a', u'ä': 'ae', u'ā': 'a', u'ą': 'a', u'ă': 'a', u'å': 'a', u'æ': 'ae', u'ç': 'c', u'ć': 'c', u'č': 'c', u'ĉ': 'c', u'ċ': 'c', u'ď': 'd', u'đ': 'd', u'è': 'e', u'é': 'e', u'ê': 'e', u'ë': 'e', u'ē': 'e', u'ę': 'e', u'ě': 'e', u'ĕ': 'e', u'ė': 'e', u'ƒ': 'f', u'ĝ': 'g', u'ğ': 'g', u'ġ': 'g', u'ģ': 'g', u'ĥ': 'h', u'ħ': 'h', u'ì': 'i', u'í': 'i', u'î': 'i', u'ï': 'i', u'ī': 'i', u'ĩ': 'i', u'ĭ': 'i', u'į': 'i', u'ı': 'i', u'ij': 'ij', u'ĵ': 'j', u'ķ': 'k', u'ĸ': 'k', u'ł': 'l', u'ľ': 'l', u'ĺ': 'l', u'ļ': 'l', u'ŀ': 'l', u'ñ': 'n', u'ń': 'n', u'ň': 'n', u'ņ': 'n', u'ʼn': 'n', u'ŋ': 'n', u'ò': 'o', u'ó': 'o', u'ô': 'o', u'õ': 'o', u'ö': 'oe', u'ø': 'o', u'ō': 'o', u'ő': 'o', u'ŏ': 'o', u'œ': 'oe', u'ŕ': 'r', u'ř': 'r', u'ŗ': 'r', u'ś': 's', u'š': 's', u'ť': 't', u'ù': 'u', u'ú': 'u', u'û': 'u', u'ü': 'ue', u'ū': 'u', u'ů': 'u', u'ű': 'u', u'ŭ': 'u', u'ũ': 'u', u'ų': 'u', u'ŵ': 'w', u'ÿ': 'y', u'ý': 'y', u'ŷ': 'y', u'ż': 'z', u'ź': 'z', u'ž': 'z', u'ß': 'ss', u'ſ': 'ss', u'Α': 'A', u'Ά': 'A', u'Ἀ': 'A', u'Ἁ': 'A', u'Ἂ': 'A', u'Ἃ': 'A', u'Ἄ': 'A', u'Ἅ': 'A', u'Ἆ': 'A', u'Ἇ': 'A', u'ᾈ': 'A', u'ᾉ': 'A', u'ᾊ': 'A', u'ᾋ': 'A', u'ᾌ': 'A', u'ᾍ': 'A', u'ᾎ': 'A', u'ᾏ': 'A', u'Ᾰ': 'A', u'Ᾱ': 'A', u'Ὰ': 'A', u'Ά': 'A', u'ᾼ': 'A', u'Β': 'B', u'Γ': 'G', u'Δ': 'D', u'Ε': 'E', u'Έ': 'E', u'Ἐ': 'E', u'Ἑ': 'E', u'Ἒ': 'E', u'Ἓ': 'E', u'Ἔ': 'E', u'Ἕ': 'E', u'Έ': 'E', u'Ὲ': 'E', u'Ζ': 'Z', u'Η': 'I', u'Ή': 'I', u'Ἠ': 'I', u'Ἡ': 'I', u'Ἢ': 'I', u'Ἣ': 'I', u'Ἤ': 'I', u'Ἥ': 'I', u'Ἦ': 'I', u'Ἧ': 'I', u'ᾘ': 'I', u'ᾙ': 'I', u'ᾚ': 'I', u'ᾛ': 'I', u'ᾜ': 'I', u'ᾝ': 'I', u'ᾞ': 'I', u'ᾟ': 'I', u'Ὴ': 'I', u'Ή': 'I', u'ῌ': 'I', u'Θ': 'TH', u'Ι': 'I', u'Ί': 'I', u'Ϊ': 'I', u'Ἰ': 'I', u'Ἱ': 'I', u'Ἲ': 'I', u'Ἳ': 'I', u'Ἴ': 'I', u'Ἵ': 'I', u'Ἶ': 'I', u'Ἷ': 'I', u'Ῐ': 'I', u'Ῑ': 'I', u'Ὶ': 'I', u'Ί': 'I', u'Κ': 'K', u'Λ': 'L', u'Μ': 'M', u'Ν': 'N', u'Ξ': 'KS', u'Ο': 'O', u'Ό': 'O', u'Ὀ': 'O', u'Ὁ': 'O', u'Ὂ': 'O', u'Ὃ': 'O', u'Ὄ': 'O', u'Ὅ': 'O', u'Ὸ': 'O', u'Ό': 'O', u'Π': 'P', u'Ρ': 'R', u'Ῥ': 'R', u'Σ': 'S', u'Τ': 'T', u'Υ': 'Y', u'Ύ': 'Y', u'Ϋ': 'Y', u'Ὑ': 'Y', u'Ὓ': 'Y', u'Ὕ': 'Y', u'Ὗ': 'Y', u'Ῠ': 'Y', u'Ῡ': 'Y', u'Ὺ': 'Y', u'Ύ': 'Y', u'Φ': 'F', u'Χ': 'X', u'Ψ': 'PS', u'Ω': 'O', u'Ώ': 'O', u'Ὠ': 'O', u'Ὡ': 'O', u'Ὢ': 'O', u'Ὣ': 'O', u'Ὤ': 'O', u'Ὥ': 'O', u'Ὦ': 'O', u'Ὧ': 'O', u'ᾨ': 'O', u'ᾩ': 'O', u'ᾪ': 'O', u'ᾫ': 'O', u'ᾬ': 'O', u'ᾭ': 'O', u'ᾮ': 'O', u'ᾯ': 'O', u'Ὼ': 'O', u'Ώ': 'O', u'ῼ': 'O', u'α': 'a', u'ά': 'a', u'ἀ': 'a', u'ἁ': 'a', u'ἂ': 'a', u'ἃ': 'a', u'ἄ': 'a', u'ἅ': 'a', u'ἆ': 'a', u'ἇ': 'a', u'ᾀ': 'a', u'ᾁ': 'a', u'ᾂ': 'a', u'ᾃ': 'a', u'ᾄ': 'a', u'ᾅ': 'a', u'ᾆ': 'a', u'ᾇ': 'a', u'ὰ': 'a', u'ά': 'a', u'ᾰ': 'a', u'ᾱ': 'a', u'ᾲ': 'a', u'ᾳ': 'a', u'ᾴ': 'a', u'ᾶ': 'a', u'ᾷ': 'a', u'β': 'b', u'γ': 'g', u'δ': 'd', u'ε': 'e', u'έ': 'e', u'ἐ': 'e', u'ἑ': 'e', u'ἒ': 'e', u'ἓ': 'e', u'ἔ': 'e', u'ἕ': 'e', u'ὲ': 'e', u'έ': 'e', u'ζ': 'z', u'η': 'i', u'ή': 'i', u'ἠ': 'i', u'ἡ': 'i', u'ἢ': 'i', u'ἣ': 'i', u'ἤ': 'i', u'ἥ': 'i', u'ἦ': 'i', u'ἧ': 'i', u'ᾐ': 'i', u'ᾑ': 'i', u'ᾒ': 'i', u'ᾓ': 'i', u'ᾔ': 'i', u'ᾕ': 'i', u'ᾖ': 'i', u'ᾗ': 'i', u'ὴ': 'i', u'ή': 'i', u'ῂ': 'i', u'ῃ': 'i', u'ῄ': 'i', u'ῆ': 'i', u'ῇ': 'i', u'θ': 'th', u'ι': 'i', u'ί': 'i', u'ϊ': 'i', u'ΐ': 'i', u'ἰ': 'i', u'ἱ': 'i', u'ἲ': 'i', u'ἳ': 'i', u'ἴ': 'i', u'ἵ': 'i', u'ἶ': 'i', u'ἷ': 'i', u'ὶ': 'i', u'ί': 'i', u'ῐ': 'i', u'ῑ': 'i', u'ῒ': 'i', u'ΐ': 'i', u'ῖ': 'i', u'ῗ': 'i', u'κ': 'k', u'λ': 'l', u'μ': 'm', u'ν': 'n', u'ξ': 'ks', u'ο': 'o', u'ό': 'o', u'ὀ': 'o', u'ὁ': 'o', u'ὂ': 'o', u'ὃ': 'o', u'ὄ': 'o', u'ὅ': 'o', u'ὸ': 'o', u'ό': 'o', u'π': 'p', u'ρ': 'r', u'ῤ': 'r', u'ῥ': 'r', u'σ': 's', u'ς': 's', u'τ': 't', u'υ': 'y', u'ύ': 'y', u'ϋ': 'y', u'ΰ': 'y', u'ὐ': 'y', u'ὑ': 'y', u'ὒ': 'y', u'ὓ': 'y', u'ὔ': 'y', u'ὕ': 'y', u'ὖ': 'y', u'ὗ': 'y', u'ὺ': 'y', u'ύ': 'y', u'ῠ': 'y', u'ῡ': 'y', u'ῢ': 'y', u'ΰ': 'y', u'ῦ': 'y', u'ῧ': 'y', u'φ': 'f', u'χ': 'x', u'ψ': 'ps', u'ω': 'o', u'ώ': 'o', u'ὠ': 'o', u'ὡ': 'o', u'ὢ': 'o', u'ὣ': 'o', u'ὤ': 'o', u'ὥ': 'o', u'ὦ': 'o', u'ὧ': 'o', u'ᾠ': 'o', u'ᾡ': 'o', u'ᾢ': 'o', u'ᾣ': 'o', u'ᾤ': 'o', u'ᾥ': 'o', u'ᾦ': 'o', u'ᾧ': 'o', u'ὼ': 'o', u'ώ': 'o', u'ῲ': 'o', u'ῳ': 'o', u'ῴ': 'o', u'ῶ': 'o', u'ῷ': 'o', u'¨': '', u'΅': '', u'᾿': '', u'῾': '', u'῍': '', u'῝': '', u'῎': '', u'῞': '', u'῏': '', u'῟': '', u'῀': '', u'῁': '', u'΄': '', u'΅': '', u'`': '', u'῭': '', u'ͺ': '', u'᾽': '', u'А': 'A', u'Б': 'B', u'В': 'V', u'Г': 'G', u'Д': 'D', u'Е': 'E', u'Ё': 'E', u'Ж': 'ZH', u'З': 'Z', u'И': 'I', u'Й': 'I', u'К': 'K', u'Л': 'L', u'М': 'M', u'Н': 'N', u'О': 'O', u'П': 'P', u'Р': 'R', u'С': 'S', u'Т': 'T', u'У': 'U', u'Ф': 'F', u'Х': 'KH', u'Ц': 'TS', u'Ч': 'CH', u'Ш': 'SH', u'Щ': 'SHCH', u'Ы': 'Y', u'Э': 'E', u'Ю': 'YU', u'Я': 'YA', u'а': 'A', u'б': 'B', u'в': 'V', u'г': 'G', u'д': 'D', u'е': 'E', u'ё': 'E', u'ж': 'ZH', u'з': 'Z', u'и': 'I', u'й': 'I', u'к': 'K', u'л': 'L', u'м': 'M', u'н': 'N', u'о': 'O', u'п': 'P', u'р': 'R', u'с': 'S', u'т': 'T', u'у': 'U', u'ф': 'F', u'х': 'KH', u'ц': 'TS', u'ч': 'CH', u'ш': 'SH', u'щ': 'SHCH', u'ы': 'Y', u'э': 'E', u'ю': 'YU', u'я': 'YA', u'Ъ': '', u'ъ': '', u'Ь': '', u'ь': '', u'ð': 'd', u'Ð': 'D', u'þ': 'th', u'Þ': 'TH',
             u'ა': 'a', u'ბ': 'b', u'გ': 'g', u'დ': 'd', u'ე': 'e', u'ვ': 'v', u'ზ': 'z', u'თ': 't', u'ი': 'i', u'კ': 'k', u'ლ': 'l', u'მ': 'm', u'ნ': 'n', u'ო': 'o', u'პ': 'p', u'ჟ': 'zh', u'რ': 'r', u'ს': 's', u'ტ': 't', u'უ': 'u', u'ფ': 'p', u'ქ': 'k', u'ღ': 'gh', u'ყ': 'q', u'შ': 'sh', u'ჩ': 'ch', u'ც': 'ts', u'ძ': 'dz', u'წ': 'ts', u'ჭ': 'ch', u'ხ': 'kh', u'ჯ': 'j', u'ჰ': 'h' }
 
 def replace_char(m):
@@ -14,43 +14,43 @@ def replace_char(m):
         return char_map[char]
     else:
         return char
-        
+
 def slughifi(value, do_slugify=True, overwrite_char_map={}):
     """
         High Fidelity slugify - slughifi.py, v 0.1
-        
+
         Examples :
-        
+
         >>> text = 'C\'est déjà l\'été.'
-        
+
         >>> slughifi(text)
         'cest-deja-lete'
-        
+
         >>> slughifi(text, overwrite_char_map={u'\'': '-',})
         'c-est-deja-l-ete'
-        
+
         >>> slughifi(text, do_slugify=False)
         "C'est deja l'ete."
-        
+
         # Normal slugify removes accented characters
         >>> slugify(text)
         'cest-dj-lt'
-        
+
     """
 
     # unicodification
     if type(value) != UnicodeType:
         value = unicode(value, 'utf-8', 'ignore')
-        
+
     # overwrite chararcter mapping
     char_map.update(overwrite_char_map)
 
     # try to replace chars
     value = re.sub('[^a-zA-Z0-9\\s\\-]{1}', replace_char, value)
-    
+
     # apply django default slugify
     if do_slugify:
         value = slugify(value)
-        
+
     return value.encode('ascii', 'ignore')
 
index a577d71..62485f3 100644 (file)
@@ -17,6 +17,6 @@ sorl-thumbnail>=3.2
 
 # home-brewed & dependencies
 lxml>=2.2.2
--e git+git://github.com/fnp/librarian.git@d43d87400dcc19851442#egg=librarian
+-e git+git://github.com/fnp/librarian.git@d43d87400dcc19851442#egg=librarian
 
 # MySQL-python>=1.2,<2.0
index 247fc2c..f798d69 100644 (file)
@@ -17,13 +17,13 @@ doc = etree.parse('rodziny.xml')
 
 for element in doc.findall('//span'):
     themes = [s.strip() for s in element.text.split(',')]
-    
+
     element.text = u''
-    
+
     for theme in themes:
         try:
             Tag.objects.get(slug=slughifi(theme))
-        
+
             link = etree.SubElement(element, 'a', href=u'/katalog/%s' % slughifi(theme))
             link.text = theme
             link.tail = ', '
index 9cc721b..3268f45 100755 (executable)
@@ -25,19 +25,19 @@ for file_name in os.listdir('mp3'):
     base_name, ext = splitext(file_name)
     if ext != '.mp3':
         continue
-    
+
     audio = easyid3.EasyID3(join('mp3', file_name))
     title = audio['title'][0]
     artist = title.split(',', 1)[0].strip()
     artist_slug = slughifi(artist)
     title_part = slughifi(title.rsplit(',', 1)[1].strip())
-    
+
     print "--------------------"
     print "File: %s" % file_name
     print "Title: %s" % title
     print
     print "Matching books:"
-    
+
     matching_books = [book for book in Book.tagged.with_all(artist_slug) if book.slug not in chosen_book_slugs]
     matching_books = [book for book in matching_books if title_part in book.slug]
 
@@ -51,11 +51,10 @@ for file_name in os.listdir('mp3'):
     else:
         print "Skipping %s: No matching book found" % file_name
         continue
-    
+
     print "You chose %d (%s)" % (i, matching_books[i].slug)
-    
+
     chosen_book_slugs.add(matching_books[i].slug)
     os.rename(join('mp3', file_name), join('new_mp3', matching_books[i].slug + '.mp3'))
     os.rename(join('oggvorbis', base_name + '.ogg'), join('new_ogg', matching_books[i].slug + '.ogg'))
-    
-    
\ No newline at end of file
+
index e83a07f..3244243 100755 (executable)
@@ -31,7 +31,7 @@ for tag in Tag.objects.all():
         tag.main_page = True
     else:
         tag.main_page = False
-    
+
     tag.save()
     sys.stderr.write('.')
 
index a9acc04..93b2805 100755 (executable)
@@ -1,12 +1,18 @@
 #!/usr/bin/env python
-from os.path import abspath, dirname, join
+import os.path
 import sys
 
+ROOT = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
+
 # Add apps and lib directories to PYTHONPATH
-sys.path.insert(0, abspath(join(dirname(__file__), '../apps')))
-sys.path.insert(0, abspath(join(dirname(__file__), '../lib')))
+sys.path = [
+    os.path.join(ROOT, 'apps'),
+    os.path.join(ROOT, 'lib'),
+    os.path.join(ROOT, 'lib/librarian'),
+] + sys.path
 
 from django.core.management import execute_manager
+
 try:
     import settings # Assumed to be in the same directory.
 except ImportError:
index c9a17dc..60b382c 100644 (file)
@@ -118,7 +118,7 @@ class ProfileMiddleware(object):
             response.content += self.summary_for_files(stats_str)
 
             os.unlink(self.tmpfile)
-            
+
             response.content += '\n%d SQL Queries:\n' % len(connection.queries)
             response.content += pprint.pformat(connection.queries)
 
index b7c55fd..2ece0b1 100644 (file)
@@ -153,8 +153,8 @@ COMPRESS_JS = {
         'output_filename': 'js/jquery.min.js',
     },
     'all': {
-        'source_filenames': ('js/jquery.autocomplete.js', 'js/jquery.form.js', 
-            'js/jquery.countdown.js', 'js/jquery.countdown-pl.js', 
+        'source_filenames': ('js/jquery.autocomplete.js', 'js/jquery.form.js',
+            'js/jquery.countdown.js', 'js/jquery.countdown-pl.js',
             'js/jquery.countdown-de.js', 'js/jquery.countdown-uk.js',
             'js/jquery.countdown-es.js', 'js/jquery.countdown-lt.js',
             'js/jquery.countdown-ru.js', 'js/jquery.countdown-fr.js',
index 41e3be2..598ef2e 100644 (file)
@@ -1,4 +1,4 @@
-$(function() {    
+$(function() {
     function scrollToAnchor(anchor) {
         if (anchor) {
             var element = $('a[name="' + anchor.slice(1) + '"]');
@@ -9,22 +9,22 @@ $(function() {
             }
         }
     }
-    
+
     $.highlightFade.defaults.speed = 3000;
     $('#toc').hide();
     if ($('#toc li').length == 0) {
         $('#menu li a[href="#toc"]').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').toggle(function() {
         $('#menu li a.selected').click();
         $(this).addClass('selected');
index dc4c07d..6e0f562 100644 (file)
@@ -3,49 +3,49 @@ var LOCALE_TEXTS = {
                "DELETE_SHELF": "Czy na pewno usunąć półkę",
                "HIDE_DESCRIPTION": "Zwiń opis",
                "EXPAND_DESCRIPTION": "Rozwiń opis",
-               "LOADING": "Ładowanie"         
+               "LOADING": "Ładowanie"
        },
     "de": {
         "DELETE_SHELF": "Translate me!",
         "HIDE_DESCRIPTION": "Translate me!",
         "EXPAND_DESCRIPTION": "Translate me!",
-        "LOADING": "Translate me!"             
+        "LOADING": "Translate me!"
     },
        "fr": {
                "DELETE_SHELF": "Translate me!",
                "HIDE_DESCRIPTION": "Translate me!",
                "EXPAND_DESCRIPTION": "Translate me!",
-               "LOADING": "Translate me!"                              
+               "LOADING": "Translate me!"
        },
        "en": {
                "DELETE_SHELF": "Translate me!",
                "HIDE_DESCRIPTION": "Translate me!",
                "EXPAND_DESCRIPTION": "Translate me!",
-               "LOADING": "Translate me!"              
-       }, 
+               "LOADING": "Translate me!"
+       },
        "ru": {
                "DELETE_SHELF": "Translate me!",
                "HIDE_DESCRIPTION": "Translate me!",
                "EXPAND_DESCRIPTION": "Translate me!",
-               "LOADING": "Translate me!"              
+               "LOADING": "Translate me!"
        },
        "es": {
                "DELETE_SHELF": "Translate me!",
                "HIDE_DESCRIPTION": "Translate me!",
                "EXPAND_DESCRIPTION": "Translate me!",
-               "LOADING": "Translate me!"                              
+               "LOADING": "Translate me!"
        },
     "lt":{
         "DELETE_SHELF": "Translate me!",
         "HIDE_DESCRIPTION": "Translate me!",
         "EXPAND_DESCRIPTION": "Translate me!",
-        "LOADING": "Translate me!"             
+        "LOADING": "Translate me!"
     },
     "uk":{
         "DELETE_SHELF": "Translate me!",
         "HIDE_DESCRIPTION": "Translate me!",
         "EXPAND_DESCRIPTION": "Translate me!",
-        "LOADING": "Translate me!"             
+        "LOADING": "Translate me!"
     }
 }
 var BANNER_TEXTS = [
@@ -85,7 +85,7 @@ function changeBannerText() {
             $(this).html(BANNER_TEXTS[index]);
             $(this).fadeIn('slow');
         });
-        
+
         setTimeout(changeBannerText, 30 * 1000);
     }
 }
@@ -93,35 +93,35 @@ function changeBannerText() {
 function autocomplete_result_handler(event, item) {
     $(event.target).closest('form').submit();
 }
-function serverTime() { 
-    var time = null; 
-    $.ajax({url: '/katalog/zegar/', 
-        async: false, dataType: 'text', 
-        success: function(text) { 
+function serverTime() {
+    var time = null;
+    $.ajax({url: '/katalog/zegar/',
+        async: false, dataType: 'text',
+        success: function(text) {
             time = new Date(text);
         }, error: function(http, message, exc) {
-            time = new Date(); 
-    }}); 
-    return time; 
+            time = new Date();
+    }});
+    return time;
 }
 
 (function($) {
     $(function() {
-       
+
         $('form input').labelify({labelledClass: 'blur'});
-        
+
         target = $('#login-register-window div.target');
-        
+
         $('#show-registration-form').click(function() {
             $('#login-form').hide();
             $('#registration-form').show();
         });
-        
+
         $('#show-login-form').click(function() {
             $('#registration-form').hide();
             $('#login-form').show();
         });
-        
+
         // Fragments
         $('.fragment-text').each(function() {
             if ($(this).prev().filter('.fragment-short-text').length) {
@@ -136,7 +136,7 @@ function serverTime() {
                 })
             }
         });
-        
+
         $('.fragment-short-text').click(function() {
             $(this).fadeOut(function() { $(this).next().fadeIn() });
             return false;
@@ -144,21 +144,21 @@ function serverTime() {
             function() { $(this).css({background: '#F3F3F3', cursor: 'pointer'}); },
             function() { $(this).css({background: '#FFF'}); }
         );
-        
+
         $('.show-all-tags').click(function() {
-            $(this).parent().parent().fadeOut(function() { 
+            $(this).parent().parent().fadeOut(function() {
                 $(this).next().fadeIn();
             });
             return false;
         });
-        
+
         $('.hide-all-tags').click(function() {
            $(this).parent().parent().fadeOut(function() {
                $(this).prev().fadeIn();
            });
-           return false; 
+           return false;
         });
-        
+
         $('#registration-form').ajaxForm({
             dataType: 'json',
             beforeSubmit: function() {
@@ -179,7 +179,7 @@ function serverTime() {
                 }
             }
         });
-        
+
         $('#login-form').ajaxForm({
             dataType: 'json',
             beforeSubmit: function() {
@@ -200,7 +200,7 @@ function serverTime() {
                 }
             }
         });
-        
+
         $('#login-register-window').jqm({
             target: target[0],
             overlay: 60,
@@ -212,7 +212,7 @@ function serverTime() {
                 hash.w.show();
             }
         });
-        
+
         $('ul.shelf-list li').hover(function() {
             $(this).css({background: '#EEE', cursor: 'pointer'});
         }, function() {
@@ -220,8 +220,8 @@ function serverTime() {
         }).click(function() {
             location.href = $('a.visit-shelf', this).attr('href');
         });
-        
-        $('.delete-shelf').click(function() { 
+
+        $('.delete-shelf').click(function() {
             var link = $(this);
             var shelf_name = $('.visit-shelf', link.parent()).text();
             if (confirm(LOCALE_TEXTS[LANGUAGE_CODE]['DELETE_SHELF']+ ' '+ shelf_name + '?')) {
@@ -231,7 +231,7 @@ function serverTime() {
             }
             return false;
         });
-        
+
         $('#user-shelves-window').jqm({
             ajax: '@href',
             target: $('#user-shelves-window div.target')[0],
@@ -243,14 +243,14 @@ function serverTime() {
                 $('div.header', hash.w).css({width: $(hash.t).width()});
                 hash.w.show();
             },
-            onLoad: function(hash) { 
+            onLoad: function(hash) {
                 $('form', hash.w).ajaxForm({
                     target: $('#user-shelves-window div.target'),
                     success: function() { setTimeout(function() { $('#user-shelves-window').jqmHide() }, 1000) }
                 });
-                
+
                 $('input', hash.w).labelify({labelledClass: 'blur'});
-                
+
                 $('ul.shelf-list li', hash.w).hover(function() {
                     $(this).css({background: '#EEE', cursor: 'pointer'});
                 }, function() {
@@ -258,7 +258,7 @@ function serverTime() {
                 }).click(function() {
                     location.href = $('a.visit-shelf', this).attr('href');
                 });
-                
+
                 $('.delete-shelf').click(function() {
                     var link = $(this);
                     var shelf_name = $('.visit-shelf', link.parent()).text();
@@ -283,7 +283,7 @@ function serverTime() {
                 $('div.header', hash.w).css({width: $(hash.t).width()});
                 hash.w.show();
             },
-            onLoad: function(hash) { 
+            onLoad: function(hash) {
                 $('form', hash.w).ajaxForm({
                                        dataType: 'json',
                     target: $('#suggest-window div.target'),
@@ -304,14 +304,14 @@ function serverTime() {
                 });
             }
         });
-    
+
         $('#books-list .book').hover(
             function() { $(this).css({background: '#F3F3F3', cursor: 'pointer'}); },
             function() { $(this).css({background: '#FFF'}); }
         ).click(function() {
             location.href = $('h2 a', this).attr('href');
         });
-        
+
                function toggled_by_slide(cont, short_el, long_el, button, short_text, long_text) {
                        function toggle(cont, short_el, long_el, button, short_text, long_text) {
                    if (cont.hasClass('short')) {
@@ -343,8 +343,8 @@ function serverTime() {
                 toggle(cont, short_el, long_el, button, short_text, long_text)
             })
                }
-        toggled_by_slide($('#description'), $('#description-short'), $('#description-long'),  
-          $('#toggle-description p'), 
+        toggled_by_slide($('#description'), $('#description-short'), $('#description-long'),
+          $('#toggle-description p'),
           LOCALE_TEXTS[LANGUAGE_CODE]['EXPAND_DESCRIPTION']+' ▼',
                  LOCALE_TEXTS[LANGUAGE_CODE]['HIDE_DESCRIPTION'] + ' ▲'
                  );
@@ -361,17 +361,17 @@ function serverTime() {
         });
 
         var target = $('#set-window div.target');
-    
+
         $('#set-window').jqm({
-            ajax: '@href', 
+            ajax: '@href',
             target: target[0],
             overlay: 60,
-            trigger: 'a.jqm-trigger', 
-            onShow: function(hash) { 
+            trigger: 'a.jqm-trigger',
+            onShow: function(hash) {
                 var offset = $(hash.t).offset();
                 target.html('<p><img src="/static/img/indicator.gif" />'+LOCALE_TEXTS[LANGUAGE_CODE]['DELETE_SHELF']+'</p>');
                 hash.w.css({position: 'absolute', left: offset.left, top: offset.top}).show() },
-            onLoad: function(hash) { 
+            onLoad: function(hash) {
                try {
                        $('#createShelfTrigger').click(function(){
                                $('#createNewShelf').show();
@@ -380,14 +380,14 @@ function serverTime() {
 
                 $('form', hash.w).ajaxForm({
                     target: target,
-                    success: function() { 
-                       setTimeout(function() { 
+                    success: function() {
+                       setTimeout(function() {
                                        $('#set-window').jqmHide();
                                   }, 1000)}
                 });
             }
         });
-        
+
         $('a.remove-from-shelf').click(function(event) {
             event.preventDefault();
             link = $(this);
@@ -395,18 +395,18 @@ function serverTime() {
                 link.parent().remove();
             });
         });
-               
+
         $('#share-shelf').hide().addClass('hidden');
                $('#share-shelf input').focus(function(){this.select();});
-                
+
         $('#user-info').show();
         changeBannerText();
         $('#onepercent-banner').show();
-        
+
         var formatsDownloaded = false;
         $('#download-shelf').click(function() {
             $('#download-shelf-menu').slideDown('fast');
-            
+
             if (!formatsDownloaded) {
                 // Get info about the formats
                 formatsDownloaded = true;
@@ -435,7 +435,7 @@ function serverTime() {
             }
             return false;
         });
-        
+
         $('#download-formats-form-cancel').click(function() {
             $('#download-shelf-menu').slideUp('fast');
             return false;
index 4e425e8..6f46e1b 100644 (file)
@@ -16,7 +16,7 @@
  */
 
 ;(function($) {
-       
+
 $.fn.extend({
        autocomplete: function(urlOrData, options) {
                var isUrl = typeof urlOrData == "string";
@@ -26,13 +26,13 @@ $.fn.extend({
                        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);
                });
@@ -81,9 +81,9 @@ $.Autocompleter = function(input, options) {
                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) {
@@ -91,7 +91,7 @@ $.Autocompleter = function(input, options) {
                        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
@@ -100,7 +100,7 @@ $.Autocompleter = function(input, options) {
                // track last key pressed
                lastKeyPressCode = event.keyCode;
                switch(event.keyCode) {
-               
+
                        case KEY.UP:
                                event.preventDefault();
                                if ( select.visible() ) {
@@ -109,7 +109,7 @@ $.Autocompleter = function(input, options) {
                                        onChange(0, true);
                                }
                                break;
-                               
+
                        case KEY.DOWN:
                                event.preventDefault();
                                if ( select.visible() ) {
@@ -118,7 +118,7 @@ $.Autocompleter = function(input, options) {
                                        onChange(0, true);
                                }
                                break;
-                               
+
                        case KEY.PAGEUP:
                                event.preventDefault();
                                if ( select.visible() ) {
@@ -127,7 +127,7 @@ $.Autocompleter = function(input, options) {
                                        onChange(0, true);
                                }
                                break;
-                               
+
                        case KEY.PAGEDOWN:
                                event.preventDefault();
                                if ( select.visible() ) {
@@ -136,7 +136,7 @@ $.Autocompleter = function(input, options) {
                                        onChange(0, true);
                                }
                                break;
-                       
+
                        // matches also semicolon
                        case options.multiple && $.trim(options.multipleSeparator) == "," && KEY.COMMA:
                        case KEY.TAB:
@@ -148,11 +148,11 @@ $.Autocompleter = function(input, options) {
                                        return false;
                                }
                                break;
-                               
+
                        case KEY.ESC:
                                select.hide();
                                break;
-                               
+
                        default:
                                clearTimeout(timeout);
                                timeout = setTimeout(onChange, options.delay);
@@ -203,16 +203,16 @@ $.Autocompleter = function(input, options) {
                $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 ) {
@@ -234,26 +234,26 @@ $.Autocompleter = function(input, options) {
                        }
                        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);
@@ -265,7 +265,7 @@ $.Autocompleter = function(input, options) {
                        select.hide();
                }
        };
-       
+
        function trimWords(value) {
                if (!value)
                        return [""];
@@ -275,12 +275,12 @@ $.Autocompleter = function(input, options) {
                        return $.trim(value).length ? $.trim(word) : null;
                });
        }
-       
+
        function lastWord(value) {
                if ( !options.multiple )
                        return value;
                var words = trimWords(value);
-               if (words.length == 1) 
+               if (words.length == 1)
                        return words[0];
                var cursorAt = $(input).selection().start;
                if (cursorAt == value.length) {
@@ -290,7 +290,7 @@ $.Autocompleter = function(input, options) {
                }
                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
@@ -355,14 +355,14 @@ $.Autocompleter = function(input, options) {
                        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",
@@ -386,7 +386,7 @@ $.Autocompleter = function(input, options) {
                        failure(term);
                }
        };
-       
+
        function parse(data) {
                var parsed = [];
                var rows = data.split("\n");
@@ -455,9 +455,9 @@ $.Autocompleter.Cache = function(options) {
 
        var data = {};
        var length = 0;
-       
+
        function matchSubset(s, sub) {
-               if (!options.matchCase) 
+               if (!options.matchCase)
                        s = s.toLowerCase();
                var i = s.indexOf(sub);
                if (options.matchContains == "word"){
@@ -467,17 +467,17 @@ $.Autocompleter.Cache = function(options) {
                if (i == -1) return false;
                return i == 0 || options.matchContains;
        };
-       
+
        function add(q, value) {
                if (length > options.cacheLength){
                        flush();
                }
-               if (!data[q]){ 
+               if (!data[q]){
                        length++;
                }
                data[q] = value;
        }
-       
+
        function populate(){
                if( !options.data ) return false;
                // track the matches
@@ -486,23 +486,23 @@ $.Autocompleter.Cache = function(options) {
 
                // 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] ) 
+                       if( !stMatchSets[firstChar] )
                                stMatchSets[firstChar] = [];
 
                        // if the match is a string
@@ -511,7 +511,7 @@ $.Autocompleter.Cache = function(options) {
                                data: rawValue,
                                result: options.formatResult && options.formatResult(rawValue) || value
                        };
-                       
+
                        // push the current match into the set list
                        stMatchSets[firstChar].push(row);
 
@@ -529,15 +529,15 @@ $.Autocompleter.Cache = function(options) {
                        add(i, value);
                });
        }
-       
+
        // populate any existing data
        setTimeout(populate, 25);
-       
+
        function flush(){
                data = {};
                length = 0;
        }
-       
+
        return {
                flush: flush,
                add: add,
@@ -545,7 +545,7 @@ $.Autocompleter.Cache = function(options) {
                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
                         */
@@ -565,9 +565,9 @@ $.Autocompleter.Cache = function(options) {
                                                        }
                                                });
                                        }
-                               }                               
+                               }
                                return csub;
-                       } else 
+                       } else
                        // if the exact item exists, use it
                        if (data[q]){
                                return data[q];
@@ -595,7 +595,7 @@ $.Autocompleter.Select = function (options, input, select, config) {
        var CLASSES = {
                ACTIVE: "ac_over"
        };
-       
+
        var listItems,
                active = -1,
                data,
@@ -603,7 +603,7 @@ $.Autocompleter.Select = function (options, input, select, config) {
                needsInit = true,
                element,
                list;
-       
+
        // Create results
        function init() {
                if (!needsInit)
@@ -613,11 +613,11 @@ $.Autocompleter.Select = function (options, input, select, config) {
                .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);            
+                           $(target(event)).addClass(CLASSES.ACTIVE);
                }
                }).click(function(event) {
                        $(target(event)).addClass(CLASSES.ACTIVE);
@@ -630,13 +630,13 @@ $.Autocompleter.Select = function (options, input, select, config) {
                }).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")
@@ -663,7 +663,7 @@ $.Autocompleter.Select = function (options, input, select, config) {
             }
         }
        };
-       
+
        function movePosition(step) {
                active += step;
                if (active < 0) {
@@ -672,13 +672,13 @@ $.Autocompleter.Select = function (options, input, select, config) {
                        active = 0;
                }
        }
-       
+
        function limitNumberOfItems(available) {
                return options.max && options.max < available
                        ? options.max
                        : available;
        }
-       
+
        function fillList() {
                list.empty();
                var max = limitNumberOfItems(data.length);
@@ -700,7 +700,7 @@ $.Autocompleter.Select = function (options, input, select, config) {
                if ( $.fn.bgiframe )
                        list.bgiframe();
        }
-       
+
        return {
                display: function(d, q) {
                        init();
@@ -752,7 +752,7 @@ $.Autocompleter.Select = function (options, input, select, config) {
                                        maxHeight: options.scrollHeight,
                                        overflow: 'auto'
                                });
-                               
+
                 if($.browser.msie && typeof document.body.style.maxHeight === "undefined") {
                                        var listHeight = 0;
                                        listItems.each(function() {
@@ -765,7 +765,7 @@ $.Autocompleter.Select = function (options, input, select, config) {
                                                listItems.width( list.width() - parseInt(listItems.css("padding-left")) - parseInt(listItems.css("padding-right")) );
                                        }
                 }
-                
+
             }
                },
                selected: function() {
index 0e02ce5..5a3f43d 100644 (file)
@@ -1,12 +1,12 @@
-/* http://keith-wood.name/countdown.html\r
-   German initialisation for the jQuery countdown extension\r
-   Written by Keith Wood (kbwood@virginbroadband.com.au) Jan 2008. */\r
-(function($) {\r
-       $.countdown.regional['de'] = {\r
-               labels: ['Jahren', 'Monate', 'Wochen', 'Tage', 'Stunden', 'Minuten', 'Sekunden'],\r
-               labels1: ['Jahre', 'Monat', 'Woche', 'Tag', 'Stunde', 'Minute', 'Sekunde'],\r
-               compactLabels: ['J', 'M', 'W', 'T'],\r
-               whichLabels: null,\r
-               timeSeparator: ':', isRTL: false};\r
-       $.countdown.setDefaults($.countdown.regional['de']);\r
-})(jQuery);\r
+/* http://keith-wood.name/countdown.html
+   German initialisation for the jQuery countdown extension
+   Written by Keith Wood (kbwood@virginbroadband.com.au) Jan 2008. */
+(function($) {
+       $.countdown.regional['de'] = {
+               labels: ['Jahren', 'Monate', 'Wochen', 'Tage', 'Stunden', 'Minuten', 'Sekunden'],
+               labels1: ['Jahre', 'Monat', 'Woche', 'Tag', 'Stunde', 'Minute', 'Sekunde'],
+               compactLabels: ['J', 'M', 'W', 'T'],
+               whichLabels: null,
+               timeSeparator: ':', isRTL: false};
+       $.countdown.setDefaults($.countdown.regional['de']);
+})(jQuery);
index 09657bc..362f58c 100644 (file)
@@ -1,12 +1,12 @@
-/* http://keith-wood.name/countdown.html\r
- * Spanish initialisation for the jQuery countdown extension\r
- * Written by Sergio Carracedo Martinez webmaster@neodisenoweb.com (2008) */\r
-(function($) {\r
-       $.countdown.regional['es'] = {\r
-               labels: ['Años', 'Meses', 'Semanas', 'Dias', 'Horas', 'Minutos', 'Segundos'],\r
-               labels1: ['Años', 'Meses', 'Semanas', 'Dias', 'Horas', 'Minutos', 'Segundos'],\r
-               compactLabels: ['a', 'm', 's', 'g'],\r
-               whichLabels: null,\r
-               timeSeparator: ':', isRTL: false};\r
-       $.countdown.setDefaults($.countdown.regional['es']);\r
-})(jQuery);\r
+/* http://keith-wood.name/countdown.html
+ * Spanish initialisation for the jQuery countdown extension
+ * Written by Sergio Carracedo Martinez webmaster@neodisenoweb.com (2008) */
+(function($) {
+       $.countdown.regional['es'] = {
+               labels: ['Años', 'Meses', 'Semanas', 'Dias', 'Horas', 'Minutos', 'Segundos'],
+               labels1: ['Años', 'Meses', 'Semanas', 'Dias', 'Horas', 'Minutos', 'Segundos'],
+               compactLabels: ['a', 'm', 's', 'g'],
+               whichLabels: null,
+               timeSeparator: ':', isRTL: false};
+       $.countdown.setDefaults($.countdown.regional['es']);
+})(jQuery);
index 5f5fac9..70e17de 100644 (file)
@@ -1,12 +1,12 @@
-/* http://keith-wood.name/countdown.html\r
-   French initialisation for the jQuery countdown extension\r
-   Written by Keith Wood (kbwood{at}iinet.com.au) Jan 2008. */\r
-(function($) {\r
-       $.countdown.regional['fr'] = {\r
-               labels: ['Années', 'Mois', 'Semaines', 'Jours', 'Heures', 'Minutes', 'Secondes'],\r
-               labels1: ['Année', 'Mois', 'Semaine', 'Jour', 'Heure', 'Minute', 'Seconde'],\r
-               compactLabels: ['a', 'm', 's', 'j'],\r
-               whichLabels: null,\r
-               timeSeparator: ':', isRTL: false};\r
-       $.countdown.setDefaults($.countdown.regional['fr']);\r
-})(jQuery);\r
+/* http://keith-wood.name/countdown.html
+   French initialisation for the jQuery countdown extension
+   Written by Keith Wood (kbwood{at}iinet.com.au) Jan 2008. */
+(function($) {
+       $.countdown.regional['fr'] = {
+               labels: ['Années', 'Mois', 'Semaines', 'Jours', 'Heures', 'Minutes', 'Secondes'],
+               labels1: ['Année', 'Mois', 'Semaine', 'Jour', 'Heure', 'Minute', 'Seconde'],
+               compactLabels: ['a', 'm', 's', 'j'],
+               whichLabels: null,
+               timeSeparator: ':', isRTL: false};
+       $.countdown.setDefaults($.countdown.regional['fr']);
+})(jQuery);
index 77a8a17..6860a4b 100644 (file)
@@ -1,17 +1,17 @@
-/* http://keith-wood.name/countdown.html\r
- * Polish initialisation for the jQuery countdown extension\r
- * Written by Pawel Lewtak lewtak@gmail.com (2008) */\r
-(function($) {\r
-       $.countdown.regional['pl'] = {\r
-               labels: ['lat', 'miesięcy', 'tygodni', 'dni', 'godzin', 'minut', 'sekund'],\r
-               labels1: ['rok', 'miesiąc', 'tydzień', 'dzień', 'godzina', 'minuta', 'sekunda'],\r
-               labels2: ['lata', 'miesiące', 'tygodnie', 'dni', 'godziny', 'minuty', 'sekundy'],\r
-               compactLabels: ['l', 'm', 't', 'd'], compactLabels1: ['r', 'm', 't', 'd'],\r
-               whichLabels: function(amount) {\r
-                       var units = amount % 10;\r
-                       var tens = Math.floor((amount % 100) / 10);\r
-                       return (amount == 1 ? 1 : (units >= 2 && units <= 4 && tens != 1 ? 2 : 0));\r
-               },\r
-               timeSeparator: ':', isRTL: false};\r
-       $.countdown.setDefaults($.countdown.regional['pl']);\r
-})(jQuery);\r
+/* http://keith-wood.name/countdown.html
+ * Polish initialisation for the jQuery countdown extension
+ * Written by Pawel Lewtak lewtak@gmail.com (2008) */
+(function($) {
+       $.countdown.regional['pl'] = {
+               labels: ['lat', 'miesięcy', 'tygodni', 'dni', 'godzin', 'minut', 'sekund'],
+               labels1: ['rok', 'miesiąc', 'tydzień', 'dzień', 'godzina', 'minuta', 'sekunda'],
+               labels2: ['lata', 'miesiące', 'tygodnie', 'dni', 'godziny', 'minuty', 'sekundy'],
+               compactLabels: ['l', 'm', 't', 'd'], compactLabels1: ['r', 'm', 't', 'd'],
+               whichLabels: function(amount) {
+                       var units = amount % 10;
+                       var tens = Math.floor((amount % 100) / 10);
+                       return (amount == 1 ? 1 : (units >= 2 && units <= 4 && tens != 1 ? 2 : 0));
+               },
+               timeSeparator: ':', isRTL: false};
+       $.countdown.setDefaults($.countdown.regional['pl']);
+})(jQuery);
index 1d1ca96..badd3eb 100644 (file)
@@ -1,12 +1,12 @@
-/* http://keith-wood.name/countdown.html\r
- * Russian initialisation for the jQuery countdown extension\r
- * Written by Dominus i3rixon@gmail.com (2008) */\r
-(function($) {\r
-       $.countdown.regional['ru'] = {\r
-               labels: ['Лет', 'Месяцев', 'Недель', 'Дней', 'Часов', 'Минут', 'Секунд'],\r
-               labels1: ['Год', 'Месяц', 'Неделя', 'День', 'Час', 'Минута', 'Секунда'],\r
-               compactLabels: ['l', 'm', 'n', 'd'], compactLabels1: ['g', 'm', 'n', 'd'],\r
-               whichLabels: null,\r
-               timeSeparator: ':', isRTL: false};\r
-       $.countdown.setDefaults($.countdown.regional['ru']);\r
-})(jQuery);\r
+/* http://keith-wood.name/countdown.html
+ * Russian initialisation for the jQuery countdown extension
+ * Written by Dominus i3rixon@gmail.com (2008) */
+(function($) {
+       $.countdown.regional['ru'] = {
+               labels: ['Лет', 'Месяцев', 'Недель', 'Дней', 'Часов', 'Минут', 'Секунд'],
+               labels1: ['Год', 'Месяц', 'Неделя', 'День', 'Час', 'Минута', 'Секунда'],
+               compactLabels: ['l', 'm', 'n', 'd'], compactLabels1: ['g', 'm', 'n', 'd'],
+               whichLabels: null,
+               timeSeparator: ':', isRTL: false};
+       $.countdown.setDefaults($.countdown.regional['ru']);
+})(jQuery);
index e38ab04..c98791e 100644 (file)
@@ -1,12 +1,12 @@
-/* http://keith-wood.name/countdown.html\r
- * Ukrainian initialisation for the jQuery countdown extension\r
- * Written by Goloborodko M misha.gm@gmail.com (2009) */\r
-(function($) {\r
-       $.countdown.regional['uk'] = {\r
-               labels: ['Років', 'Місяців', 'Тижднів', 'Днів', 'Годин', 'Хвилин', 'Секунд'],\r
-               labels1: ['Рік', 'Місяць', 'Тиждень', 'День', 'Година', 'Хвилина', 'Секунда'],\r
-               compactLabels: ['r', 'm', 't', 'd'],\r
-               whichLabels: null,\r
-               timeSeparator: ':', isRTL: false};\r
-       $.countdown.setDefaults($.countdown.regional['uk']);\r
-})(jQuery);\r
+/* http://keith-wood.name/countdown.html
+ * Ukrainian initialisation for the jQuery countdown extension
+ * Written by Goloborodko M misha.gm@gmail.com (2009) */
+(function($) {
+       $.countdown.regional['uk'] = {
+               labels: ['Років', 'Місяців', 'Тижднів', 'Днів', 'Годин', 'Хвилин', 'Секунд'],
+               labels1: ['Рік', 'Місяць', 'Тиждень', 'День', 'Година', 'Хвилина', 'Секунда'],
+               compactLabels: ['r', 'm', 't', 'd'],
+               whichLabels: null,
+               timeSeparator: ':', isRTL: false};
+       $.countdown.setDefaults($.countdown.regional['uk']);
+})(jQuery);
index 27e2f4a..f9c58d3 100644 (file)
@@ -1,8 +1,8 @@
 /* http://keith-wood.name/countdown.html
    Countdown for jQuery v1.5.8.
    Written by Keith Wood (kbwood{at}iinet.com.au) January 2008.
-   Dual licensed under the GPL (http://dev.jquery.com/browser/trunk/jquery/GPL-LICENSE.txt) and 
-   MIT (http://dev.jquery.com/browser/trunk/jquery/MIT-LICENSE.txt) licenses. 
+   Dual licensed under the GPL (http://dev.jquery.com/browser/trunk/jquery/GPL-LICENSE.txt) and
+   MIT (http://dev.jquery.com/browser/trunk/jquery/MIT-LICENSE.txt) licenses.
    Please attribute the author if you use it. */
 
 /* Display a countdown timer.
@@ -68,12 +68,12 @@ var S = 6; // Seconds
 $.extend(Countdown.prototype, {
        /* Class name added to elements to indicate already configured with countdown. */
        markerClassName: 'hasCountdown',
-       
+
        /* Shared timer for all countdowns. */
        _timer: setInterval(function() { $.countdown._updateTargets(); }, 980),
        /* List of currently active countdown targets. */
        _timerTargets: [],
-       
+
        /* Override the default settings for all instances of the countdown widget.
           @param  options  (object) the new settings to use as defaults */
        setDefaults: function(options) {
@@ -277,7 +277,7 @@ $.extend(Countdown.prototype, {
                        }
                }
        },
-       
+
        /* Calculate interal settings for an instance.
           @param  target  (element) the containing division
           @param  inst    (object) the current settings for this instance */
@@ -362,7 +362,7 @@ $.extend(Countdown.prototype, {
                                inst[inst._since ? '_since' : '_until'] =
                                        this._determineTime(sign + inst._periods[0] + 'y' +
                                                sign + inst._periods[1] + 'o' + sign + inst._periods[2] + 'w' +
-                                               sign + inst._periods[3] + 'd' + sign + inst._periods[4] + 'h' + 
+                                               sign + inst._periods[3] + 'd' + sign + inst._periods[4] + 'h' +
                                                sign + inst._periods[5] + 'm' + sign + inst._periods[6] + 's');
                                this._addTarget(target);
                        }
@@ -421,7 +421,7 @@ $.extend(Countdown.prototype, {
                                        case 'd': day += parseInt(matches[1], 10); break;
                                        case 'w': day += parseInt(matches[1], 10) * 7; break;
                                        case 'o':
-                                               month += parseInt(matches[1], 10); 
+                                               month += parseInt(matches[1], 10);
                                                day = Math.min(day, $.countdown._getDaysInMonth(year, month));
                                                break;
                                        case 'y':
@@ -508,8 +508,8 @@ $.extend(Countdown.prototype, {
                return (layout ? this._buildLayout(inst, show, layout, compact, significant, showSignificant) :
                        ((compact ? // Compact version
                        '<span class="countdown_row countdown_amount' +
-                       (inst._hold ? ' countdown_holding' : '') + '">' + 
-                       showCompact(Y) + showCompact(O) + showCompact(W) + showCompact(D) + 
+                       (inst._hold ? ' countdown_holding' : '') + '">' +
+                       showCompact(Y) + showCompact(O) + showCompact(W) + showCompact(D) +
                        (show[H] ? this._minDigits(inst._periods[H], 2) : '') +
                        (show[M] ? (show[H] ? timeSeparator : '') +
                        this._minDigits(inst._periods[M], 2) : '') +
@@ -616,7 +616,7 @@ $.extend(Countdown.prototype, {
                show[S] = (format.match('s') ? '?' : (format.match('S') ? '!' : null));
                return show;
        },
-       
+
        /* Calculate the requested periods between now and the target time.
           @param  inst         (object) the current settings for this instance
           @param  show         (string[7]) flags indicating which periods are requested/required
index 5ecba6e..52fce07 100644 (file)
@@ -1,4 +1,4 @@
-/* 
+/*
  * jQuery Event Delegation Plugin - jquery.eventdelegation.js
  * Fast flexible event handling
  *
                'keydown',
                'keypress',
                'keyup'
-               ], function(i, eventName) {     
+               ], function(i, eventName) {
                        allowed[eventName] = true;
        });
-       
+
        $.fn.extend({
                delegate: function (event, selector, f) {
                        return $(this).each(function () {
@@ -32,7 +32,7 @@
                                        $(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]);
@@ -40,7 +40,7 @@
                                                                        e.preventDefault();
                                                                return;
                                                        }
-                                                       
+
                                                        el = $(el).parent();
                                                }
                                        });
index 659baa9..36af6b1 100644 (file)
@@ -13,7 +13,7 @@
 (function($) {
 
 /*
-    Usage Note:  
+    Usage Note:
     -----------
     Do not use both ajaxSubmit and ajaxForm on the same form.  These
     functions are intended to be exclusive.  Use ajaxSubmit if you want
             target: '#output'
         });
     });
-        
+
     When using ajaxForm, the ajaxSubmit function will be invoked for you
-    at the appropriate time.  
+    at the appropriate time.
 */
 
 /**
- * ajaxSubmit() provides a mechanism for immediately submitting 
+ * ajaxSubmit() provides a mechanism for immediately submitting
  * an HTML form using AJAX.
  */
 $.fn.ajaxSubmit = function(options) {
@@ -80,14 +80,14 @@ $.fn.ajaxSubmit = function(options) {
     if (options.beforeSubmit && options.beforeSubmit(a, this, options) === false) {
         log('ajaxSubmit: submit aborted via beforeSubmit callback');
         return this;
-    }    
+    }
 
     // fire vetoable 'validate' event
     this.trigger('form-submit-validate', [a, this, options, veto]);
     if (veto.veto) {
         log('ajaxSubmit: submit vetoed via form-submit-validate trigger');
         return this;
-    }    
+    }
 
     var q = $.param(a);
 
@@ -125,7 +125,7 @@ $.fn.ajaxSubmit = function(options) {
             found = true;
 
     // options.iframe allows user to force iframe mode
-   if (options.iframe || found) { 
+   if (options.iframe || found) {
        // hack to fix Safari hang (thanks to Tim Molendijk for this)
        // see:  http://groups.google.com/group/jquery-dev/browse_thread/thread/36395b7ab510dd5d
        if ($.browser.safari && options.closeKeepAlive)
@@ -144,19 +144,19 @@ $.fn.ajaxSubmit = function(options) {
     // private function for handling file uploads (hat tip to YAHOO!)
     function fileUpload() {
         var form = $form[0];
-        
+
         if ($(':input[@name=submit]', form).length) {
             alert('Error: Form elements must not be named "submit".');
             return;
         }
-        
+
         var opts = $.extend({}, $.ajaxSettings, options);
 
         var id = 'jqFormIO' + (new Date().getTime());
         var $io = $('<iframe id="' + id + '" name="' + id + '" />');
         var io = $io[0];
 
-        if ($.browser.msie || $.browser.opera) 
+        if ($.browser.msie || $.browser.opera)
             io.src = 'javascript:false;document.write("");';
         $io.css({ position: 'absolute', top: '-1000px', left: '-1000px' });
 
@@ -191,7 +191,7 @@ $.fn.ajaxSubmit = function(options) {
                 }
             }
         }
-        
+
         // take a breath so that pending repaints get some cpu time before the upload starts
         setTimeout(function() {
             // make sure form attrs are set
@@ -216,7 +216,7 @@ $.fn.ajaxSubmit = function(options) {
                         extraInputs.push(
                             $('<input type="hidden" name="'+n+'" value="'+options.extraData[n]+'" />')
                                 .appendTo(form)[0]);
-            
+
                 // add iframe to doc and submit the form
                 $io.appendTo('body');
                 io.attachEvent ? io.attachEvent('onload', cb) : io.addEventListener('load', cb, false);
@@ -232,7 +232,7 @@ $.fn.ajaxSubmit = function(options) {
 
         function cb() {
             if (cbInvoked++) return;
-            
+
             io.detachEvent ? io.detachEvent('onload', cb) : io.removeEventListener('load', cb, false);
 
             var operaHack = 0;
@@ -243,7 +243,7 @@ $.fn.ajaxSubmit = function(options) {
                 var data, doc;
 
                 doc = io.contentWindow ? io.contentWindow.document : io.contentDocument ? io.contentDocument : io.document;
-                
+
                 if (doc.body == null && !operaHack && $.browser.opera) {
                     // In Opera 9.2.x the iframe DOM is not always traversable when
                     // the onload callback fires so we give Opera 100ms to right itself
@@ -252,7 +252,7 @@ $.fn.ajaxSubmit = function(options) {
                     setTimeout(cb, 100);
                     return;
                 }
-                
+
                 xhr.responseText = doc.body ? doc.body.innerHTML : null;
                 xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc;
                 xhr.getResponseHeader = function(header){
@@ -317,7 +317,7 @@ $.fn.ajaxSubmit = function(options) {
  * The options argument for ajaxForm works exactly as it does for ajaxSubmit.  ajaxForm merely
  * passes the options argument along after properly binding events for submit elements and
  * the form itself.
- */ 
+ */
 $.fn.ajaxForm = function(options) {
     return this.ajaxFormUnbind().bind('submit.form-plugin',function() {
         $(this).ajaxSubmit(options);
@@ -563,10 +563,10 @@ $.fn.resetForm = function() {
 /**
  * Enables or disables any matching elements.
  */
-$.fn.enable = function(b) { 
+$.fn.enable = function(b) {
     if (b == undefined) b = true;
-    return this.each(function() { 
-        this.disabled = !b 
+    return this.each(function() {
+        this.disabled = !b
     });
 };
 
@@ -576,7 +576,7 @@ $.fn.enable = function(b) {
  */
 $.fn.select = function(select) {
     if (select == undefined) select = true;
-    return this.each(function() { 
+    return this.each(function() {
         var t = this.type;
         if (t == 'checkbox' || t == 'radio')
             this.checked = select;
index 600cfe1..bbe39f0 100644 (file)
@@ -12,9 +12,9 @@
  * 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] 
+ * 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:
@@ -42,7 +42,7 @@
  *        - 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
  */
@@ -73,7 +73,7 @@ jQuery.fn.highlightFade = function(settings) {
 };
 
 jQuery.highlightFade = function(e,a,o,t) {
-       e.highlighting[a].timer = window.setInterval(function() { 
+       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);
@@ -119,7 +119,7 @@ jQuery.highlightFade.getBaseValue = function(e,a,b) {
        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; 
+               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;
index 966336a..248bb19 100644 (file)
@@ -4,7 +4,7 @@
  * Copyright (c) 2007 Brice Burgess <bhb@iceburg.net>, http://www.iceburg.net
  * Licensed under the MIT License:
  * http://www.opensource.org/licenses/mit-license.php
- * 
+ *
  * $Version: 2007.??.?? +r12 beta
  * Requires: jQuery 1.1.3+
  */
  * notices, modal windows, and image containers. An expando ("_jqm") containing
  * the UUID or "serial" of the modal is added to each element. This expando helps
  * reference the modal's settings in the jqModal Hash Object (jQuery.jqm.hash)
- * 
+ *
  * Accepts a parameter object with the following modal settings;
- * 
- * (Integer) zIndex - Desired z-Index of the modal. This setting does not override (has no effect on) preexisting z-Index styling (set via CSS or inline style).  
- * (Integer) overlay - [0-100] Translucency percentage (opacity) of the body covering overlay. Set to 0 for NO overlay, and up to 100 for a 100% opaque overlay.  
+ *
+ * (Integer) zIndex - Desired z-Index of the modal. This setting does not override (has no effect on) preexisting z-Index styling (set via CSS or inline style).
+ * (Integer) overlay - [0-100] Translucency percentage (opacity) of the body covering overlay. Set to 0 for NO overlay, and up to 100 for a 100% opaque overlay.
  * (String) overlayClass - This class is applied to the body covering overlay. Allows CSS control of the overlay look (tint, background image, etc.).
  * (String) closeClass - A close trigger is added to all elements matching this class within the modal.
  * (Mixed) trigger - An open trigger is added to all matching elements within the DOM. Trigger can be a selector String, a jQuery collection of elements, a DOM element, or a False boolean.
@@ -59,7 +59,7 @@ onLoad: false
 //    *AND*...
 return this.each(function(){if(this._jqm)return;s++;this._jqm=s;
 
-// ... Add this element's serial to the jqModal Hash Object 
+// ... Add this element's serial to the jqModal Hash Object
 //  Hash is globally accessible via jQuery.jqm.hash. It consists of;
 //   c: {obj} config/options
 //   a: {bool} active state (true: active/visible, false: inactive/hidden)
@@ -74,7 +74,7 @@ o.trigger&&$(this).jqmAddTrigger(o.trigger);
 });};
 
 // Adds behavior to triggering elements via the hide-show (HS) function.
-// 
+//
 $.fn.jqmAddClose=function(e){return HS(this,e,'jqmHide');};
 $.fn.jqmAddTrigger=function(e){return HS(this,e,'jqmShow');};
 
@@ -98,23 +98,23 @@ hash:{},
 // mark this modal as active (h.a === true)
 // set the triggering object (h.t) and the modal's z-Index.
 open:function(s,t){var h=H[s],c=h.c,cc='.'+c.closeClass,z=/^\d+$/.test(h.w.css('z-index'))&&h.w.css('z-index')||c.zIndex,o=$('<div></div>').css({height:'100%',width:'100%',position:'fixed',left:0,top:0,'z-index':z-1,opacity:c.overlay/100});h.t=t;h.a=true;h.w.css('z-index',z);
+
  // IF the modal argument was passed as true;
  //    Bind the Keep Focus Function if no other Modals are open (!A[0]),
  //    Add this modal to the opened modals stack (A) for nested modal support,
  //    and Mark overlay to show wait cursor when mouse hovers over it.
  if(c.modal) {!A[0]&&F('bind');A.push(s);o.css('cursor','wait');}
+
  // ELSE IF an overlay was requested (translucency set greater than 0);
  //    Attach a Close event to overlay to hide modal when overlay is clicked.
  else if(c.overlay > 0)h.w.jqmAddClose(o);
+
  // ELSE disable the overlay
  else o=false;
 
  // Add the Overlay to BODY if not disabled.
  h.o=(o)?o.addClass(c.overlayClass).prependTo('body'):false;
+
  // IF IE6;
  //  Set the Overlay to 100% height/width, and fix-position it via JS workaround
  if(ie6&&$('html,body').css({height:'100%',width:'100%'})&&o){o=o.css({position:'absolute'})[0];for(var y in {Top:1,Left:1})o.style.setExpression(y.toLowerCase(),"(_=(document.documentElement.scroll"+y+" || document.body.scroll"+y+"))+'px'");}
@@ -123,13 +123,13 @@ open:function(s,t){var h=H[s],c=h.c,cc='.'+c.closeClass,z=/^\d+$/.test(h.w.css('
  //  determine the target element {JQ} to recieve content (r),
  //  determine the URL {STR} to load content from (u)
  if(c.ajax) {var r=c.target||h.w,u=c.ajax,r=(typeof r == 'string')?$(r,h.w):$(r),u=(u.substr(0,1) == '@')?$(t).attr(u.substring(1)):u;
+
   // Load the Content (and once loaded);
    // Fire the onLoad callback (if exists),
    // Attach closing events to elements inside the modal that match the closingClass,
    // and Execute the jqModal default Open Callback
   r.load(u,function(){c.onLoad&&c.onLoad.call(this,h);cc&&h.w.jqmAddClose($(cc,h.w));O(h);});}
+
  // ELSE the modal content is NOT to be loaded via ajax;
  //  Attach closing events to elements inside the modal that match the closingClass
  else cc&&h.w.jqmAddClose($(cc,h.w));
@@ -137,13 +137,13 @@ open:function(s,t){var h=H[s],c=h.c,cc='.'+c.closeClass,z=/^\d+$/.test(h.w.css('
  // IF toTop was passed and an overlay exists;
  //  Remember the DOM posistion of the modal by inserting a tagged (matching serial) <SPAN> before the modal
  //  Move the Modal from its current position to a first child of the body tag (after the overlay)
- c.toTop&&h.o&&h.w.before('<span id="jqmP'+h.w[0]._jqm+'"></span>').insertAfter(h.o);  
+ c.toTop&&h.o&&h.w.before('<span id="jqmP'+h.w[0]._jqm+'"></span>').insertAfter(h.o);
+
  // Execute user defined onShow callback, or else show (make visible) the modal.
  // Execute the jqModal default Open Callback.
  // Return false to prevent trigger click from being followed.
  (c.onShow)?c.onShow(h):h.w.show();O(h);return false;
+
 },
 
 // Function is executed by $.jqmHide to hide a modal
@@ -152,11 +152,11 @@ close:function(s){var h=H[s];h.a=false;
  // If modal, remove from modal stack.
    // If no modals in modal stack, unbind the Keep Focus Function
  if(h.c.modal){A.pop();!A[0]&&F('unbind');}
+
  // IF toTop was passed and an overlay exists;
  //  Move modal back to its previous ("remembered") position.
  h.c.toTop&&h.o&&$('#jqmP'+h.w[0]._jqm).after(h.w).remove();
+
  // Execute user defined onHide callback, or else hide (make invisible) the modal and remove the overlay.
  if(h.c.onHide)h.c.onHide(h);else{h.w.hide()&&h.o&&h.o.remove()}return false;
 }};
@@ -191,10 +191,10 @@ F=function(t){$()[t]("keypress",x)[t]("keydown",x)[t]("mousedown",x);},
 //      ELSE if so (r===false); follow event (return true [!false])
 x=function(e){var h=H[A[A.length-1]],r=(!$(e.target).parents('.jqmID'+h.s)[0]);r&&f(h);return !r;},
 
-// hide-show function; assigns click events to trigger elements that 
+// hide-show function; assigns click events to trigger elements that
 //   hide, show, or hide AND show modals.
 
-// Expandos (jqmShow and/or jqmHide) are added to all trigger elements. 
+// Expandos (jqmShow and/or jqmHide) are added to all trigger elements.
 // These Expandos hold an array of modal serials {INT} to show or hide.
 
 //  w: {DOM Element} The modal element (window/dialog/notice/etc. container)
@@ -207,7 +207,7 @@ HS=function(w,e,y){var s=[];w.each(function(){s.push(this._jqm)});
 // for each triggering element attach the jqmHide or jqmShow expando (y)
 //  or else expand the expando with the current serial array
  $(e).each(function(){if(this[y])$.extend(this[y],s);
+
  // Assign a click event on the trigger element which examines the element's
  //  jqmHide/Show expandos and attempts to execute $.jqmHide/Show on matching modals
  else{this[y]=s;$(this).click(function(){for(var i in {jqmShow:1,jqmHide:1})for(var s in this[i])if(H[this[i][s]])H[this[i][s]].w[i](this);return false;});}});return w;};
index 13196b2..49b76a3 100644 (file)
@@ -12,7 +12,7 @@
  *
  * You can also pass an options object with the following keys:
  *   text
- *     "title" to get the in-field label from the field's title attribute 
+ *     "title" to get the in-field label from the field's title attribute
  *      (this is the default)
  *     "label" to get the in-field label from the inner text of the field's label
  *      (note that the label must be attached to the field with for="fieldid")
@@ -22,7 +22,7 @@
  *   labelledClass
  *     a class that will be applied to the input field when it contains the
  *      label and removed when it contains user input. Defaults to blank.
- *  
+ *
  */
 jQuery.fn.labelify = function(settings) {
   settings = jQuery.extend({
@@ -51,7 +51,7 @@ jQuery.fn.labelify = function(settings) {
     if (!lookupval) { return; }
 
     // need to strip newlines because the browser strips them
-    // if you set textbox.value to a string containing them    
+    // if you set textbox.value to a string containing them
     $(this).data("label",lookup(this).replace(/\n/g,''));
     $(this).focus(function() {
       if (this.value === $(this).data("label")) {
@@ -64,7 +64,7 @@ jQuery.fn.labelify = function(settings) {
         $(this).addClass(settings.labelledClass);
       }
     });
-    
+
     var removeValuesOnExit = function() {
       jQuery_labellified_elements.each(function(){
         if (this.value === $(this).data("label")) {
@@ -73,10 +73,10 @@ jQuery.fn.labelify = function(settings) {
         }
       })
     };
-    
+
     $(this).parents("form").submit(removeValuesOnExit);
     $(window).unload(removeValuesOnExit);
-    
+
     if (this.value !== this.defaultValue) {
       // user already started typing; don't overwrite their work!
       return;
index 7f42489..c403ab9 100644 (file)
-/**\r
- * jQuery.ScrollTo\r
- * Copyright (c) 2007-2008 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com\r
- * Dual licensed under MIT and GPL.\r
- * Date: 9/11/2008\r
- *\r
- * @projectDescription Easy element scrolling using jQuery.\r
- * http://flesler.blogspot.com/2007/10/jqueryscrollto.html\r
- * Tested with jQuery 1.2.6. On FF 2/3, IE 6/7, Opera 9.2/5 and Safari 3. on Windows.\r
- *\r
- * @author Ariel Flesler\r
- * @version 1.4\r
- *\r
- * @id jQuery.scrollTo\r
- * @id jQuery.fn.scrollTo\r
- * @param {String, Number, DOMElement, jQuery, Object} target Where to scroll the matched elements.\r
- *       The different options for target are:\r
- *             - A number position (will be applied to all axes).\r
- *             - A string position ('44', '100px', '+=90', etc ) will be applied to all axes\r
- *             - A jQuery/DOM element ( logically, child of the element to scroll )\r
- *             - A string selector, that will be relative to the element to scroll ( 'li:eq(2)', etc )\r
- *             - A hash { top:x, left:y }, x and y can be any kind of number/string like above.\r
- * @param {Number} duration The OVERALL length of the animation, this argument can be the settings object instead.\r
- * @param {Object,Function} settings Optional set of settings or the onAfter callback.\r
- *      @option {String} axis Which axis must be scrolled, use 'x', 'y', 'xy' or 'yx'.\r
- *      @option {Number} duration The OVERALL length of the animation.\r
- *      @option {String} easing The easing method for the animation.\r
- *      @option {Boolean} margin If true, the margin of the target element will be deducted from the final position.\r
- *      @option {Object, Number} offset Add/deduct from the end position. One number for both axes or { top:x, left:y }.\r
- *      @option {Object, Number} over Add/deduct the height/width multiplied by 'over', can be { top:x, left:y } when using both axes.\r
- *      @option {Boolean} queue If true, and both axis are given, the 2nd axis will only be animated after the first one ends.\r
- *      @option {Function} onAfter Function to be called after the scrolling ends. \r
- *      @option {Function} onAfterFirst If queuing is activated, this function will be called after the first scrolling ends.\r
- * @return {jQuery} Returns the same jQuery object, for chaining.\r
- *\r
- * @desc Scroll to a fixed position\r
- * @example $('div').scrollTo( 340 );\r
- *\r
- * @desc Scroll relatively to the actual position\r
- * @example $('div').scrollTo( '+=340px', { axis:'y' } );\r
- *\r
- * @dec Scroll using a selector (relative to the scrolled element)\r
- * @example $('div').scrollTo( 'p.paragraph:eq(2)', 500, { easing:'swing', queue:true, axis:'xy' } );\r
- *\r
- * @ Scroll to a DOM element (same for jQuery object)\r
- * @example var second_child = document.getElementById('container').firstChild.nextSibling;\r
- *                     $('#container').scrollTo( second_child, { duration:500, axis:'x', onAfter:function(){\r
- *                             alert('scrolled!!');                                                                                                                               \r
- *                     }});\r
- *\r
- * @desc Scroll on both axes, to different values\r
- * @example $('div').scrollTo( { top: 300, left:'+=200' }, { axis:'xy', offset:-20 } );\r
- */\r
-;(function( $ ){\r
-       \r
-       var $scrollTo = $.scrollTo = function( target, duration, settings ){\r
-               $(window).scrollTo( target, duration, settings );\r
-       };\r
-\r
-       $scrollTo.defaults = {\r
-               axis:'y',\r
-               duration:1\r
-       };\r
-\r
-       // Returns the element that needs to be animated to scroll the window.\r
-       // Kept for backwards compatibility (specially for localScroll & serialScroll)\r
-       $scrollTo.window = function( scope ){\r
-               return $(window).scrollable();\r
-       };\r
-\r
-       // Hack, hack, hack... stay away!\r
-       // Returns the real elements to scroll (supports window/iframes, documents and regular nodes)\r
-       $.fn.scrollable = function(){\r
-               return this.map(function(){\r
-                       // Just store it, we might need it\r
-                       var win = this.parentWindow || this.defaultView,\r
-                               // If it's a document, get its iframe or the window if it's THE document\r
-                               elem = this.nodeName == '#document' ? win.frameElement || win : this,\r
-                               // Get the corresponding document\r
-                               doc = elem.contentDocument || (elem.contentWindow || elem).document,\r
-                               isWin = elem.setInterval;\r
-\r
-                       return elem.nodeName == 'IFRAME' || isWin && $.browser.safari ? doc.body\r
-                               : isWin ? doc.documentElement\r
-                               : this;\r
-               });\r
-       };\r
-\r
-       $.fn.scrollTo = function( target, duration, settings ){\r
-               if( typeof duration == 'object' ){\r
-                       settings = duration;\r
-                       duration = 0;\r
-               }\r
-               if( typeof settings == 'function' )\r
-                       settings = { onAfter:settings };\r
-                       \r
-               settings = $.extend( {}, $scrollTo.defaults, settings );\r
-               // Speed is still recognized for backwards compatibility\r
-               duration = duration || settings.speed || settings.duration;\r
-               // Make sure the settings are given right\r
-               settings.queue = settings.queue && settings.axis.length > 1;\r
-               \r
-               if( settings.queue )\r
-                       // Let's keep the overall duration\r
-                       duration /= 2;\r
-               settings.offset = both( settings.offset );\r
-               settings.over = both( settings.over );\r
-\r
-               return this.scrollable().each(function(){\r
-                       var elem = this,\r
-                               $elem = $(elem),\r
-                               targ = target, toff, attr = {},\r
-                               win = $elem.is('html,body');\r
-\r
-                       switch( typeof targ ){\r
-                               // A number will pass the regex\r
-                               case 'number':\r
-                               case 'string':\r
-                                       if( /^([+-]=)?\d+(px)?$/.test(targ) ){\r
-                                               targ = both( targ );\r
-                                               // We are done\r
-                                               break;\r
-                                       }\r
-                                       // Relative selector, no break!\r
-                                       targ = $(targ,this);\r
-                               case 'object':\r
-                                       // DOMElement / jQuery\r
-                                       if( targ.is || targ.style )\r
-                                               // Get the real position of the target \r
-                                               toff = (targ = $(targ)).offset();\r
-                       }\r
-                       $.each( settings.axis.split(''), function( i, axis ){\r
-                               var Pos = axis == 'x' ? 'Left' : 'Top',\r
-                                       pos = Pos.toLowerCase(),\r
-                                       key = 'scroll' + Pos,\r
-                                       old = elem[key],\r
-                                       Dim = axis == 'x' ? 'Width' : 'Height',\r
-                                       dim = Dim.toLowerCase();\r
-\r
-                               if( toff ){// jQuery / DOMElement\r
-                                       attr[key] = toff[pos] + ( win ? 0 : old - $elem.offset()[pos] );\r
-\r
-                                       // If it's a dom element, reduce the margin\r
-                                       if( settings.margin ){\r
-                                               attr[key] -= parseInt(targ.css('margin'+Pos)) || 0;\r
-                                               attr[key] -= parseInt(targ.css('border'+Pos+'Width')) || 0;\r
-                                       }\r
-                                       \r
-                                       attr[key] += settings.offset[pos] || 0;\r
-                                       \r
-                                       if( settings.over[pos] )\r
-                                               // Scroll to a fraction of its width/height\r
-                                               attr[key] += targ[dim]() * settings.over[pos];\r
-                               }else\r
-                                       attr[key] = targ[pos];\r
-\r
-                               // Number or 'number'\r
-                               if( /^\d+$/.test(attr[key]) )\r
-                                       // Check the limits\r
-                                       attr[key] = attr[key] <= 0 ? 0 : Math.min( attr[key], max(Dim) );\r
-\r
-                               // Queueing axes\r
-                               if( !i && settings.queue ){\r
-                                       // Don't waste time animating, if there's no need.\r
-                                       if( old != attr[key] )\r
-                                               // Intermediate animation\r
-                                               animate( settings.onAfterFirst );\r
-                                       // Don't animate this axis again in the next iteration.\r
-                                       delete attr[key];\r
-                               }\r
-                       });                     \r
-                       animate( settings.onAfter );                    \r
-\r
-                       function animate( callback ){\r
-                               $elem.animate( attr, duration, settings.easing, callback && function(){\r
-                                       callback.call(this, target, settings);\r
-                               });\r
-                       };\r
-                       function max( Dim ){\r
-                               var attr ='scroll'+Dim,\r
-                                       doc = elem.ownerDocument;\r
-                               \r
-                               return win\r
-                                               ? Math.max( doc.documentElement[attr], doc.body[attr]  )\r
-                                               : elem[attr];\r
-                       };\r
-               }).end();\r
-       };\r
-\r
-       function both( val ){\r
-               return typeof val == 'object' ? val : { top:val, left:val };\r
-       };\r
-\r
+/**
+ * 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
index e4fd74d..06ad40c 100644 (file)
@@ -4,10 +4,10 @@
       choices: []
     };
     $.extend(settings, options);
-    
+
     var input = $(this).hide();
     var values = input.val().split(',');
-    
+
     var container = $('<div></div>').insertAfter($(this));
     var choicesList = $('<ol class="choices connectedSortable"></ol>').appendTo(container).css({
       width: 200, float: 'left', minHeight: 200, backgroundColor: '#eee', margin: 0, padding: 0
@@ -19,7 +19,7 @@
     $.each(settings.choices, function() {
       choiceIds.push('' + this.id);
     });
-    
+
     function createItem(hash) {
       return $('<li>' + hash.name + '</li>').css({
         backgroundColor: '#cff',
         margin: 0
       }).data('obj-id', hash.id);
     }
-    
+
     $.each(settings.choices, function() {
       if ($.inArray('' + this.id, values) == -1) {
         choicesList.append(createItem(this));
       }
     });
-    
+
     $.each(values, function() {
       var index = $.inArray('' + this, choiceIds); // Why this[0]?
       if (index != -1) {
         valuesList.append(createItem(settings.choices[index]));
       }
     });
-    
+
     choicesList.sortable({
                connectWith: '.connectedSortable'
        }).disableSelection();
-       
+
        valuesList.sortable({
                connectWith: '.connectedSortable',
                update: function() {
index 4cb5eb6..33c794a 100644 (file)
@@ -4,12 +4,12 @@
       sponsors: []
     };
     $.extend(settings, options);
-    
+
     var input = $(this).hide();
-    
+
     var container = $('<div class="sponsors"></div>').appendTo(input.parent());
     var groups = $.evalJSON(input.val());
-    
+
     var unusedDiv = $('<div class="sponsors-sponsor-group sponsors-unused-sponsor-group"></div>')
       .appendTo(container)
       .append('<p class="sponsors-sponsor-group-name sponsors-unused-sponsor-group-name">dostępni sponsorzy</p>');
         .sortable({
           connectWith: '.sponsors-sponsor-group-list'
                });
-    
+
     // Edit group name inline
     function editNameInline(name) {
       name.unbind('click.sponsorsFooter');
       var inlineInput = $('<input></input>').val(name.html());
       name.html('');
-      
+
       function endEditing() {
         name.html(inlineInput.val());
         inlineInput.remove();
         input.parents('form').unbind('submit.sponsorsFooter', endEditing);
         return false;
       }
-      
+
       inlineInput.appendTo(name).focus().blur(endEditing);
       input.parents('form').bind('submit.sponsorsFooter', endEditing);
     }
-    
+
     // Remove sponsor with passed id from sponsors array and return it
     function popSponsor(id) {
       for (var i=0; i < settings.sponsors.length; i++) {
       }
       return null;
     }
-    
+
     // Create sponsor group and bind events
     function createGroup(name, sponsors) {
       if (!sponsors) {
         sponsors = [];
       }
-      
+
       var groupDiv = $('<div class="sponsors-sponsor-group"></div>');
-      
+
       $('<a class="sponsors-remove-sponsor-group">X</a>')
         .click(function() {
           groupDiv.fadeOut('slow', function() {
             groupDiv.remove();
           });
         }).appendTo(groupDiv);
-      
+
       $('<p class="sponsors-sponsor-group-name">' + name + '</p>')
         .bind('click.sponsorsFooter', function() {
           editNameInline($(this));
         }).appendTo(groupDiv);
-      
+
       var groupList = $('<ol class="sponsors-sponsor-group-list"></ol>')
         .appendTo(groupDiv)
         .sortable({
           connectWith: '.sponsors-sponsor-group-list'
                });
-      
-      
+
+
       for (var i = 0; i < sponsors.length; i++) {
         $('<li class="sponsors-sponsor"><img src="' + sponsors[i].image + '" alt="' + sponsors[i].name + '"/></li>')
           .data('obj_id', sponsors[i].id)
       }
       return groupDiv;
     }
-    
+
     // Create groups from data in input value
     for (var i = 0; i < groups.length; i++) {
       var group = groups[i];
       var sponsors = [];
-      
+
       for (var j = 0; j < group.sponsors.length; j++) {
         var s = popSponsor(group.sponsors[j]);
         if (s) {
       }
       createGroup(group.name, sponsors).appendTo(container);
     }
-    
+
     // Serialize input value before submiting form
     input.parents('form').submit(function(event) {
       var groups = [];
       });
       input.val($.toJSON(groups));
     });
-    
+
     for (i = 0; i < settings.sponsors.length; i++) {
       $('<li class="sponsors-sponsor"><img src="' + settings.sponsors[i].image + '" alt="' + settings.sponsors[i].name + '"/></li>')
         .data('obj_id', settings.sponsors[i].id)
         .appendTo(unusedList);
     }
-    
+
     $('<button type="button">Dodaj nową grupę</button>')
       .click(function() {
         var newGroup = createGroup('').appendTo(container);
         editNameInline($('.sponsors-sponsor-group-name', newGroup));
       }).prependTo(input.parent());
-    
+
     input.parent().append('<div style="clear: both"></div>');
   };
 })(jQuery);
index d7bbdc0..86ad6ac 100644 (file)
@@ -12,7 +12,7 @@
 
 {% block bodycontent %}
     <div id="onepercent-content" class="container_12">
-        
+
         <div id="header" class="grid_12">
             <div id="logos" class="alpha grid_5 suffix_1">
                 <img src="{{ STATIC_URL }}img/logo-big.png" />
@@ -22,7 +22,7 @@
                 <img src="{{ STATIC_URL }}img/tagline.png" />
             </div>
         </div>
-        
+
         <div id="lists" class="grid_12">
             <div id="why" class="alpha grid_6">
                 <h2>Dlaczego warto?</h2>
@@ -46,12 +46,12 @@ KRS 0000070056</pre>
                 <p>Skorzystaj z programu ułatwiającego przygotowanie deklaracji podatkowej on-line.</p>
             </div>
         </div>
-        
+
         <div id="copy" class="grid_12">
             <p>Biblioteka Wolne Lektury to projekt realizowany przez Fundację Nowoczesna Polska. Rozwijamy się tylko dzięki pomocy wolontariuszy i darczyńców. Pomóż nam! Dzięki 1% podatku dodajemy nowe lektury i nowe funkcjonalności.</p>
             <p>Fundacja jest organizacją pożytku publicznego. Bez ponoszenia dodatkowych kosztów Twoi rodzice mogą wspomóc rozwój internetowej biblioteki Wolne Lektury. Co ważne, te pieniądze i tak nie zostaną w ich kieszeni. Jeśli nie podarują ich Fundacji Nowoczesna Polska lub innej organizacji pożytku publicznego, to rząd zdecyduje, jak je wydać. Powiedz rodzicom o możliwości przekazania 1% podatku i przekonaj ich, że warto samodzielnie zadecydować, co stanie się z częścią ich podatków.

Dzięki uzyskanym w ten sposób środkom będziemy mogli opublikować na stronie Wolnych Lektur jeszcze więcej tekstów oraz dodać nowe narzędzia ułatwiające czytanie. Dzięki temu biblioteka będzie jeszcze bardziej przydatnym i przyjaznym miejscem w sieci.</p>
         </div>
-        
+
         <div class="grid_12">
             <a id="back" href="{% url main_page %}">Wróć do serwisu Wolne Lektury</a>
         </div>
index cc8ccc9..50a56ea 100644 (file)
@@ -5,7 +5,7 @@
 <head>
 <title>404 - {% trans "Page does not exist" %} - WolneLektury.pl</title>
 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
-<link rel="stylesheet" type="text/css" href="http://yui.yahooapis.com/2.5.2/build/reset/reset-min.css"> 
+<link rel="stylesheet" type="text/css" href="http://yui.yahooapis.com/2.5.2/build/reset/reset-min.css">
 <link rel="stylesheet" href="{{ STATIC_URL }}css/error.css" type="text/css" />
 </head>
 
index 4ecf3ad..e0b2bbb 100644 (file)
@@ -5,11 +5,11 @@
 <head>
 <title>500 - {% trans "Server error" %} WolneLektury.pl</title>
 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
-<link rel="stylesheet" type="text/css" href="http://yui.yahooapis.com/2.5.2/build/reset/reset-min.css"> 
+<link rel="stylesheet" type="text/css" href="http://yui.yahooapis.com/2.5.2/build/reset/reset-min.css">
 <style type="text/css">
 
 body {
-font-family: verdana, arial, sans-serif; 
+font-family: verdana, arial, sans-serif;
 margin: 2em;
 font-size: 100%;
 }
@@ -30,7 +30,7 @@ img {
 
 a:link {
     color: #037;
-    text-decoration: underline; 
+    text-decoration: underline;
 }
 
 a:visited {
index 7d444a2..3b9ebe1 100644 (file)
@@ -5,11 +5,11 @@
 <head>
 <title>503 - {% trans "Service unavailable" %} WolneLektury.pl</title>
 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
-<link rel="stylesheet" type="text/css" href="http://yui.yahooapis.com/2.5.2/build/reset/reset-min.css"> 
+<link rel="stylesheet" type="text/css" href="http://yui.yahooapis.com/2.5.2/build/reset/reset-min.css">
 <style type="text/css">
 
 body {
-font-family: verdana, arial, sans-serif; 
+font-family: verdana, arial, sans-serif;
 margin: 2em;
 font-size: 100%;
 }
@@ -30,7 +30,7 @@ img {
 
 a:link {
     color: #037;
-    text-decoration: underline; 
+    text-decoration: underline;
 }
 
 a:visited {
index 2e80c55..a168ff7 100644 (file)
@@ -16,7 +16,7 @@
         </ol>
         <p><input type="hidden" name="next" value="{{ next }}" /></p>
     </form>
-    
+
     <form action="." method="post" accept-charset="utf-8" id="registration-form">
         <h2>{% trans "Register" %}</h2>
 
index ddec560..ca41767 100644 (file)
@@ -46,7 +46,7 @@
             <div class="social-links" style="float:right">
                 <a href="http://pl-pl.facebook.com/pages/Wolne-Lektury/203084073268"><img src="{{ STATIC_URL }}img/social/facebook.png" alt="WolneLektury @ Facebook" /></a>
                 <a href="http://twitter.com/wolnelektury"><img src="{{ STATIC_URL }}img/social/twitter.png" alt="WolneLektury @ Twitter" /></a>
-                <a href="http://nasza-klasa.pl/profile/30441509"><img src="{{ STATIC_URL }}img/social/naszaklasa.png" alt="WolneLektury @ Nasza-Klasa" /></a>     
+                <a href="http://nasza-klasa.pl/profile/30441509"><img src="{{ STATIC_URL }}img/social/naszaklasa.png" alt="WolneLektury @ Nasza-Klasa" /></a>
             </div>
                        <div class="lang-menu" style="float:right;">
                                <form action="/i18n/setlang/" method="post">
                                        </select>
                                        <input type="submit" value="{% trans "Choose language" %}">
                                </form>
-                       </div>                  
+                       </div>
             <div class="clearboth"></div>
         </div>
         <div id="maincontent">
             {% block body %}
-            {% endblock %}    
+            {% endblock %}
         </div>
         <div class="clearboth"></div>
         <div id="footer">
             <p>
                {% blocktrans %}
                                Wolne Lektury is a project lead by <a href="http://nowoczesnapolska.org.pl/">Modern Poland Foundation</a>.
-                               Digital reproductions are made by <a href="http://www.bn.org.pl/">The National Library</a>, based on TNL resources. 
+                               Digital reproductions are made by <a href="http://www.bn.org.pl/">The National Library</a>, based on TNL resources.
                                Hosting <a href="http://eo.pl/">EO Networks</a>.
                                {% endblocktrans %}
             </p>
index 7963fe6..809bd1a 100644 (file)
@@ -11,7 +11,7 @@
     <form action="{% url search %}" method="get" accept-charset="utf-8" id="search-form">
         <p>{{ form.q }} <input type="submit" value="{% trans "Search" %}" /> <strong>{% trans "or" %}</strong> <a href="{% url main_page %}">{% trans "return to main page" %}</a></p>
     </form>
-    
+
     <div id="books-list">
         {% if extra_info.license %}
         <p>{% trans "Work is licensed under " %}<a href="{{ extra_info.license }}">{{ extra_info.license_description }}</a>.</p>
@@ -44,7 +44,7 @@
             {% endif %}
             {% if book.mp3_file %}
                 <div id="czytamy-sluchajac-info">
-                    <a href="http://czytamysluchajac.pl/" id="czytamysluchajac-logo"><img src="{{ STATIC_URL }}img/czytamysluchajac-logo-small.png" /></a>     
+                    <a href="http://czytamysluchajac.pl/" id="czytamysluchajac-logo"><img src="{{ STATIC_URL }}img/czytamysluchajac-logo-small.png" /></a>
                     <p>{% trans "Artist" %}: {{ book.get_extra_info_value.artist_name }}</p>
                                        {% if book.get_extra_info_value.director_name %}
                         <p>{% trans "Director" %}: {{ book.get_extra_info_value.director_name }}</p>
@@ -62,7 +62,7 @@
             {% endif %}
             </div>
         </div>
-    
+
         {% if book_children %}
         {% autopaginate book_children 10 %}
         <div id="book-children">
         </div>
         {% paginate %}
         {% endif %}
-    
+
     </div>
-        
+
     <div id="tags-list">
         <div id="book-info">
             <h2>{% trans "Details" %}</h2>
             <ul>
                 <li>
-                    {% trans "Author" %}: 
+                    {% trans "Author" %}:
                     {% for tag in categories.author %}
                     <a href="{{ tag.get_absolute_url }}">{{ tag }}</a>
                     {% endfor %}
index 3e6a9c5..c5f9164 100644 (file)
@@ -11,7 +11,7 @@
     <form action="{% url search %}" method="GET" accept-charset="utf-8" id="search-form">
         <p>{{ form.q }} <input type="submit" value="{% trans "Search" %}" /> <strong>{% trans "or" %}</strong> <a href="{% url main_page %}">{% trans "return to main page" %}</a></p>
     </form>
-    
+
     <div id="book-list">
         {% for first_letter, group in books_by_first_letter.items %}
         <div class="group">
@@ -22,6 +22,6 @@
             {% endfor %}
             </ol>
         </div>
-        {% endfor %}    
+        {% endfor %}
     </div>
 {% endblock %}
\ No newline at end of file
index 5f19220..e35d1d8 100644 (file)
     <form action="{% url search %}" method="get" accept-charset="utf-8" id="search-form">
         <p>{{ form.q }} <input type="submit" value="{% trans "Search" %}" /> <strong>{% trans "or" %}</strong> <a href="{% url main_page %}">{% trans "return to main page" %}</a></p>
     </form>
-    
+
     <div id="books-list">
     {% if book.in_pd %}
-               {% trans "This work is in public domain and will be published on Internet school library of Wolne Lektury soon." %} 
+               {% trans "This work is in public domain and will be published on Internet school library of Wolne Lektury soon." %}
        {% else %}
            {% if book.pd %}
                        {% trans "This work will become part of public domain and will be allowed to be published without restrictions in" %}
index 51b3821..1e7a1a7 100644 (file)
@@ -9,7 +9,7 @@
 {% block body %}
     <h1>{% title_from_tags tags %}</h1>
     {% breadcrumbs tags %}
-    
+
        <p>{% trans "The criteria are ambiguous. Please select one of the following options:" %}</p>
     <div id="books-list">
         {% for option in options %}
index 62a5820..a6ff53a 100644 (file)
@@ -18,7 +18,7 @@
             {% for tag in tags %}
                 <li><a href="{% catalogue_url choices tag %}">{{ tag }}&nbsp;({{ tag.count }})</a></li>
             {% endfor %}
-        </ul>    
+        </ul>
         <p><a href="#" class="hide-all-tags">{% trans "Hide" %}</a></p>
     </div>
 {% endif %}
index 2b9e1fa..8fb3b96 100644 (file)
@@ -12,7 +12,7 @@
     <form action="{% url search %}" method="get" accept-charset="utf-8" id="search-form">
         <p>{{ form.q }} {{ form.tags }} <input type="submit" value="{% trans "Search" %}" /> <strong>{% trans "or" %}</strong> <a href="{% url catalogue.views.book_list %}">{% trans "check list of books" %}</a> {% trans "in our repository" %}</p>
     </form>
-    
+
     <div id="intro">
         <p id="tags-description">↓ {% trans "Browse books by categories" %} ↓</p>
         <div id="propaganda">
@@ -80,7 +80,7 @@
 
                         <li>polityczny obraz świata
                         <span class="subcategories"><a href="/katalog/panstwo/">Państwo</a>, <a href="/katalog/obowiazek/">Obowiązek</a>, <a href="/katalog/cnota/">Cnota</a>, <a href="/katalog/obywatel/">Obywatel</a>, <a href="/katalog/patriota/">Patriota</a>, <a href="/katalog/ojczyzna/">Ojczyzna</a>, <a href="/katalog/narod/">Naród</a>, <a href="/katalog/przywodca/">Przywódca</a>, <a href="/katalog/wladza/">Władza</a>, <a href="/katalog/urzednik/">Urzędnik</a>, <a href="/katalog/krol/">Król</a>, <a href="/katalog/rycerz/">Rycerz</a>, <a href="/katalog/zolnierz/">Żołnierz</a>, <a href="/katalog/wojna/">Wojna</a>, <a href="/katalog/wrog/">Wróg</a>, <a href="/katalog/zwyciestwo/">Zwycięstwo</a>, <a href="/katalog/walka/">Walka</a>, <a href="/katalog/sila/">Siła</a>, <a href="/katalog/historia/">Historia</a>, <a href="/katalog/powstanie/">Powstanie</a>, <a href="/katalog/smierc-bohaterska/">Śmierć bohaterska</a>, <a href="/katalog/slawa/">Sława</a>, <a href="/katalog/rewolucja/">Rewolucja</a>, <a href="/katalog/sad/">Sąd</a>, <a href="/katalog/zdrada/">Zdrada</a></span></li>
-                       
+
                         <li>przyroda
                         <span class="subcategories"><a href="/katalog/natura/">Natura</a>, <a href="/katalog/zywioly/">Żywioły</a>, <a href="/katalog/ogien/">Ogień</a>, <a href="/katalog/ziemia/">Ziemia</a>, <a href="/katalog/wiatr/">Wiatr</a>, <a href="/katalog/woda/">Woda</a>, <a href="/katalog/wiosna/">Wiosna</a>, <a href="/katalog/lato/">Lato</a>, <a href="/katalog/jesien/">Jesień</a>, <a href="/katalog/zima/">Zima</a>, <a href="/katalog/przemijanie/">Przemijanie</a>, <a href="/katalog/slonce/">Słońce</a>, <a href="/katalog/ksiezyc/">Księżyc</a>, <a href="/katalog/gwiazda/">Gwiazda</a>, <a href="/katalog/oblok/">Obłok</a>, <a href="/katalog/noc/">Noc</a>, <a href="/katalog/swiatlo/">Światło</a>, <a href="/katalog/gora/">Góra</a>, <a href="/katalog/rzeka/">Rzeka</a>, <a href="/katalog/morze/">Morze</a>, <a href="/katalog/burza/">Burza</a>, <a href="/katalog/deszcz/">Deszcz</a>, <a href="/katalog/bloto/">Błoto</a>, <a href="/katalog/przyroda-nieozywiona/">Przyroda nieożywiona</a>, <a href="/katalog/rosliny/">Rośliny</a>, <a href="/katalog/kwiaty/">Kwiaty</a>, <a href="/katalog/ogrod/">Ogród</a>, <a href="/katalog/sielanka/">Sielanka</a>, <a href="/katalog/raj/">Raj</a>, <a href="/katalog/jablko/">Jabłko</a>, <a href="/katalog/drzewo/">Drzewo</a>, <a href="/katalog/zwierzeta/">Zwierzęta</a>, <a href="/katalog/ptak/">Ptak</a>, <a href="/katalog/motyl/">Motyl</a>, <a href="/katalog/kot/">Kot</a>, <a href="/katalog/kon/">Koń</a>, <a href="/katalog/pies/">Pies</a>, <a href="/katalog/waz/">Wąż</a>, <a href="/katalog/potwor/">Potwór</a></span></li>
                     </ol>
         </div>
     </div>
     <div class="clearboth"></div>
-    
+
     <div id="site-info">
         <div id="latest-blog-posts">
             <h2>{% trans "News" %}</h2>
index 7296317..79c0ada 100644 (file)
@@ -2,5 +2,5 @@
 <script>
 $.countdown.setDefaults($.countdown.regional['{{ LANGUAGE_CODE }}']);
 d = new Date({{ pd_counter }}, 1, 1);
-$('#countdown').countdown({until: d, format: 'ydHMS', serverSync: serverTime}); 
+$('#countdown').countdown({until: d, format: 'ydHMS', serverSync: serverTime});
 </script>
index fe45140..8e99cdc 100644 (file)
@@ -9,7 +9,7 @@
 {% block body %}
     <h1>{% title_from_tags tags %}</h1>
     {% breadcrumbs tags %}
-    
+
     <div id="books-list">
         <p>{% trans "More than one result matching the criteria found." %}</p>
                <ul class='matches'>
index f7e08ac..1a92e4b 100644 (file)
@@ -9,10 +9,10 @@
 {% block body %}
     <h1>{% title_from_tags tags %}</h1>
     {% breadcrumbs tags %}
-    
+
     <div id="books-list">
         <p>{% trans "Sorry! Search cirteria did not match any resources." %}</p>
-               
+
                <p>{% blocktrans %}Search engine supports following criteria: title, author, theme/topic, epoch, kind and genre.
                As for now we do not support full text search.{% endblocktrans %}</p>
         {% include "info/join_us.html" %}
index f68d065..e10ddc2 100644 (file)
@@ -9,7 +9,7 @@
 {% block body %}
     <h1>{% title_from_tags tags %}</h1>
     {% breadcrumbs tags %}
-    
+
     <div id="books-list">
         <p>{% trans "Sorry! Search query must have at least two characters." %}</p>
         {% include "info/join_us.html" %}
index 5f7d69f..952ba2b 100644 (file)
@@ -9,7 +9,7 @@
 {% block body %}
     <h1>{% title_from_tags tags %}</h1>
     {% breadcrumbs tags %}
-    
+
     {% if shelf_is_set and not object_list %}
     <div id="books-list">
         <h2>{% trans "Your shelf is empty" %}</h2>
                                        {% trans "This author's works are copyrighted." %}
                                {% else %}{% comment %} Is dead {% endcomment %}
                    {% if last_tag.in_pd %}
-                                               <p>{% trans "This author's works are in public domain and will be published on Internet school library of Wolne Lektury soon." %}</p>            
+                                               <p>{% trans "This author's works are in public domain and will be published on Internet school library of Wolne Lektury soon." %}</p>
                        {% else %}
                                                {% comment %} Is dead, but not yet in public domain {% endcomment %}
                        <div>
             {% if categories.epoch %}
                 <h2>{% trans "Epochs" %}</h2>
                 {% tag_list categories.epoch tags %}
-            {% endif %}        
+            {% endif %}
         </div>
         <div id="themes-list">
             {% if categories.theme %}
index afc3d69..d0186ae 100644 (file)
@@ -8,7 +8,7 @@
     <form action="{% url search %}" method="GET" accept-charset="utf-8" id="search-form">
         <p>{{ form.q }} <input type="submit" value="{% trans "Search" %}" /> <strong>{% trans "or" %}</strong> <a href="{% url lessons_document_list %}">{% trans "return to list of materials" %}</a></p>
     </form>
-    
+
     <div id="document-detail">
         <p class="download"><a href="{{ object.file.url }}">{% trans "Download" %}</a> {% if object.author %}({% trans "author" %}: {{ object.author }}){% endif %}</p>
         {% if object.slideshare_id %}
index 79ba80d..d8564a5 100644 (file)
@@ -17,9 +17,9 @@
                 }
                 return false;
             });
-            
+
             var lastHash = null;
-            
+
             function checkHash() {
                 if (document.location.hash != lastHash) {
                     lastHash = document.location.hash;
@@ -28,7 +28,7 @@
                         $('#document-list a').removeClass('active');
                         documentLink.addClass('active');
                     };
-                    
+
                     if ($('#document-detail').attr('data-hash') != lastHash) {
                         $('#document-detail')
                             .attr('data-hash', lastHash)
                 } else if (!document.location.hash) {
                     $('#document-list a:first').click();
                 }
-                
+
                 setTimeout(checkHash, 500);
             };
-            
+
             checkHash();
         });
     </script>
@@ -50,7 +50,7 @@
     <form action="{% url search %}" method="GET" accept-charset="utf-8" id="search-form">
         <p>{{ form.q }} <input type="submit" value="{% trans "Search" %}" /> <strong>{% trans "or" %}</strong> <a href="{% url main_page %}">{% trans "return to main page" %}</a></p>
     </form>
-    
+
     <div id="document-list">
         <ol>
         {% for object in object_list %}
@@ -61,4 +61,3 @@
     <div id="document-detail">
     </div>
 {% endblock %}
-            
\ No newline at end of file
index 5101355..4026ea3 100644 (file)
@@ -21,39 +21,39 @@ urlpatterns = patterns('',
     url(r'^katalog/', include('catalogue.urls')),
     url(r'^materialy/', include('lessons.urls')),
     url(r'^sugestia/', include('suggest.urls')),
-    
+
     # Static pages
-    url(r'^wolontariat/$', 'django.views.generic.list_detail.object_detail', 
+    url(r'^wolontariat/$', 'django.views.generic.list_detail.object_detail',
         dict(infopages, slug='voluntary_services'), name='voluntary_services'),
-    url(r'^mozesz-nam-pomoc/$', 'django.views.generic.list_detail.object_detail', 
+    url(r'^mozesz-nam-pomoc/$', 'django.views.generic.list_detail.object_detail',
         dict(infopages, slug='help_us'), name='help_us'),
-    url(r'^o-projekcie/$', 'django.views.generic.list_detail.object_detail', 
+    url(r'^o-projekcie/$', 'django.views.generic.list_detail.object_detail',
         dict(infopages, slug='about_us'), name='about_us'),
-        
+
     url(r'^1procent/$', 'django.views.generic.simple.direct_to_template', {
         'template': '1percent.html'
     }, name='1percent'),
-    
+
     # Admin panel
     url(r'^admin/catalogue/book/import$', 'catalogue.views.import_book', name='import_book'),
     url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
     url(r'^admin/(.*)$', admin.site.root),
-    
+
     # Authentication
     url(r'^uzytkownicy/zaloguj/$', 'catalogue.views.login', name='login'),
     url(r'^uzytkownicy/wyloguj/$', 'catalogue.views.logout_then_redirect', name='logout'),
     url(r'^uzytkownicy/utworz/$', 'catalogue.views.register', name='register'),
-    
+
     # API
     (r'^api/', include('api.urls')),
-    
+
     # Static files
-    url(r'^%s(?P<path>.*)$' % settings.MEDIA_URL[1:], 'django.views.static.serve', 
+    url(r'^%s(?P<path>.*)$' % settings.MEDIA_URL[1:], 'django.views.static.serve',
         {'document_root': settings.MEDIA_ROOT, 'show_indexes': True}),
     url(r'^%s(?P<path>.*)$' % settings.STATIC_URL[1:], 'django.views.static.serve',
         {'document_root': settings.STATIC_ROOT, 'show_indexes': True}),
     url(r'^$', 'django.views.generic.simple.redirect_to', {'url': 'katalog/'}),
-    url(r'^i18n/', include('django.conf.urls.i18n')),    
+    url(r'^i18n/', include('django.conf.urls.i18n')),
 )
 
 if 'rosetta' in settings.INSTALLED_APPS: