- Split tests in catalouge into a package.
- Don't write to actual MEDIA_ROOT during tests.
- Fixed trailing whitespace.
--- /dev/null
+[submodule "lib/librarian"]
+ path = lib/librarian
+ url = git://github.com/fnp/librarian.git
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)
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',)
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',)
(
[JSONField], # Class(es) these apply to
[], # Positional arguments (not used)
- {}, # Keyword argument
+ {}, # Keyword argument
), ], ["^catalogue\.fields\.JSONField"])
except ImportError:
pass
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)
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'),
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
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)
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)
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:
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()
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)),
('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')
# Deleting model 'Fragment'
db.delete_table('catalogue_fragment')
-
-
+
+
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
}
}
-
+
complete_apps = ['catalogue']
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)),
# 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'},
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
}
}
-
+
complete_apps = ['catalogue']
'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()
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)
# 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')
# Deleting field 'Book._short_html_lt'
db.delete_column('catalogue_book', '_short_html_lt')
-
-
+
+
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
}
}
-
+
complete_apps = ['catalogue']
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)
# 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')
# Deleting field 'Fragment._short_html_uk'
db.delete_column('catalogue_fragment', '_short_html_uk')
-
-
+
+
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
}
}
-
+
complete_apps = ['catalogue']
class Migration(SchemaMigration):
-
+
def forwards(self, orm):
""" Add _tag_counter and make sure all books carry their ancestors' l-tags """
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')
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. """
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'},
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
}
}
-
+
complete_apps = ['catalogue']
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()
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'},
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
}
}
-
+
complete_apps = ['catalogue']
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'},
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
}
}
-
+
complete_apps = ['catalogue']
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',
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()
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)
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)
@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')
book.save()
return book
-
-
+
+
def refresh_tag_counter(self):
tags = {}
for child in self.children.all().order_by():
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:
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):
'kind': u'rodzaj',
'set': u'półka',
}
-
+
title = []
for tag in tags:
title.append("%s: %s" % (mapping[tag.category], tag.name))
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:
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)
def catalogue_url(parser, token):
bits = token.split_contents()
tag_name = bits[0]
-
+
tags_to_add = []
tags_to_remove = []
for bit in bits[1:]:
tags_to_remove.append(bit[1:])
else:
tags_to_add.append(bit)
-
+
return CatalogueURLNode(tags_to_add, tags_to_remove)
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 = []
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:
@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 = []
choices = []
some_tags_hidden = False
tag_count = len(tags)
-
+
if tag_count == 1:
one_tag = tags[0]
else:
# 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
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)
except VariableDoesNotExist:
no_value = True
value_missing = None
-
+
for tests, nodelist in self.cases:
if tests is None:
return nodelist.render(context)
--- /dev/null
+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())
+++ /dev/null
-# -*- 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'])
-
--- /dev/null
+from catalogue.tests.book_import import *
+from catalogue.tests.tags import *
+from catalogue.tests.search import *
--- /dev/null
+# -*- 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")
--- /dev/null
+# -*- 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,))
--- /dev/null
+# -*- 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'])
# 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'),
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()
import traceback
import re
import itertools
-from operator import itemgetter
+from operator import itemgetter
from django.conf import settings
from django.template import RequestContext
'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))
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:
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]
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
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źżŹŻ',
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
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 = {}
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.
"""
""""
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')
ordering = ('key',)
verbose_name = _('chunk')
verbose_name_plural = _('chunks')
-
+
def __unicode__(self):
return self.key
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')
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
n.save()
return ''
return content
-
+
register.tag('chunk', do_get_chunk)
return c.attachment.url
except Attachment.DoesNotExist:
return ''
-
+
register.simple_tag(attachment)
raise NotImplementedError
def filter_js(self, js):
raise NotImplementedError
-
+
class FilterError(Exception):
"""
This exception is raised when a filter fails
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
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
args = ''
def handle_noargs(self, **options):
-
+
force = options.get('force', False)
verbosity = int(options.get('verbosity', 1))
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):
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)),
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'},
'title_uk': ('django.db.models.fields.CharField', [], {'max_length': '120', 'null': True, 'blank': True})
}
}
-
+
complete_apps = ['infopages']
"""
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)
ordering = ('slug',)
verbose_name = _('info page')
verbose_name_plural = _('info pages')
-
+
def __unicode__(self):
return self.title
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")
'form': forms.SearchForm(),
},
}, name='lessons_document_list'),
-
+
url(r'^(?P<slug>[a-zA-Z0-9_-]+)/$', 'lessons.views.document_detail', name='lessons_document_detail'),
)
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',
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
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']
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
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)
"""
def __init__(self, tag_model):
self.tag_model = tag_model
-
+
def __get__(self, instance, owner):
if not instance:
tag_manager = ModelTagManager(self.tag_model)
# Python 2.3 compatibility
try:
set
-except NameError:
+except NameError:
from sets import Set as set
from django.contrib.contenttypes import generic
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.
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]
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.
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
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
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 = """
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
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'),)
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,
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):
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
if not auth_string:
return False
-
+
try:
(authmeth, auth) = auth_string.split(" ", 1)
(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
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
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):
def oauth_request_token(request):
oauth_server, oauth_request = initialize_server_request(request)
-
+
if oauth_server is None:
return INVALID_PARAMS_RESPONSE
try:
@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()
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())
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.
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.
"""
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
response.content = tmpl
return response
-
+
@staticmethod
def is_valid_request(request):
"""
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)
- 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)
'f'
>>> info["argnames"]
['self', 'x', 'y', 'args', 'kw']
-
+
>>> info["defaults"]
(1, 2)
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'.
def caller(func, *args, **kw):
# do something
return func(*args, **kw)
-
+
Here is an example of usage:
>>> @decorator
>>> chatty.__name__
'chatty'
-
+
>>> @chatty
... def f(): pass
...
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
"""
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)
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':
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:
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:
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
"""
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))
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' ])
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)
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=()):
Dispatch, all types are routed through here.
"""
ret = None
-
+
if isinstance(thing, QuerySet):
ret = _qs(thing, fields=fields)
elif isinstance(thing, (tuple, list)):
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
ret = { }
handler = self.in_typemapper(type(data), self.anonymous)
get_absolute_uri = False
-
+
if handler or fields:
v = lambda f: getattr(data, f.attname)
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:
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)):
# 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):
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)
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
more memory friendly for large datasets.
"""
yield self.render(request)
-
+
@classmethod
def get(cls, format):
"""
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):
"""
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)):
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')
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
"""
def render(self, request):
return pickle.dumps(self.construct())
-
+
Emitter.register('pickle', PickleEmitter, 'application/python-pickle')
"""
response = serializers.serialize(format, self.data, indent=True)
return response
-
+
Emitter.register('django', DjangoEmitter, 'text/xml; charset=utf-8')
class Form(forms.Form):
pass
-
+
class ModelForm(forms.ModelForm):
"""
Subclass of `forms.ModelForm` which makes sure
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)
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
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
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
inst.save()
return rc.ALL_OK
-
+
def delete(self, request, *args, **kwargs):
if not self.has_model():
raise NotImplementedError
return rc.DUPLICATE_ENTRY
except self.model.DoesNotExist:
return rc.NOT_HERE
-
+
class AnonymousBaseHandler(BaseHandler):
"""
Anonymous handler.
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)
token.save()
return token
-
+
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)
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()
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',
}
self.key = key
self.secret = secret
self.save()
-
+
# -- OAuth 1.0a stuff
def get_callback_url(self):
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
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
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
"""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
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}
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')
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()),
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)
that as well.
"""
em = kwargs.pop('emitter_format', None)
-
+
if not em:
em = request.GET.get('format', 'json')
return em
-
+
@property
def anonymous(self):
"""
"""
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
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):
"""
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
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
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
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.
"""
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()
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)
# Django imports
-import django.dispatch
+import django.dispatch
# Piston imports
from utils import send_consumer_mail
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:
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:
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
</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>
</head>
<body>
<h1>Authorize Token</h1>
-
+
<form action="{% url piston.authentication.oauth_user_auth %}" method="POST">
{{ form.as_table }}
</form>
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
)
if isinstance(data, dict):
data = urlencode(data)
-
+
return super(OAuthClient, self).post(path, data, content_type, follow, **extra)
class TestCase(test.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)
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)
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.
"""
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
@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:
"""
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)
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-
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
stable, you can change it here.
"""
ident += ':%s' % extra
-
+
now = time.time()
count, expiration = cache.get(ident, (1, None))
return t
cache.set(ident, (count+1, expiration), (expiration - now))
-
+
return f(self, request, *args, **kwargs)
return wrap
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.
"""
request.META['REQUEST_METHOD'] = 'POST'
request._load_post_and_files()
request.META['REQUEST_METHOD'] = 'PUT'
-
+
request.PUT = request.POST
class Mimer(object):
TYPES = dict()
-
+
def __init__(self, request):
self.request = request
-
+
def is_multipart(self):
content_type = self.content_type()
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
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):
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()
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
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.
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
class SponsorPageAdmin(admin.ModelAdmin):
formfield_overrides = {
fields.JSONField: {'widget': widgets.SponsorPageWidget},
- }
+ }
list_display = ('name',)
search_fields = ('name',)
ordering = ('name',)
class JSONFormField(forms.CharField):
widget = forms.Textarea
-
+
def clean(self, value):
try:
loads(value)
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)
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)),
('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'},
'sponsors': ('sponsors.fields.JSONField', [], {'default': '{}'})
}
}
-
+
complete_apps = ['sponsors']
'options': ['pad', 'detail'],
})
url = models.URLField(_('url'), blank=True, verify_exists=False)
-
+
def __unicode__(self):
return self.name
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():
pass
result.append(result_group)
return result
-
+
def html(self):
return self._html
html = property(fget=html)
'sponsors': self.populated_sponsors(),
})
return super(SponsorPage, self).save(*args, **kwargs)
-
+
def __unicode__(self):
return self.name
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);
except:
return u''
return mark_safe(page.html)
-
+
sponsor_page = register.simple_tag(sponsor_page)
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))
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)),
('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'},
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'})
}
}
-
+
complete_apps = ['suggest']
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
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'),
)
from suggest.models import Suggestion
# FIXME - shouldn't be in catalogue
-from catalogue.views import LazyEncoder
+from catalogue.views import LazyEncoder
#@require_POST
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']
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])
--- /dev/null
+Subproject commit d43d87400dcc19851442a42ff20af9fc0b549087
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
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
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
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):
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):
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')
# 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
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 = ', '
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]
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
+
tag.main_page = True
else:
tag.main_page = False
-
+
tag.save()
sys.stderr.write('.')
#!/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:
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)
'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',
-$(function() {
+$(function() {
function scrollToAnchor(anchor) {
if (anchor) {
var element = $('a[name="' + anchor.slice(1) + '"]');
}
}
}
-
+
$.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');
"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 = [
$(this).html(BANNER_TEXTS[index]);
$(this).fadeIn('slow');
});
-
+
setTimeout(changeBannerText, 30 * 1000);
}
}
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) {
})
}
});
-
+
$('.fragment-short-text').click(function() {
$(this).fadeOut(function() { $(this).next().fadeIn() });
return false;
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() {
}
}
});
-
+
$('#login-form').ajaxForm({
dataType: 'json',
beforeSubmit: function() {
}
}
});
-
+
$('#login-register-window').jqm({
target: target[0],
overlay: 60,
hash.w.show();
}
});
-
+
$('ul.shelf-list li').hover(function() {
$(this).css({background: '#EEE', cursor: 'pointer'});
}, function() {
}).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 + '?')) {
}
return false;
});
-
+
$('#user-shelves-window').jqm({
ajax: '@href',
target: $('#user-shelves-window div.target')[0],
$('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() {
}).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();
$('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'),
});
}
});
-
+
$('#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')) {
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'] + ' ▲'
);
});
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();
$('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);
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;
}
return false;
});
-
+
$('#download-formats-form-cancel').click(function() {
$('#download-shelf-menu').slideUp('fast');
return false;
*/
;(function($) {
-
+
$.fn.extend({
autocomplete: function(urlOrData, options) {
var isUrl = typeof urlOrData == "string";
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);
});
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) {
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
// track last key pressed
lastKeyPressCode = event.keyCode;
switch(event.keyCode) {
-
+
case KEY.UP:
event.preventDefault();
if ( select.visible() ) {
onChange(0, true);
}
break;
-
+
case KEY.DOWN:
event.preventDefault();
if ( select.visible() ) {
onChange(0, true);
}
break;
-
+
case KEY.PAGEUP:
event.preventDefault();
if ( select.visible() ) {
onChange(0, true);
}
break;
-
+
case KEY.PAGEDOWN:
event.preventDefault();
if ( select.visible() ) {
onChange(0, true);
}
break;
-
+
// matches also semicolon
case options.multiple && $.trim(options.multipleSeparator) == "," && KEY.COMMA:
case KEY.TAB:
return false;
}
break;
-
+
case KEY.ESC:
select.hide();
break;
-
+
default:
clearTimeout(timeout);
timeout = setTimeout(onChange, options.delay);
$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 ) {
}
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);
select.hide();
}
};
-
+
function trimWords(value) {
if (!value)
return [""];
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) {
}
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
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",
failure(term);
}
};
-
+
function parse(data) {
var parsed = [];
var rows = data.split("\n");
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"){
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
// 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
data: rawValue,
result: options.formatResult && options.formatResult(rawValue) || value
};
-
+
// push the current match into the set list
stMatchSets[firstChar].push(row);
add(i, value);
});
}
-
+
// populate any existing data
setTimeout(populate, 25);
-
+
function flush(){
data = {};
length = 0;
}
-
+
return {
flush: flush,
add: add,
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
*/
}
});
}
- }
+ }
return csub;
- } else
+ } else
// if the exact item exists, use it
if (data[q]){
return data[q];
var CLASSES = {
ACTIVE: "ac_over"
};
-
+
var listItems,
active = -1,
data,
needsInit = true,
element,
list;
-
+
// Create results
function init() {
if (!needsInit)
.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);
}).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")
}
}
};
-
+
function movePosition(step) {
active += step;
if (active < 0) {
active = 0;
}
}
-
+
function limitNumberOfItems(available) {
return options.max && options.max < available
? options.max
: available;
}
-
+
function fillList() {
list.empty();
var max = limitNumberOfItems(data.length);
if ( $.fn.bgiframe )
list.bgiframe();
}
-
+
return {
display: function(d, q) {
init();
maxHeight: options.scrollHeight,
overflow: 'auto'
});
-
+
if($.browser.msie && typeof document.body.style.maxHeight === "undefined") {
var listHeight = 0;
listItems.each(function() {
listItems.width( list.width() - parseInt(listItems.css("padding-left")) - parseInt(listItems.css("padding-right")) );
}
}
-
+
}
},
selected: function() {
-/* 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);
-/* 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);
-/* 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);
-/* 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);
-/* 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);
-/* 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);
/* 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.
$.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) {
}
}
},
-
+
/* Calculate interal settings for an instance.
@param target (element) the containing division
@param inst (object) the current settings for this instance */
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);
}
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':
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) : '') +
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
-/*
+/*
* 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 () {
$(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]);
e.preventDefault();
return;
}
-
+
el = $(el).parent();
}
});
(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) {
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);
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)
// 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' });
}
}
}
-
+
// take a breath so that pending repaints get some cpu time before the upload starts
setTimeout(function() {
// make sure form attrs are set
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);
function cb() {
if (cbInvoked++) return;
-
+
io.detachEvent ? io.detachEvent('onload', cb) : io.removeEventListener('load', cb, false);
var operaHack = 0;
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
setTimeout(cb, 100);
return;
}
-
+
xhr.responseText = doc.body ? doc.body.innerHTML : null;
xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc;
xhr.getResponseHeader = function(header){
* 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);
/**
* 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
});
};
*/
$.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;
* 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:
* - Fixed bug where multiple events on the same element would speed each subsequent event
* 0.1:
* - Initial Release
- *
+ *
* @author Blair Mitchelmore (blair@offput.ca)
* @version 0.5
*/
};
jQuery.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);
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;
* 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.
// *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)
});};
// 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');};
// 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'");}
// 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));
// 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
// 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;
}};
// 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)
// 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;};
*
* 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")
* 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({
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")) {
$(this).addClass(settings.labelledClass);
}
});
-
+
var removeValuesOnExit = function() {
jQuery_labellified_elements.each(function(){
if (this.value === $(this).data("label")) {
}
})
};
-
+
$(this).parents("form").submit(removeValuesOnExit);
$(window).unload(removeValuesOnExit);
-
+
if (this.value !== this.defaultValue) {
// user already started typing; don't overwrite their work!
return;
-/**\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
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
$.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() {
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);
{% 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" />
<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>
<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>
<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>
<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%;
}
a:link {
color: #037;
- text-decoration: underline;
+ text-decoration: underline;
}
a:visited {
<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%;
}
a:link {
color: #037;
- text-decoration: underline;
+ text-decoration: underline;
}
a:visited {
</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>
<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>
<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>
{% 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>
{% 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 %}
<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">
{% endfor %}
</ol>
</div>
- {% endfor %}
+ {% endfor %}
</div>
{% endblock %}
\ No newline at end of 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" %}
{% 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 %}
{% for tag in tags %}
<li><a href="{% catalogue_url choices tag %}">{{ tag }} ({{ tag.count }})</a></li>
{% endfor %}
- </ul>
+ </ul>
<p><a href="#" class="hide-all-tags">{% trans "Hide" %}</a></p>
</div>
{% endif %}
<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">
<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>
<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>
{% 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'>
{% 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" %}
{% 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" %}
{% 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 %}
<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 %}
}
return false;
});
-
+
var lastHash = null;
-
+
function checkHash() {
if (document.location.hash != lastHash) {
lastHash = document.location.hash;
$('#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>
<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 %}
<div id="document-detail">
</div>
{% endblock %}
-
\ No newline at end of file
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: