parent
2b3e715d3d
commit
38025dccfc
108 changed files with 3332 additions and 56 deletions
@ -0,0 +1,6 @@ |
||||
# See http://peak.telecommunity.com/DevCenter/setuptools#namespace-packages |
||||
try: |
||||
__import__('pkg_resources').declare_namespace(__name__) |
||||
except ImportError: |
||||
from pkgutil import extend_path |
||||
__path__ = extend_path(__path__, __name__) |
||||
@ -0,0 +1,6 @@ |
||||
# See http://peak.telecommunity.com/DevCenter/setuptools#namespace-packages |
||||
try: |
||||
__import__('pkg_resources').declare_namespace(__name__) |
||||
except ImportError: |
||||
from pkgutil import extend_path |
||||
__path__ = extend_path(__path__, __name__) |
||||
@ -0,0 +1,8 @@ |
||||
"""emencia.django.newsletter""" |
||||
__version__ = '0.3.dev' |
||||
__license__ = 'BSD License' |
||||
|
||||
__author__ = 'Fantomas42' |
||||
__email__ = 'fantomas42@gmail.com' |
||||
|
||||
__url__ = 'http://emencia.fr/' |
||||
@ -0,0 +1,35 @@ |
||||
"""Admin for emencia.django.newsletter""" |
||||
from django.contrib import admin |
||||
from django.conf import settings |
||||
|
||||
from emencia.django.newsletter.models import Link |
||||
from emencia.django.newsletter.models import Contact |
||||
from emencia.django.newsletter.models import WorkGroup |
||||
from emencia.django.newsletter.models import SMTPServer |
||||
from emencia.django.newsletter.models import Newsletter |
||||
from emencia.django.newsletter.models import MailingList |
||||
from emencia.django.newsletter.models import ContactMailingStatus |
||||
|
||||
from emencia.django.newsletter.settings import USE_WORKGROUPS |
||||
from emencia.django.newsletter.admin.contact import ContactAdmin |
||||
from emencia.django.newsletter.admin.workgroup import WorkGroupAdmin |
||||
from emencia.django.newsletter.admin.newsletter import NewsletterAdmin |
||||
from emencia.django.newsletter.admin.smtpserver import SMTPServerAdmin |
||||
from emencia.django.newsletter.admin.mailinglist import MailingListAdmin |
||||
|
||||
|
||||
admin.site.register(Contact, ContactAdmin) |
||||
admin.site.register(SMTPServer, SMTPServerAdmin) |
||||
admin.site.register(Newsletter, NewsletterAdmin) |
||||
admin.site.register(MailingList, MailingListAdmin) |
||||
|
||||
if USE_WORKGROUPS: |
||||
admin.site.register(WorkGroup, WorkGroupAdmin) |
||||
|
||||
|
||||
class LinkAdmin(admin.ModelAdmin): |
||||
list_display = ('title', 'url', 'creation_date') |
||||
|
||||
if settings.DEBUG: |
||||
admin.site.register(Link, LinkAdmin) |
||||
admin.site.register(ContactMailingStatus) |
||||
@ -0,0 +1,184 @@ |
||||
"""ModelAdmin for Contact""" |
||||
import StringIO |
||||
from django.conf import settings |
||||
from datetime import datetime |
||||
|
||||
from django.contrib import admin |
||||
from django.dispatch import Signal |
||||
from django.conf.urls.defaults import url |
||||
from django.conf.urls.defaults import patterns |
||||
from django.core.urlresolvers import reverse |
||||
from django.shortcuts import render_to_response |
||||
from django.template import RequestContext |
||||
from django.utils.translation import ugettext_lazy as _ |
||||
from django.http import HttpResponseRedirect |
||||
from django.contrib.admin.views.main import ChangeList |
||||
from django.db import DatabaseError |
||||
|
||||
from emencia.django.newsletter.models import MailingList |
||||
from emencia.django.newsletter.settings import USE_WORKGROUPS |
||||
from emencia.django.newsletter.utils.importation import import_dispatcher |
||||
from emencia.django.newsletter.utils.workgroups import request_workgroups |
||||
from emencia.django.newsletter.utils.workgroups import request_workgroups_contacts_pk |
||||
from emencia.django.newsletter.utils.vcard import vcard_contacts_export_response |
||||
from emencia.django.newsletter.utils.excel import ExcelResponse |
||||
|
||||
|
||||
contacts_imported = Signal(providing_args=['source', 'type']) |
||||
|
||||
|
||||
class ContactAdmin(admin.ModelAdmin): |
||||
date_hierarchy = 'creation_date' |
||||
list_display = ('email', 'first_name', 'last_name', 'tags', 'tester', 'subscriber', |
||||
'valid', 'total_subscriptions', 'creation_date', 'related_object_admin') |
||||
list_filter = ('subscriber', 'valid', 'tester', 'creation_date', 'modification_date') |
||||
search_fields = ('email', 'first_name', 'last_name', 'tags') |
||||
fieldsets = ((None, {'fields': ('email', 'first_name', 'last_name')}), |
||||
(None, {'fields': ('tags',)}), |
||||
(_('Status'), {'fields': ('subscriber', 'valid', 'tester')}), |
||||
(_('Advanced'), {'fields': ('object_id', 'content_type'), |
||||
'classes': ('collapse',)}), |
||||
) |
||||
actions = ['create_mailinglist', 'export_vcard', 'export_excel'] |
||||
actions_on_top = False |
||||
actions_on_bottom = True |
||||
|
||||
def queryset(self, request): |
||||
queryset = super(ContactAdmin, self).queryset(request) |
||||
if not request.user.is_superuser and USE_WORKGROUPS: |
||||
contacts_pk = request_workgroups_contacts_pk(request) |
||||
queryset = queryset.filter(pk__in=contacts_pk) |
||||
return queryset |
||||
|
||||
def save_model(self, request, contact, form, change): |
||||
workgroups = [] |
||||
if not contact.pk and not request.user.is_superuser \ |
||||
and USE_WORKGROUPS: |
||||
workgroups = request_workgroups(request) |
||||
contact.save() |
||||
for workgroup in workgroups: |
||||
workgroup.contacts.add(contact) |
||||
|
||||
def related_object_admin(self, contact): |
||||
"""Display link to related object's admin""" |
||||
if contact.content_type and contact.object_id: |
||||
admin_url = reverse('admin:%s_%s_change' % (contact.content_type.app_label, |
||||
contact.content_type.model), |
||||
args=(contact.object_id,)) |
||||
return '%s: <a href="%s">%s</a>' % (contact.content_type.model.capitalize(), |
||||
admin_url, |
||||
contact.content_object.__unicode__()) |
||||
return _('No relative object') |
||||
related_object_admin.allow_tags = True |
||||
related_object_admin.short_description = _('Related object') |
||||
|
||||
def total_subscriptions(self, contact): |
||||
"""Display user subscriptions to unsubscriptions""" |
||||
subscriptions = contact.subscriptions().count() |
||||
unsubscriptions = contact.unsubscriptions().count() |
||||
return '%s / %s' % (subscriptions - unsubscriptions, subscriptions) |
||||
total_subscriptions.short_description = _('Total subscriptions') |
||||
|
||||
def export_vcard(self, request, queryset, export_name=''): |
||||
"""Export selected contact in VCard""" |
||||
return vcard_contacts_export_response(queryset) |
||||
export_vcard.short_description = _('Export contacts as VCard') |
||||
|
||||
def export_excel(self, request, queryset, export_name=''): |
||||
"""Export selected contact in Excel""" |
||||
if not export_name: |
||||
export_name = 'contacts_edn_%s' % datetime.now().strftime('%d-%m-%Y') |
||||
return ExcelResponse(queryset, export_name) |
||||
export_excel.short_description = _('Export contacts in Excel') |
||||
|
||||
def create_mailinglist(self, request, queryset): |
||||
"""Create a mailing list from selected contact""" |
||||
when = str(datetime.now()).split('.')[0] |
||||
new_mailing = MailingList(name=_('New mailinglist at %s') % when, |
||||
description=_('New mailing list created in admin at %s') % when) |
||||
new_mailing.save() |
||||
|
||||
if 'lite' in settings.DATABASES['default']['ENGINE']: |
||||
self.message_user(request, _('SQLite3 or a SpatialLite database type detected, ' \ |
||||
'please note you will be limited to 999 contacts ' \ |
||||
'per mailing list.')) |
||||
try: |
||||
new_mailing.subscribers = queryset.all() |
||||
except DatabaseError: |
||||
new_mailing.subscribers = queryset.none() |
||||
|
||||
if not request.user.is_superuser and USE_WORKGROUPS: |
||||
for workgroup in request_workgroups(request): |
||||
workgroup.mailinglists.add(new_mailing) |
||||
|
||||
self.message_user(request, _('%s succesfully created.') % new_mailing) |
||||
return HttpResponseRedirect(reverse('admin:newsletter_mailinglist_change', |
||||
args=[new_mailing.pk])) |
||||
create_mailinglist.short_description = _('Create a mailinglist') |
||||
|
||||
def importation(self, request): |
||||
"""Import contacts from a VCard""" |
||||
opts = self.model._meta |
||||
|
||||
if request.POST: |
||||
source = request.FILES.get('source') or \ |
||||
StringIO.StringIO(request.POST.get('source', '')) |
||||
if not request.user.is_superuser and USE_WORKGROUPS: |
||||
workgroups = request_workgroups(request) |
||||
else: |
||||
workgroups = [] |
||||
inserted = import_dispatcher(source, request.POST['type'], |
||||
workgroups) |
||||
if inserted: |
||||
contacts_imported.send(sender=self, source=source, |
||||
type=request.POST['type']) |
||||
|
||||
self.message_user(request, _('%s contacts succesfully imported.') % inserted) |
||||
|
||||
context = {'title': _('Contact importation'), |
||||
'opts': opts, |
||||
'root_path': self.admin_site.root_path, |
||||
'app_label': opts.app_label} |
||||
|
||||
return render_to_response('newsletter/contact_import.html', |
||||
context, RequestContext(request)) |
||||
|
||||
def filtered_request_queryset(self, request): |
||||
"""Return queryset filtered by the admin list view""" |
||||
cl = ChangeList(request, self.model, self.list_display, |
||||
self.list_display_links, self.list_filter, |
||||
self.date_hierarchy, self.search_fields, |
||||
self.list_select_related, self.list_per_page, |
||||
self.list_editable, self) |
||||
return cl.get_query_set() |
||||
|
||||
def creation_mailinglist(self, request): |
||||
"""Create a mailing list form the filtered contacts""" |
||||
return self.create_mailinglist(request, self.filtered_request_queryset(request)) |
||||
|
||||
def exportation_vcard(self, request): |
||||
"""Export filtered contacts in VCard""" |
||||
return self.export_vcard(request, self.filtered_request_queryset(request), |
||||
'contacts_edn_%s' % datetime.now().strftime('%d-%m-%Y')) |
||||
|
||||
def exportation_excel(self, request): |
||||
"""Export filtered contacts in Excel""" |
||||
return self.export_excel(request, self.filtered_request_queryset(request), |
||||
'contacts_edn_%s' % datetime.now().strftime('%d-%m-%Y')) |
||||
|
||||
def get_urls(self): |
||||
urls = super(ContactAdmin, self).get_urls() |
||||
my_urls = patterns('', |
||||
url(r'^import/$', |
||||
self.admin_site.admin_view(self.importation), |
||||
name='newsletter_contact_import'), |
||||
url(r'^create_mailinglist/$', |
||||
self.admin_site.admin_view(self.creation_mailinglist), |
||||
name='newsletter_contact_create_mailinglist'), |
||||
url(r'^export/vcard/$', |
||||
self.admin_site.admin_view(self.exportation_vcard), |
||||
name='newsletter_contact_export_vcard'), |
||||
url(r'^export/excel/$', |
||||
self.admin_site.admin_view(self.exportation_excel), |
||||
name='newsletter_contact_export_excel'),) |
||||
return my_urls + urls |
||||
@ -0,0 +1,125 @@ |
||||
"""ModelAdmin for MailingList""" |
||||
from datetime import datetime |
||||
|
||||
from django.contrib import admin |
||||
from django.conf.urls.defaults import url |
||||
from django.conf.urls.defaults import patterns |
||||
from django.utils.encoding import smart_str |
||||
from django.core.urlresolvers import reverse |
||||
from django.shortcuts import get_object_or_404 |
||||
from django.utils.translation import ugettext_lazy as _ |
||||
from django.http import HttpResponseRedirect |
||||
|
||||
from emencia.django.newsletter.models import Contact |
||||
from emencia.django.newsletter.models import MailingList |
||||
from emencia.django.newsletter.settings import USE_WORKGROUPS |
||||
from emencia.django.newsletter.utils.workgroups import request_workgroups |
||||
from emencia.django.newsletter.utils.workgroups import request_workgroups_contacts_pk |
||||
from emencia.django.newsletter.utils.workgroups import request_workgroups_mailinglists_pk |
||||
from emencia.django.newsletter.utils.vcard import vcard_contacts_export_response |
||||
from emencia.django.newsletter.utils.excel import ExcelResponse |
||||
|
||||
|
||||
class MailingListAdmin(admin.ModelAdmin): |
||||
date_hierarchy = 'creation_date' |
||||
list_display = ('creation_date', 'name', 'description', |
||||
'subscribers_count', 'unsubscribers_count', |
||||
'exportation_links') |
||||
list_editable = ('name', 'description') |
||||
list_filter = ('creation_date', 'modification_date') |
||||
search_fields = ('name', 'description',) |
||||
filter_horizontal = ['subscribers', 'unsubscribers'] |
||||
fieldsets = ((None, {'fields': ('name', 'description',)}), |
||||
(None, {'fields': ('subscribers',)}), |
||||
(None, {'fields': ('unsubscribers',)}), |
||||
) |
||||
actions = ['merge_mailinglist'] |
||||
actions_on_top = False |
||||
actions_on_bottom = True |
||||
|
||||
def queryset(self, request): |
||||
queryset = super(MailingListAdmin, self).queryset(request) |
||||
if not request.user.is_superuser and USE_WORKGROUPS: |
||||
mailinglists_pk = request_workgroups_mailinglists_pk(request) |
||||
queryset = queryset.filter(pk__in=mailinglists_pk) |
||||
return queryset |
||||
|
||||
def save_model(self, request, mailinglist, form, change): |
||||
workgroups = [] |
||||
if not mailinglist.pk and not request.user.is_superuser \ |
||||
and USE_WORKGROUPS: |
||||
workgroups = request_workgroups(request) |
||||
mailinglist.save() |
||||
for workgroup in workgroups: |
||||
workgroup.mailinglists.add(mailinglist) |
||||
|
||||
def formfield_for_manytomany(self, db_field, request, **kwargs): |
||||
if 'subscribers' in db_field.name and not request.user.is_superuser \ |
||||
and USE_WORKGROUPS: |
||||
contacts_pk = request_workgroups_contacts_pk(request) |
||||
kwargs['queryset'] = Contact.objects.filter(pk__in=contacts_pk) |
||||
return super(MailingListAdmin, self).formfield_for_manytomany( |
||||
db_field, request, **kwargs) |
||||
|
||||
def merge_mailinglist(self, request, queryset): |
||||
"""Merge multiple mailing list""" |
||||
if queryset.count() == 1: |
||||
self.message_user(request, _('Please select a least 2 mailing list.')) |
||||
return None |
||||
|
||||
subscribers = {} |
||||
unsubscribers = {} |
||||
for ml in queryset: |
||||
for contact in ml.subscribers.all(): |
||||
subscribers[contact] = '' |
||||
for contact in ml.unsubscribers.all(): |
||||
unsubscribers[contact] = '' |
||||
|
||||
when = str(datetime.now()).split('.')[0] |
||||
new_mailing = MailingList(name=_('Merging list at %s') % when, |
||||
description=_('Mailing list created by merging at %s') % when) |
||||
new_mailing.save() |
||||
new_mailing.subscribers = subscribers.keys() |
||||
new_mailing.unsubscribers = unsubscribers.keys() |
||||
|
||||
if not request.user.is_superuser and USE_WORKGROUPS: |
||||
for workgroup in request_workgroups(request): |
||||
workgroup.mailinglists.add(new_mailing) |
||||
|
||||
self.message_user(request, _('%s succesfully created by merging.') % new_mailing) |
||||
return HttpResponseRedirect(reverse('admin:newsletter_mailinglist_change', |
||||
args=[new_mailing.pk])) |
||||
merge_mailinglist.short_description = _('Merge selected mailinglists') |
||||
|
||||
def exportation_links(self, mailinglist): |
||||
"""Display links for exportation""" |
||||
return u'<a href="%s">%s</a> / <a href="%s">%s</a>' % ( |
||||
reverse('admin:newsletter_mailinglist_export_excel', |
||||
args=[mailinglist.pk]), _('Excel'), |
||||
reverse('admin:newsletter_mailinglist_export_vcard', |
||||
args=[mailinglist.pk]), _('VCard')) |
||||
exportation_links.allow_tags = True |
||||
exportation_links.short_description = _('Export') |
||||
|
||||
def exportion_vcard(self, request, mailinglist_id): |
||||
"""Export subscribers in the mailing in VCard""" |
||||
mailinglist = get_object_or_404(MailingList, pk=mailinglist_id) |
||||
name = 'contacts_%s' % smart_str(mailinglist.name) |
||||
return vcard_contacts_export_response(mailinglist.subscribers.all(), name) |
||||
|
||||
def exportion_excel(self, request, mailinglist_id): |
||||
"""Export subscribers in the mailing in Excel""" |
||||
mailinglist = get_object_or_404(MailingList, pk=mailinglist_id) |
||||
name = 'contacts_%s' % smart_str(mailinglist.name) |
||||
return ExcelResponse(mailinglist.subscribers.all(), name) |
||||
|
||||
def get_urls(self): |
||||
urls = super(MailingListAdmin, self).get_urls() |
||||
my_urls = patterns('', |
||||
url(r'^export/vcard/(?P<mailinglist_id>\d+)/$', |
||||
self.admin_site.admin_view(self.exportion_vcard), |
||||
name='newsletter_mailinglist_export_vcard'), |
||||
url(r'^export/excel/(?P<mailinglist_id>\d+)/$', |
||||
self.admin_site.admin_view(self.exportion_excel), |
||||
name='newsletter_mailinglist_export_excel')) |
||||
return my_urls + urls |
||||
@ -0,0 +1,185 @@ |
||||
"""ModelAdmin for Newsletter""" |
||||
from HTMLParser import HTMLParseError |
||||
|
||||
from django import forms |
||||
from django.db.models import Q |
||||
from django.contrib import admin |
||||
from django.utils.translation import ugettext_lazy as _ |
||||
|
||||
from emencia.django.newsletter.models import Contact |
||||
from emencia.django.newsletter.models import Newsletter |
||||
from emencia.django.newsletter.models import Attachment |
||||
from emencia.django.newsletter.models import MailingList |
||||
from emencia.django.newsletter.mailer import Mailer |
||||
from emencia.django.newsletter.settings import USE_TINYMCE |
||||
from emencia.django.newsletter.settings import USE_WORKGROUPS |
||||
try: |
||||
CAN_USE_PREMAILER = True |
||||
from emencia.django.newsletter.utils.premailer import Premailer |
||||
from emencia.django.newsletter.utils.premailer import PremailerError |
||||
except ImportError: |
||||
CAN_USE_PREMAILER = False |
||||
from emencia.django.newsletter.utils.workgroups import request_workgroups |
||||
from emencia.django.newsletter.utils.workgroups import request_workgroups_contacts_pk |
||||
from emencia.django.newsletter.utils.workgroups import request_workgroups_newsletters_pk |
||||
from emencia.django.newsletter.utils.workgroups import request_workgroups_mailinglists_pk |
||||
|
||||
|
||||
class AttachmentAdminInline(admin.TabularInline): |
||||
model = Attachment |
||||
extra = 1 |
||||
fieldsets = ((None, {'fields': (('title', 'file_attachment'))}),) |
||||
|
||||
|
||||
class BaseNewsletterAdmin(admin.ModelAdmin): |
||||
date_hierarchy = 'creation_date' |
||||
list_display = ('title', 'mailing_list', 'server', 'status', |
||||
'sending_date', 'creation_date', 'modification_date', |
||||
'historic_link', 'statistics_link') |
||||
list_filter = ('status', 'sending_date', 'creation_date', 'modification_date') |
||||
search_fields = ('title', 'content', 'header_sender', 'header_reply') |
||||
filter_horizontal = ['test_contacts'] |
||||
fieldsets = ((None, {'fields': ('title', 'content',)}), |
||||
(_('Receivers'), {'fields': ('mailing_list', 'test_contacts',)}), |
||||
(_('Sending'), {'fields': ('sending_date', 'status',)}), |
||||
(_('Miscellaneous'), {'fields': ('server', 'header_sender', |
||||
'header_reply', 'slug'), |
||||
'classes': ('collapse',)}), |
||||
) |
||||
prepopulated_fields = {'slug': ('title',)} |
||||
inlines = (AttachmentAdminInline,) |
||||
actions = ['send_mail_test', 'make_ready_to_send', 'make_cancel_sending'] |
||||
actions_on_top = False |
||||
actions_on_bottom = True |
||||
|
||||
def get_actions(self, request): |
||||
actions = super(BaseNewsletterAdmin, self).get_actions(request) |
||||
if not request.user.has_perm('newsletter.can_change_status'): |
||||
del actions['make_ready_to_send'] |
||||
del actions['make_cancel_sending'] |
||||
return actions |
||||
|
||||
def queryset(self, request): |
||||
queryset = super(BaseNewsletterAdmin, self).queryset(request) |
||||
if not request.user.is_superuser and USE_WORKGROUPS: |
||||
newsletters_pk = request_workgroups_newsletters_pk(request) |
||||
queryset = queryset.filter(pk__in=newsletters_pk) |
||||
return queryset |
||||
|
||||
def formfield_for_foreignkey(self, db_field, request, **kwargs): |
||||
if db_field.name == 'mailing_list' and \ |
||||
not request.user.is_superuser and USE_WORKGROUPS: |
||||
mailinglists_pk = request_workgroups_mailinglists_pk(request) |
||||
kwargs['queryset'] = MailingList.objects.filter(pk__in=mailinglists_pk) |
||||
return db_field.formfield(**kwargs) |
||||
return super(BaseNewsletterAdmin, self).formfield_for_foreignkey( |
||||
db_field, request, **kwargs) |
||||
|
||||
def formfield_for_choice_field(self, db_field, request, **kwargs): |
||||
if db_field.name == 'status' and \ |
||||
not request.user.has_perm('newsletter.can_change_status'): |
||||
kwargs['choices'] = ((Newsletter.DRAFT, _('Default')),) |
||||
return db_field.formfield(**kwargs) |
||||
return super(BaseNewsletterAdmin, self).formfield_for_choice_field( |
||||
db_field, request, **kwargs) |
||||
|
||||
def formfield_for_manytomany(self, db_field, request, **kwargs): |
||||
if db_field.name == 'test_contacts': |
||||
queryset = Contact.objects.filter(tester=True) |
||||
if not request.user.is_superuser and USE_WORKGROUPS: |
||||
contacts_pk = request_workgroups_contacts_pk(request) |
||||
queryset = queryset.filter(pk__in=contacts_pk) |
||||
kwargs['queryset'] = queryset |
||||
return super(BaseNewsletterAdmin, self).formfield_for_manytomany( |
||||
db_field, request, **kwargs) |
||||
|
||||
def save_model(self, request, newsletter, form, change): |
||||
workgroups = [] |
||||
if not newsletter.pk and not request.user.is_superuser \ |
||||
and USE_WORKGROUPS: |
||||
workgroups = request_workgroups(request) |
||||
|
||||
if newsletter.content.startswith('http://'): |
||||
if CAN_USE_PREMAILER: |
||||
try: |
||||
premailer = Premailer(newsletter.content.strip()) |
||||
newsletter.content = premailer.transform() |
||||
except PremailerError: |
||||
self.message_user(request, _('Unable to download HTML, due to errors within.')) |
||||
else: |
||||
self.message_user(request, _('Please install lxml for parsing an URL.')) |
||||
if not request.user.has_perm('newsletter.can_change_status'): |
||||
newsletter.status = form.initial.get('status', Newsletter.DRAFT) |
||||
|
||||
newsletter.save() |
||||
|
||||
for workgroup in workgroups: |
||||
workgroup.newsletters.add(newsletter) |
||||
|
||||
def historic_link(self, newsletter): |
||||
"""Display link for historic""" |
||||
if newsletter.contactmailingstatus_set.count(): |
||||
return u'<a href="%s">%s</a>' % (newsletter.get_historic_url(), _('View historic')) |
||||
return _('Not available') |
||||
historic_link.allow_tags = True |
||||
historic_link.short_description = _('Historic') |
||||
|
||||
def statistics_link(self, newsletter): |
||||
"""Display link for statistics""" |
||||
if newsletter.status == Newsletter.SENDING or \ |
||||
newsletter.status == Newsletter.SENT: |
||||
return u'<a href="%s">%s</a>' % (newsletter.get_statistics_url(), _('View statistics')) |
||||
return _('Not available') |
||||
statistics_link.allow_tags = True |
||||
statistics_link.short_description = _('Statistics') |
||||
|
||||
def send_mail_test(self, request, queryset): |
||||
"""Send newsletter in test""" |
||||
for newsletter in queryset: |
||||
if newsletter.test_contacts.count(): |
||||
mailer = Mailer(newsletter, test=True) |
||||
try: |
||||
mailer.run() |
||||
except HTMLParseError: |
||||
self.message_user(request, _('Unable send newsletter, due to errors within HTML.')) |
||||
continue |
||||
self.message_user(request, _('%s succesfully sent.') % newsletter) |
||||
else: |
||||
self.message_user(request, _('No test contacts assigned for %s.') % newsletter) |
||||
send_mail_test.short_description = _('Send test email') |
||||
|
||||
def make_ready_to_send(self, request, queryset): |
||||
"""Make newsletter ready to send""" |
||||
queryset = queryset.filter(status=Newsletter.DRAFT) |
||||
for newsletter in queryset: |
||||
newsletter.status = Newsletter.WAITING |
||||
newsletter.save() |
||||
self.message_user(request, _('%s newletters are ready to send') % queryset.count()) |
||||
make_ready_to_send.short_description = _('Make ready to send') |
||||
|
||||
def make_cancel_sending(self, request, queryset): |
||||
"""Cancel the sending of newsletters""" |
||||
queryset = queryset.filter(Q(status=Newsletter.WAITING) | |
||||
Q(status=Newsletter.SENDING)) |
||||
for newsletter in queryset: |
||||
newsletter.status = Newsletter.CANCELED |
||||
newsletter.save() |
||||
self.message_user(request, _('%s newletters are cancelled') % queryset.count()) |
||||
make_cancel_sending.short_description = _('Cancel the sending') |
||||
|
||||
|
||||
if USE_TINYMCE: |
||||
from tinymce.widgets import TinyMCE |
||||
|
||||
class NewsletterTinyMCEForm(forms.ModelForm): |
||||
content = forms.CharField( |
||||
widget=TinyMCE(attrs={'cols': 150, 'rows': 80})) |
||||
|
||||
class Meta: |
||||
model = Newsletter |
||||
|
||||
class NewsletterAdmin(BaseNewsletterAdmin): |
||||
form = NewsletterTinyMCEForm |
||||
else: |
||||
class NewsletterAdmin(BaseNewsletterAdmin): |
||||
pass |
||||
@ -0,0 +1,57 @@ |
||||
"""ModelAdmin for SMTPServer""" |
||||
from django import forms |
||||
from django.contrib import admin |
||||
from django.core.exceptions import ValidationError |
||||
from django.utils.translation import ugettext_lazy as _ |
||||
|
||||
from emencia.django.newsletter.models import SMTPServer |
||||
|
||||
|
||||
class SMTPServerAdminForm(forms.ModelForm): |
||||
"""Form ofr SMTPServer with custom validation""" |
||||
|
||||
def clean_headers(self): |
||||
"""Check if the headers are well formated""" |
||||
for line in self.cleaned_data['headers'].splitlines(): |
||||
elems = line.split(':') |
||||
if len(elems) < 2: |
||||
raise ValidationError(_('Invalid syntax, do not forget the ":".')) |
||||
if len(elems) > 2: |
||||
raise ValidationError(_('Invalid syntax, several assignments by line.')) |
||||
|
||||
return self.cleaned_data['headers'] |
||||
|
||||
class Meta: |
||||
model = SMTPServer |
||||
|
||||
|
||||
class SMTPServerAdmin(admin.ModelAdmin): |
||||
form = SMTPServerAdminForm |
||||
list_display = ('name', 'host', 'port', 'user', 'tls', 'mails_hour',) |
||||
list_filter = ('tls',) |
||||
search_fields = ('name', 'host', 'user') |
||||
fieldsets = ((None, {'fields': ('name', )}), |
||||
(_('Configuration'), {'fields': ('host', 'port', |
||||
'user', 'password', 'tls')}), |
||||
(_('Miscellaneous'), {'fields': ('mails_hour', 'headers'), |
||||
'classes': ('collapse', )}), |
||||
) |
||||
actions = ['check_connections'] |
||||
actions_on_top = False |
||||
actions_on_bottom = True |
||||
|
||||
def check_connections(self, request, queryset): |
||||
"""Check the SMTP connection""" |
||||
message = '%s connection %s' |
||||
for server in queryset: |
||||
try: |
||||
smtp = server.connect() |
||||
if smtp: |
||||
status = 'OK' |
||||
smtp.quit() |
||||
else: |
||||
status = 'KO' |
||||
except: |
||||
status = 'KO' |
||||
self.message_user(request, message % (server.__unicode__(), status)) |
||||
check_connections.short_description = _('Check connection') |
||||
@ -0,0 +1,26 @@ |
||||
"""ModelAdmin for WorkGroup""" |
||||
from django.contrib import admin |
||||
from django.utils.translation import ugettext_lazy as _ |
||||
|
||||
|
||||
class WorkGroupAdmin(admin.ModelAdmin): |
||||
list_display = ('name', 'group', 'contacts_length', |
||||
'mailinglists_length', 'newsletters_length') |
||||
fieldsets = ((None, {'fields': ('name', 'group')}), |
||||
(None, {'fields': ('contacts', 'mailinglists', 'newsletters')}), |
||||
) |
||||
filter_horizontal = ['contacts', 'mailinglists', 'newsletters'] |
||||
actions_on_top = False |
||||
actions_on_bottom = True |
||||
|
||||
def contacts_length(self, workgroup): |
||||
return workgroup.contacts.count() |
||||
contacts_length.short_description = _('Contacts length') |
||||
|
||||
def mailinglists_length(self, workgroup): |
||||
return workgroup.mailinglists.count() |
||||
mailinglists_length.short_description = _('Mailing List length') |
||||
|
||||
def newsletters_length(self, workgroup): |
||||
return workgroup.newsletters.count() |
||||
newsletters_length.short_description = _('Newsletter length') |
||||
@ -0,0 +1,38 @@ |
||||
"""Plugins for CMS""" |
||||
from django.utils.translation import ugettext_lazy as _ |
||||
|
||||
from cms.plugin_base import CMSPluginBase |
||||
from cms.plugin_pool import plugin_pool |
||||
|
||||
from emencia.django.newsletter.cmsplugin_newsletter import settings |
||||
from emencia.django.newsletter.cmsplugin_newsletter.models import SubscriptionFormPlugin |
||||
from emencia.django.newsletter.forms import MailingListSubscriptionForm |
||||
|
||||
|
||||
class CMSSubscriptionFormPlugin(CMSPluginBase): |
||||
module = _('newsletter') |
||||
model = SubscriptionFormPlugin |
||||
name = _('Subscription Form') |
||||
render_template = 'newsletter/cms/subscription_form.html' |
||||
text_enabled = False |
||||
admin_preview = False |
||||
|
||||
def render(self, context, instance, placeholder): |
||||
request = context['request'] |
||||
if request.method == "POST" and (settings.FORM_NAME in request.POST.keys()): |
||||
form = MailingListSubscriptionForm(data=request.POST) |
||||
if form.is_valid(): |
||||
form.save(instance.mailing_list) |
||||
form.saved = True |
||||
else: |
||||
form = MailingListSubscriptionForm() |
||||
context.update({ |
||||
'object': instance, |
||||
'form': form, |
||||
'form_name': settings.FORM_NAME, |
||||
'placeholder': placeholder, |
||||
}) |
||||
return context |
||||
|
||||
|
||||
plugin_pool.register_plugin(CMSSubscriptionFormPlugin) |
||||
@ -0,0 +1,19 @@ |
||||
"""Models of Emencia CMS Plugins""" |
||||
from django.db import models |
||||
from django.utils.translation import ugettext_lazy as _ |
||||
|
||||
from cms.models import CMSPlugin |
||||
|
||||
from emencia.django.newsletter.models import MailingList |
||||
|
||||
|
||||
class SubscriptionFormPlugin(CMSPlugin): |
||||
"""CMS Plugin for susbcribing to a mailing list""" |
||||
title = models.CharField(_('title'), max_length=100, blank=True) |
||||
show_description = models.BooleanField(_('show description'), default=True, |
||||
help_text=_('Show the mailing list\'s description.')) |
||||
mailing_list = models.ForeignKey(MailingList, verbose_name=_('mailing list'), |
||||
help_text=_('Mailing List to subscribe to.')) |
||||
|
||||
def __unicode__(self): |
||||
return self.mailing_list.name |
||||
@ -0,0 +1,4 @@ |
||||
"""Settings for emencia.django.newsletter.cmsplugin_newsletter""" |
||||
from django.conf import settings |
||||
|
||||
FORM_NAME = getattr(settings, 'SUBSCRIPTION_FORM_NAME', 'cms_subscription_form_plugin') |
||||
@ -0,0 +1,56 @@ |
||||
|
||||
"""Forms for emencia.django.newsletter""" |
||||
from django import forms |
||||
from django.utils.translation import ugettext_lazy as _ |
||||
|
||||
from emencia.django.newsletter.models import Contact |
||||
from emencia.django.newsletter.models import MailingList |
||||
|
||||
|
||||
class MailingListSubscriptionForm(forms.ModelForm): |
||||
"""Form for subscribing to a mailing list""" |
||||
# Notes : This form will not check the uniquess of |
||||
# the 'email' field, by defining it explictly and setting |
||||
# it the Meta.exclude list, for allowing registration |
||||
# to a mailing list even if the contact already exists. |
||||
# Then the contact is always added to the subscribers field |
||||
# of the mailing list because it will be cleaned with no |
||||
# double. |
||||
|
||||
email = forms.EmailField(label=_('Email'), max_length=75) |
||||
|
||||
def save(self, mailing_list): |
||||
data = self.cleaned_data |
||||
contact, created = Contact.objects.get_or_create( |
||||
email=data['email'], |
||||
defaults={'first_name': data['first_name'], |
||||
'last_name': data['last_name']}) |
||||
|
||||
mailing_list.subscribers.add(contact) |
||||
mailing_list.unsubscribers.remove(contact) |
||||
|
||||
class Meta: |
||||
model = Contact |
||||
fields = ('first_name', 'last_name') |
||||
exclude = ('email',) |
||||
|
||||
|
||||
class AllMailingListSubscriptionForm(MailingListSubscriptionForm): |
||||
"""Form for subscribing to all mailing list""" |
||||
|
||||
mailing_lists = forms.ModelMultipleChoiceField( |
||||
queryset=MailingList.objects.all(), |
||||
initial=[obj.id for obj in MailingList.objects.all()], |
||||
label=_('Mailing lists'), |
||||
widget=forms.CheckboxSelectMultiple()) |
||||
|
||||
def save(self, mailing_list): |
||||
data = self.cleaned_data |
||||
contact, created = Contact.objects.get_or_create( |
||||
email=data['email'], |
||||
defaults={'first_name': data['first_name'], |
||||
'last_name': data['last_name']}) |
||||
|
||||
for mailing_list in data['mailing_lists']: |
||||
mailing_list.subscribers.add(contact) |
||||
mailing_list.unsubscribers.remove(contact) |
||||
@ -1,3 +1,4 @@ |
||||
"""Managers for emencia.django.newsletter""" |
||||
from django.db import models |
||||
|
||||
|
||||
@ -0,0 +1,272 @@ |
||||
from south.db import db |
||||
from django.db import models |
||||
from emencia.django.newsletter.models import * |
||||
|
||||
|
||||
class Migration: |
||||
|
||||
def forwards(self, orm): |
||||
|
||||
# Adding model 'MailingList' |
||||
db.create_table('newsletter_mailinglist', ( |
||||
('id', orm['newsletter.MailingList:id']), |
||||
('name', orm['newsletter.MailingList:name']), |
||||
('description', orm['newsletter.MailingList:description']), |
||||
('creation_date', orm['newsletter.MailingList:creation_date']), |
||||
('modification_date', orm['newsletter.MailingList:modification_date']), |
||||
)) |
||||
db.send_create_signal('newsletter', ['MailingList']) |
||||
|
||||
# Adding model 'ContactMailingStatus' |
||||
db.create_table('newsletter_contactmailingstatus', ( |
||||
('id', orm['newsletter.ContactMailingStatus:id']), |
||||
('newsletter', orm['newsletter.ContactMailingStatus:newsletter']), |
||||
('contact', orm['newsletter.ContactMailingStatus:contact']), |
||||
('status', orm['newsletter.ContactMailingStatus:status']), |
||||
('link', orm['newsletter.ContactMailingStatus:link']), |
||||
('creation_date', orm['newsletter.ContactMailingStatus:creation_date']), |
||||
)) |
||||
db.send_create_signal('newsletter', ['ContactMailingStatus']) |
||||
|
||||
# Adding model 'WorkGroup' |
||||
db.create_table('newsletter_workgroup', ( |
||||
('id', orm['newsletter.WorkGroup:id']), |
||||
('name', orm['newsletter.WorkGroup:name']), |
||||
('group', orm['newsletter.WorkGroup:group']), |
||||
)) |
||||
db.send_create_signal('newsletter', ['WorkGroup']) |
||||
|
||||
# Adding model 'Link' |
||||
db.create_table('newsletter_link', ( |
||||
('id', orm['newsletter.Link:id']), |
||||
('title', orm['newsletter.Link:title']), |
||||
('url', orm['newsletter.Link:url']), |
||||
('creation_date', orm['newsletter.Link:creation_date']), |
||||
)) |
||||
db.send_create_signal('newsletter', ['Link']) |
||||
|
||||
# Adding model 'Newsletter' |
||||
db.create_table('newsletter_newsletter', ( |
||||
('id', orm['newsletter.Newsletter:id']), |
||||
('title', orm['newsletter.Newsletter:title']), |
||||
('content', orm['newsletter.Newsletter:content']), |
||||
('mailing_list', orm['newsletter.Newsletter:mailing_list']), |
||||
('server', orm['newsletter.Newsletter:server']), |
||||
('header_sender', orm['newsletter.Newsletter:header_sender']), |
||||
('header_reply', orm['newsletter.Newsletter:header_reply']), |
||||
('status', orm['newsletter.Newsletter:status']), |
||||
('sending_date', orm['newsletter.Newsletter:sending_date']), |
||||
('slug', orm['newsletter.Newsletter:slug']), |
||||
('creation_date', orm['newsletter.Newsletter:creation_date']), |
||||
('modification_date', orm['newsletter.Newsletter:modification_date']), |
||||
)) |
||||
db.send_create_signal('newsletter', ['Newsletter']) |
||||
|
||||
# Adding model 'SMTPServer' |
||||
db.create_table('newsletter_smtpserver', ( |
||||
('id', orm['newsletter.SMTPServer:id']), |
||||
('name', orm['newsletter.SMTPServer:name']), |
||||
('host', orm['newsletter.SMTPServer:host']), |
||||
('user', orm['newsletter.SMTPServer:user']), |
||||
('password', orm['newsletter.SMTPServer:password']), |
||||
('port', orm['newsletter.SMTPServer:port']), |
||||
('tls', orm['newsletter.SMTPServer:tls']), |
||||
('headers', orm['newsletter.SMTPServer:headers']), |
||||
('mails_hour', orm['newsletter.SMTPServer:mails_hour']), |
||||
)) |
||||
db.send_create_signal('newsletter', ['SMTPServer']) |
||||
|
||||
# Adding model 'Contact' |
||||
db.create_table('newsletter_contact', ( |
||||
('id', orm['newsletter.Contact:id']), |
||||
('email', orm['newsletter.Contact:email']), |
||||
('first_name', orm['newsletter.Contact:first_name']), |
||||
('last_name', orm['newsletter.Contact:last_name']), |
||||
('subscriber', orm['newsletter.Contact:subscriber']), |
||||
('valid', orm['newsletter.Contact:valid']), |
||||
('tester', orm['newsletter.Contact:tester']), |
||||
('tags', orm['newsletter.Contact:tags']), |
||||
('content_type', orm['newsletter.Contact:content_type']), |
||||
('object_id', orm['newsletter.Contact:object_id']), |
||||
('creation_date', orm['newsletter.Contact:creation_date']), |
||||
('modification_date', orm['newsletter.Contact:modification_date']), |
||||
)) |
||||
db.send_create_signal('newsletter', ['Contact']) |
||||
|
||||
# Adding ManyToManyField 'WorkGroup.mailinglists' |
||||
db.create_table('newsletter_workgroup_mailinglists', ( |
||||
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), |
||||
('workgroup', models.ForeignKey(orm.WorkGroup, null=False)), |
||||
('mailinglist', models.ForeignKey(orm.MailingList, null=False)) |
||||
)) |
||||
|
||||
# Adding ManyToManyField 'MailingList.subscribers' |
||||
db.create_table('newsletter_mailinglist_subscribers', ( |
||||
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), |
||||
('mailinglist', models.ForeignKey(orm.MailingList, null=False)), |
||||
('contact', models.ForeignKey(orm.Contact, null=False)) |
||||
)) |
||||
|
||||
# Adding ManyToManyField 'WorkGroup.contacts' |
||||
db.create_table('newsletter_workgroup_contacts', ( |
||||
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), |
||||
('workgroup', models.ForeignKey(orm.WorkGroup, null=False)), |
||||
('contact', models.ForeignKey(orm.Contact, null=False)) |
||||
)) |
||||
|
||||
# Adding ManyToManyField 'WorkGroup.newsletters' |
||||
db.create_table('newsletter_workgroup_newsletters', ( |
||||
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), |
||||
('workgroup', models.ForeignKey(orm.WorkGroup, null=False)), |
||||
('newsletter', models.ForeignKey(orm.Newsletter, null=False)) |
||||
)) |
||||
|
||||
# Adding ManyToManyField 'MailingList.unsubscribers' |
||||
db.create_table('newsletter_mailinglist_unsubscribers', ( |
||||
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), |
||||
('mailinglist', models.ForeignKey(orm.MailingList, null=False)), |
||||
('contact', models.ForeignKey(orm.Contact, null=False)) |
||||
)) |
||||
|
||||
# Adding ManyToManyField 'Newsletter.test_contacts' |
||||
db.create_table('newsletter_newsletter_test_contacts', ( |
||||
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), |
||||
('newsletter', models.ForeignKey(orm.Newsletter, null=False)), |
||||
('contact', models.ForeignKey(orm.Contact, null=False)) |
||||
)) |
||||
|
||||
def backwards(self, orm): |
||||
|
||||
# Deleting model 'MailingList' |
||||
db.delete_table('newsletter_mailinglist') |
||||
|
||||
# Deleting model 'ContactMailingStatus' |
||||
db.delete_table('newsletter_contactmailingstatus') |
||||
|
||||
# Deleting model 'WorkGroup' |
||||
db.delete_table('newsletter_workgroup') |
||||
|
||||
# Deleting model 'Link' |
||||
db.delete_table('newsletter_link') |
||||
|
||||
# Deleting model 'Newsletter' |
||||
db.delete_table('newsletter_newsletter') |
||||
|
||||
# Deleting model 'SMTPServer' |
||||
db.delete_table('newsletter_smtpserver') |
||||
|
||||
# Deleting model 'Contact' |
||||
db.delete_table('newsletter_contact') |
||||
|
||||
# Dropping ManyToManyField 'WorkGroup.mailinglists' |
||||
db.delete_table('newsletter_workgroup_mailinglists') |
||||
|
||||
# Dropping ManyToManyField 'MailingList.subscribers' |
||||
db.delete_table('newsletter_mailinglist_subscribers') |
||||
|
||||
# Dropping ManyToManyField 'WorkGroup.contacts' |
||||
db.delete_table('newsletter_workgroup_contacts') |
||||
|
||||
# Dropping ManyToManyField 'WorkGroup.newsletters' |
||||
db.delete_table('newsletter_workgroup_newsletters') |
||||
|
||||
# Dropping ManyToManyField 'MailingList.unsubscribers' |
||||
db.delete_table('newsletter_mailinglist_unsubscribers') |
||||
|
||||
# Dropping ManyToManyField 'Newsletter.test_contacts' |
||||
db.delete_table('newsletter_newsletter_test_contacts') |
||||
|
||||
models = { |
||||
'auth.group': { |
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
||||
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), |
||||
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'blank': 'True'}) |
||||
}, |
||||
'auth.permission': { |
||||
'Meta': {'unique_together': "(('content_type', 'codename'),)"}, |
||||
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
||||
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), |
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) |
||||
}, |
||||
'contenttypes.contenttype': { |
||||
'Meta': {'unique_together': "(('app_label', 'model'),)", 'db_table': "'django_content_type'"}, |
||||
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
||||
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) |
||||
}, |
||||
'newsletter.contact': { |
||||
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']", 'null': 'True', 'blank': 'True'}), |
||||
'creation_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), |
||||
'email': ('django.db.models.fields.EmailField', [], {'unique': 'True', 'max_length': '75'}), |
||||
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), |
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
||||
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), |
||||
'modification_date': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), |
||||
'object_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), |
||||
'subscriber': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), |
||||
'tags': ('tagging.fields.TagField', [], {'default': "''"}), |
||||
'tester': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), |
||||
'valid': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}) |
||||
}, |
||||
'newsletter.contactmailingstatus': { |
||||
'contact': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['newsletter.Contact']"}), |
||||
'creation_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), |
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
||||
'link': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['newsletter.Link']", 'null': 'True', 'blank': 'True'}), |
||||
'newsletter': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['newsletter.Newsletter']"}), |
||||
'status': ('django.db.models.fields.IntegerField', [], {}) |
||||
}, |
||||
'newsletter.link': { |
||||
'creation_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), |
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
||||
'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
||||
'url': ('django.db.models.fields.CharField', [], {'max_length': '255'}) |
||||
}, |
||||
'newsletter.mailinglist': { |
||||
'creation_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), |
||||
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), |
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
||||
'modification_date': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), |
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
||||
'subscribers': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['newsletter.Contact']"}), |
||||
'unsubscribers': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['newsletter.Contact']", 'null': 'True', 'blank': 'True'}) |
||||
}, |
||||
'newsletter.newsletter': { |
||||
'content': ('django.db.models.fields.TextField', [], {'default': "u'<body>\\n<!-- Edit your newsletter here -->\\n</body>'"}), |
||||
'creation_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), |
||||
'header_reply': ('django.db.models.fields.CharField', [], {'default': "'Emencia Newsletter<noreply@emencia.com>'", 'max_length': '255'}), |
||||
'header_sender': ('django.db.models.fields.CharField', [], {'default': "'Emencia Newsletter<noreply@emencia.com>'", 'max_length': '255'}), |
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
||||
'mailing_list': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['newsletter.MailingList']"}), |
||||
'modification_date': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), |
||||
'sending_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), |
||||
'server': ('django.db.models.fields.related.ForeignKey', [], {'default': '1', 'to': "orm['newsletter.SMTPServer']"}), |
||||
'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'}), |
||||
'status': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
||||
'test_contacts': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['newsletter.Contact']", 'null': 'True', 'blank': 'True'}), |
||||
'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}) |
||||
}, |
||||
'newsletter.smtpserver': { |
||||
'headers': ('django.db.models.fields.TextField', [], {'blank': 'True'}), |
||||
'host': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
||||
'mails_hour': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
||||
'password': ('django.db.models.fields.CharField', [], {'max_length': '128', 'blank': 'True'}), |
||||
'port': ('django.db.models.fields.IntegerField', [], {'default': '25'}), |
||||
'tls': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), |
||||
'user': ('django.db.models.fields.CharField', [], {'max_length': '128', 'blank': 'True'}) |
||||
}, |
||||
'newsletter.workgroup': { |
||||
'contacts': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['newsletter.Contact']", 'null': 'True', 'blank': 'True'}), |
||||
'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.Group']"}), |
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
||||
'mailinglists': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['newsletter.MailingList']", 'null': 'True', 'blank': 'True'}), |
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
||||
'newsletters': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['newsletter.Newsletter']", 'null': 'True', 'blank': 'True'}) |
||||
} |
||||
} |
||||
|
||||
complete_apps = ['newsletter'] |
||||
@ -0,0 +1,130 @@ |
||||
from south.db import db |
||||
from south.v2 import SchemaMigration |
||||
|
||||
|
||||
class Migration(SchemaMigration): |
||||
|
||||
def forwards(self, orm): |
||||
|
||||
# Adding model 'Attachment' |
||||
db.create_table('newsletter_attachment', ( |
||||
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), |
||||
('newsletter', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['newsletter.Newsletter'])), |
||||
('title', self.gf('django.db.models.fields.CharField')(max_length=255)), |
||||
('file_attachment', self.gf('django.db.models.fields.files.FileField')(max_length=100)), |
||||
)) |
||||
db.send_create_signal('newsletter', ['Attachment']) |
||||
|
||||
def backwards(self, orm): |
||||
# Deleting model 'Attachment' |
||||
db.delete_table('newsletter_attachment') |
||||
|
||||
models = { |
||||
'auth.group': { |
||||
'Meta': {'object_name': 'Group'}, |
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
||||
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), |
||||
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) |
||||
}, |
||||
'auth.permission': { |
||||
'Meta': {'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, |
||||
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
||||
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), |
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) |
||||
}, |
||||
'contenttypes.contenttype': { |
||||
'Meta': {'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, |
||||
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
||||
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) |
||||
}, |
||||
'newsletter.attachment': { |
||||
'Meta': {'object_name': 'Attachment'}, |
||||
'file_attachment': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}), |
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
||||
'newsletter': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['newsletter.Newsletter']"}), |
||||
'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}) |
||||
}, |
||||
'newsletter.contact': { |
||||
'Meta': {'object_name': 'Contact'}, |
||||
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']", 'null': 'True', 'blank': 'True'}), |
||||
'creation_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), |
||||
'email': ('django.db.models.fields.EmailField', [], {'unique': 'True', 'max_length': '75'}), |
||||
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), |
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
||||
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), |
||||
'modification_date': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), |
||||
'object_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), |
||||
'subscriber': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), |
||||
'tags': ('tagging.fields.TagField', [], {'default': "''"}), |
||||
'tester': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), |
||||
'valid': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}) |
||||
}, |
||||
'newsletter.contactmailingstatus': { |
||||
'Meta': {'object_name': 'ContactMailingStatus'}, |
||||
'contact': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['newsletter.Contact']"}), |
||||
'creation_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), |
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
||||
'link': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['newsletter.Link']", 'null': 'True', 'blank': 'True'}), |
||||
'newsletter': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['newsletter.Newsletter']"}), |
||||
'status': ('django.db.models.fields.IntegerField', [], {}) |
||||
}, |
||||
'newsletter.link': { |
||||
'Meta': {'object_name': 'Link'}, |
||||
'creation_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), |
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
||||
'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
||||
'url': ('django.db.models.fields.CharField', [], {'max_length': '255'}) |
||||
}, |
||||
'newsletter.mailinglist': { |
||||
'Meta': {'object_name': 'MailingList'}, |
||||
'creation_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), |
||||
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), |
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
||||
'modification_date': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), |
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
||||
'subscribers': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'mailinglist_subscriber'", 'symmetrical': 'False', 'to': "orm['newsletter.Contact']"}), |
||||
'unsubscribers': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'mailinglist_unsubscriber'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['newsletter.Contact']"}) |
||||
}, |
||||
'newsletter.newsletter': { |
||||
'Meta': {'object_name': 'Newsletter'}, |
||||
'content': ('django.db.models.fields.TextField', [], {'default': "u'<body>\\n<!-- Edit your newsletter here -->\\n</body>'"}), |
||||
'creation_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), |
||||
'header_reply': ('django.db.models.fields.CharField', [], {'default': "'Giorgio Barbarotta Newsletter<noreply@giorgiobarbarotta.it>'", 'max_length': '255'}), |
||||
'header_sender': ('django.db.models.fields.CharField', [], {'default': "'Giorgio Barbarotta Newsletter<noreply@giorgiobarbarotta.it>'", 'max_length': '255'}), |
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
||||
'mailing_list': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['newsletter.MailingList']"}), |
||||
'modification_date': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), |
||||
'sending_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), |
||||
'server': ('django.db.models.fields.related.ForeignKey', [], {'default': '1', 'to': "orm['newsletter.SMTPServer']"}), |
||||
'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'}), |
||||
'status': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
||||
'test_contacts': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['newsletter.Contact']", 'null': 'True', 'blank': 'True'}), |
||||
'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}) |
||||
}, |
||||
'newsletter.smtpserver': { |
||||
'Meta': {'object_name': 'SMTPServer'}, |
||||
'headers': ('django.db.models.fields.TextField', [], {'blank': 'True'}), |
||||
'host': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
||||
'mails_hour': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
||||
'password': ('django.db.models.fields.CharField', [], {'max_length': '128', 'blank': 'True'}), |
||||
'port': ('django.db.models.fields.IntegerField', [], {'default': '25'}), |
||||
'tls': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), |
||||
'user': ('django.db.models.fields.CharField', [], {'max_length': '128', 'blank': 'True'}) |
||||
}, |
||||
'newsletter.workgroup': { |
||||
'Meta': {'object_name': 'WorkGroup'}, |
||||
'contacts': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['newsletter.Contact']", 'null': 'True', 'blank': 'True'}), |
||||
'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.Group']"}), |
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
||||
'mailinglists': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['newsletter.MailingList']", 'null': 'True', 'blank': 'True'}), |
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
||||
'newsletters': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['newsletter.Newsletter']", 'null': 'True', 'blank': 'True'}) |
||||
} |
||||
} |
||||
|
||||
complete_apps = ['newsletter'] |
||||
@ -0,0 +1,123 @@ |
||||
from south.db import db |
||||
from south.v2 import SchemaMigration |
||||
|
||||
|
||||
class Migration(SchemaMigration): |
||||
|
||||
def forwards(self, orm): |
||||
# Adding unique constraint on 'Newsletter', fields ['slug'] |
||||
db.create_unique('newsletter_newsletter', ['slug']) |
||||
|
||||
def backwards(self, orm): |
||||
# Removing unique constraint on 'Newsletter', fields ['slug'] |
||||
db.delete_unique('newsletter_newsletter', ['slug']) |
||||
|
||||
models = { |
||||
'auth.group': { |
||||
'Meta': {'object_name': 'Group'}, |
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
||||
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), |
||||
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) |
||||
}, |
||||
'auth.permission': { |
||||
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, |
||||
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
||||
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), |
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) |
||||
}, |
||||
'contenttypes.contenttype': { |
||||
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, |
||||
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
||||
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) |
||||
}, |
||||
'newsletter.attachment': { |
||||
'Meta': {'object_name': 'Attachment'}, |
||||
'file_attachment': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}), |
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
||||
'newsletter': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['newsletter.Newsletter']"}), |
||||
'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}) |
||||
}, |
||||
'newsletter.contact': { |
||||
'Meta': {'ordering': "('creation_date',)", 'object_name': 'Contact'}, |
||||
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']", 'null': 'True', 'blank': 'True'}), |
||||
'creation_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), |
||||
'email': ('django.db.models.fields.EmailField', [], {'unique': 'True', 'max_length': '75'}), |
||||
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), |
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
||||
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), |
||||
'modification_date': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), |
||||
'object_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), |
||||
'subscriber': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), |
||||
'tags': ('tagging.fields.TagField', [], {'default': "''"}), |
||||
'tester': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
||||
'valid': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) |
||||
}, |
||||
'newsletter.contactmailingstatus': { |
||||
'Meta': {'ordering': "('creation_date',)", 'object_name': 'ContactMailingStatus'}, |
||||
'contact': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['newsletter.Contact']"}), |
||||
'creation_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), |
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
||||
'link': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['newsletter.Link']", 'null': 'True', 'blank': 'True'}), |
||||
'newsletter': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['newsletter.Newsletter']"}), |
||||
'status': ('django.db.models.fields.IntegerField', [], {}) |
||||
}, |
||||
'newsletter.link': { |
||||
'Meta': {'ordering': "('creation_date',)", 'object_name': 'Link'}, |
||||
'creation_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), |
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
||||
'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
||||
'url': ('django.db.models.fields.CharField', [], {'max_length': '255'}) |
||||
}, |
||||
'newsletter.mailinglist': { |
||||
'Meta': {'ordering': "('creation_date',)", 'object_name': 'MailingList'}, |
||||
'creation_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), |
||||
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), |
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
||||
'modification_date': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), |
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
||||
'subscribers': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'mailinglist_subscriber'", 'symmetrical': 'False', 'to': "orm['newsletter.Contact']"}), |
||||
'unsubscribers': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'mailinglist_unsubscriber'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['newsletter.Contact']"}) |
||||
}, |
||||
'newsletter.newsletter': { |
||||
'Meta': {'ordering': "('creation_date',)", 'object_name': 'Newsletter'}, |
||||
'content': ('django.db.models.fields.TextField', [], {'default': "u'<body>\\n<!-- Edit your newsletter here -->\\n</body>'"}), |
||||
'creation_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), |
||||
'header_reply': ('django.db.models.fields.CharField', [], {'default': "'Emencia Newsletter<noreply@emencia.com>'", 'max_length': '255'}), |
||||
'header_sender': ('django.db.models.fields.CharField', [], {'default': "'Emencia Newsletter<noreply@emencia.com>'", 'max_length': '255'}), |
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
||||
'mailing_list': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['newsletter.MailingList']"}), |
||||
'modification_date': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), |
||||
'sending_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), |
||||
'server': ('django.db.models.fields.related.ForeignKey', [], {'default': '1', 'to': "orm['newsletter.SMTPServer']"}), |
||||
'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50', 'db_index': 'True'}), |
||||
'status': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
||||
'test_contacts': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['newsletter.Contact']", 'null': 'True', 'blank': 'True'}), |
||||
'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}) |
||||
}, |
||||
'newsletter.smtpserver': { |
||||
'Meta': {'object_name': 'SMTPServer'}, |
||||
'headers': ('django.db.models.fields.TextField', [], {'blank': 'True'}), |
||||
'host': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
||||
'mails_hour': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
||||
'password': ('django.db.models.fields.CharField', [], {'max_length': '128', 'blank': 'True'}), |
||||
'port': ('django.db.models.fields.IntegerField', [], {'default': '25'}), |
||||
'tls': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
||||
'user': ('django.db.models.fields.CharField', [], {'max_length': '128', 'blank': 'True'}) |
||||
}, |
||||
'newsletter.workgroup': { |
||||
'Meta': {'object_name': 'WorkGroup'}, |
||||
'contacts': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['newsletter.Contact']", 'null': 'True', 'blank': 'True'}), |
||||
'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.Group']"}), |
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
||||
'mailinglists': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['newsletter.MailingList']", 'null': 'True', 'blank': 'True'}), |
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
||||
'newsletters': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['newsletter.Newsletter']", 'null': 'True', 'blank': 'True'}) |
||||
} |
||||
} |
||||
|
||||
complete_apps = ['newsletter'] |
||||
@ -0,0 +1,660 @@ |
||||
/* |
||||
* jQuery Form Plugin |
||||
* version: 2.36 (07-NOV-2009) |
||||
* @requires jQuery v1.2.6 or later |
||||
* |
||||
* Examples and documentation at: http://malsup.com/jquery/form/
|
||||
* Dual licensed under the MIT and GPL licenses: |
||||
* http://www.opensource.org/licenses/mit-license.php
|
||||
* http://www.gnu.org/licenses/gpl.html
|
||||
*/ |
||||
;(function($) { |
||||
|
||||
/* |
||||
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 |
||||
to bind your own submit handler to the form. For example, |
||||
|
||||
$(document).ready(function() { |
||||
$('#myForm').bind('submit', function() { |
||||
$(this).ajaxSubmit({ |
||||
target: '#output' |
||||
}); |
||||
return false; // <-- important!
|
||||
}); |
||||
}); |
||||
|
||||
Use ajaxForm when you want the plugin to manage all the event binding |
||||
for you. For example, |
||||
|
||||
$(document).ready(function() { |
||||
$('#myForm').ajaxForm({ |
||||
target: '#output' |
||||
}); |
||||
}); |
||||
|
||||
When using ajaxForm, the ajaxSubmit function will be invoked for you |
||||
at the appropriate time. |
||||
*/ |
||||
|
||||
/** |
||||
* ajaxSubmit() provides a mechanism for immediately submitting |
||||
* an HTML form using AJAX. |
||||
*/ |
||||
$.fn.ajaxSubmit = function(options) { |
||||
// fast fail if nothing selected (http://dev.jquery.com/ticket/2752)
|
||||
if (!this.length) { |
||||
log('ajaxSubmit: skipping submit process - no element selected'); |
||||
return this; |
||||
} |
||||
|
||||
if (typeof options == 'function') |
||||
options = { success: options }; |
||||
|
||||
var url = $.trim(this.attr('action')); |
||||
if (url) { |
||||
// clean url (don't include hash vaue)
|
||||
url = (url.match(/^([^#]+)/)||[])[1]; |
||||
} |
||||
url = url || window.location.href || ''; |
||||
|
||||
options = $.extend({ |
||||
url: url, |
||||
type: this.attr('method') || 'GET', |
||||
iframeSrc: /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank' |
||||
}, options || {}); |
||||
|
||||
// hook for manipulating the form data before it is extracted;
|
||||
// convenient for use with rich editors like tinyMCE or FCKEditor
|
||||
var veto = {}; |
||||
this.trigger('form-pre-serialize', [this, options, veto]); |
||||
if (veto.veto) { |
||||
log('ajaxSubmit: submit vetoed via form-pre-serialize trigger'); |
||||
return this; |
||||
} |
||||
|
||||
// provide opportunity to alter form data before it is serialized
|
||||
if (options.beforeSerialize && options.beforeSerialize(this, options) === false) { |
||||
log('ajaxSubmit: submit aborted via beforeSerialize callback'); |
||||
return this; |
||||
} |
||||
|
||||
var a = this.formToArray(options.semantic); |
||||
if (options.data) { |
||||
options.extraData = options.data; |
||||
for (var n in options.data) { |
||||
if(options.data[n] instanceof Array) { |
||||
for (var k in options.data[n]) |
||||
a.push( { name: n, value: options.data[n][k] } ); |
||||
} |
||||
else |
||||
a.push( { name: n, value: options.data[n] } ); |
||||
} |
||||
} |
||||
|
||||
// give pre-submit callback an opportunity to abort the submit
|
||||
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); |
||||
|
||||
if (options.type.toUpperCase() == 'GET') { |
||||
options.url += (options.url.indexOf('?') >= 0 ? '&' : '?') + q; |
||||
options.data = null; // data is null for 'get'
|
||||
} |
||||
else |
||||
options.data = q; // data is the query string for 'post'
|
||||
|
||||
var $form = this, callbacks = []; |
||||
if (options.resetForm) callbacks.push(function() { $form.resetForm(); }); |
||||
if (options.clearForm) callbacks.push(function() { $form.clearForm(); }); |
||||
|
||||
// perform a load on the target only if dataType is not provided
|
||||
if (!options.dataType && options.target) { |
||||
var oldSuccess = options.success || function(){}; |
||||
callbacks.push(function(data) { |
||||
$(options.target).html(data).each(oldSuccess, arguments); |
||||
}); |
||||
} |
||||
else if (options.success) |
||||
callbacks.push(options.success); |
||||
|
||||
options.success = function(data, status) { |
||||
for (var i=0, max=callbacks.length; i < max; i++) |
||||
callbacks[i].apply(options, [data, status, $form]); |
||||
}; |
||||
|
||||
// are there files to upload?
|
||||
var files = $('input:file', this).fieldValue(); |
||||
var found = false; |
||||
for (var j=0; j < files.length; j++) |
||||
if (files[j]) |
||||
found = true; |
||||
|
||||
var multipart = false; |
||||
// var mp = 'multipart/form-data';
|
||||
// multipart = ($form.attr('enctype') == mp || $form.attr('encoding') == mp);
|
||||
|
||||
// options.iframe allows user to force iframe mode
|
||||
// 06-NOV-09: now defaulting to iframe mode if file input is detected
|
||||
if ((files.length && options.iframe !== false) || options.iframe || found || multipart) { |
||||
// hack to fix Safari hang (thanks to Tim Molendijk for this)
|
||||
// see: http://groups.google.com/group/jquery-dev/browse_thread/thread/36395b7ab510dd5d
|
||||
if (options.closeKeepAlive) |
||||
$.get(options.closeKeepAlive, fileUpload); |
||||
else |
||||
fileUpload(); |
||||
} |
||||
else |
||||
$.ajax(options); |
||||
|
||||
// fire 'notify' event
|
||||
this.trigger('form-submit-notify', [this, options]); |
||||
return this; |
||||
|
||||
|
||||
// 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 s = $.extend(true, {}, $.extend(true, {}, $.ajaxSettings), opts); |
||||
|
||||
var id = 'jqFormIO' + (new Date().getTime()); |
||||
var $io = $('<iframe id="' + id + '" name="' + id + '" src="'+ opts.iframeSrc +'" />'); |
||||
var io = $io[0]; |
||||
|
||||
$io.css({ position: 'absolute', top: '-1000px', left: '-1000px' }); |
||||
|
||||
var xhr = { // mock object
|
||||
aborted: 0, |
||||
responseText: null, |
||||
responseXML: null, |
||||
status: 0, |
||||
statusText: 'n/a', |
||||
getAllResponseHeaders: function() {}, |
||||
getResponseHeader: function() {}, |
||||
setRequestHeader: function() {}, |
||||
abort: function() { |
||||
this.aborted = 1; |
||||
$io.attr('src', opts.iframeSrc); // abort op in progress
|
||||
} |
||||
}; |
||||
|
||||
var g = opts.global; |
||||
// trigger ajax global events so that activity/block indicators work like normal
|
||||
if (g && ! $.active++) $.event.trigger("ajaxStart"); |
||||
if (g) $.event.trigger("ajaxSend", [xhr, opts]); |
||||
|
||||
if (s.beforeSend && s.beforeSend(xhr, s) === false) { |
||||
s.global && $.active--; |
||||
return; |
||||
} |
||||
if (xhr.aborted) |
||||
return; |
||||
|
||||
var cbInvoked = 0; |
||||
var timedOut = 0; |
||||
|
||||
// add submitting element to data if we know it
|
||||
var sub = form.clk; |
||||
if (sub) { |
||||
var n = sub.name; |
||||
if (n && !sub.disabled) { |
||||
options.extraData = options.extraData || {}; |
||||
options.extraData[n] = sub.value; |
||||
if (sub.type == "image") { |
||||
options.extraData[name+'.x'] = form.clk_x; |
||||
options.extraData[name+'.y'] = form.clk_y; |
||||
} |
||||
} |
||||
} |
||||
|
||||
// take a breath so that pending repaints get some cpu time before the upload starts
|
||||
setTimeout(function() { |
||||
// make sure form attrs are set
|
||||
var t = $form.attr('target'), a = $form.attr('action'); |
||||
|
||||
// update form attrs in IE friendly way
|
||||
form.setAttribute('target',id); |
||||
if (form.getAttribute('method') != 'POST') |
||||
form.setAttribute('method', 'POST'); |
||||
if (form.getAttribute('action') != opts.url) |
||||
form.setAttribute('action', opts.url); |
||||
|
||||
// ie borks in some cases when setting encoding
|
||||
if (! options.skipEncodingOverride) { |
||||
$form.attr({ |
||||
encoding: 'multipart/form-data', |
||||
enctype: 'multipart/form-data' |
||||
}); |
||||
} |
||||
|
||||
// support timout
|
||||
if (opts.timeout) |
||||
setTimeout(function() { timedOut = true; cb(); }, opts.timeout); |
||||
|
||||
// add "extra" data to form if provided in options
|
||||
var extraInputs = []; |
||||
try { |
||||
if (options.extraData) |
||||
for (var n in options.extraData) |
||||
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); |
||||
form.submit(); |
||||
} |
||||
finally { |
||||
// reset attrs and remove "extra" input elements
|
||||
form.setAttribute('action',a); |
||||
t ? form.setAttribute('target', t) : $form.removeAttr('target'); |
||||
$(extraInputs).remove(); |
||||
} |
||||
}, 10); |
||||
|
||||
var domCheckCount = 50; |
||||
|
||||
function cb() { |
||||
if (cbInvoked++) return; |
||||
|
||||
io.detachEvent ? io.detachEvent('onload', cb) : io.removeEventListener('load', cb, false); |
||||
|
||||
var ok = true; |
||||
try { |
||||
if (timedOut) throw 'timeout'; |
||||
// extract the server response from the iframe
|
||||
var data, doc; |
||||
|
||||
doc = io.contentWindow ? io.contentWindow.document : io.contentDocument ? io.contentDocument : io.document; |
||||
|
||||
var isXml = opts.dataType == 'xml' || doc.XMLDocument || $.isXMLDoc(doc); |
||||
log('isXml='+isXml); |
||||
if (!isXml && (doc.body == null || doc.body.innerHTML == '')) { |
||||
if (--domCheckCount) { |
||||
// in some browsers (Opera) the iframe DOM is not always traversable when
|
||||
// the onload callback fires, so we loop a bit to accommodate
|
||||
cbInvoked = 0; |
||||
setTimeout(cb, 100); |
||||
return; |
||||
} |
||||
log('Could not access iframe DOM after 50 tries.'); |
||||
return; |
||||
} |
||||
|
||||
xhr.responseText = doc.body ? doc.body.innerHTML : null; |
||||
xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc; |
||||
xhr.getResponseHeader = function(header){ |
||||
var headers = {'content-type': opts.dataType}; |
||||
return headers[header]; |
||||
}; |
||||
|
||||
if (opts.dataType == 'json' || opts.dataType == 'script') { |
||||
// see if user embedded response in textarea
|
||||
var ta = doc.getElementsByTagName('textarea')[0]; |
||||
if (ta) |
||||
xhr.responseText = ta.value; |
||||
else { |
||||
// account for browsers injecting pre around json response
|
||||
var pre = doc.getElementsByTagName('pre')[0]; |
||||
if (pre) |
||||
xhr.responseText = pre.innerHTML; |
||||
}
|
||||
} |
||||
else if (opts.dataType == 'xml' && !xhr.responseXML && xhr.responseText != null) { |
||||
xhr.responseXML = toXml(xhr.responseText); |
||||
} |
||||
data = $.httpData(xhr, opts.dataType); |
||||
} |
||||
catch(e){ |
||||
ok = false; |
||||
$.handleError(opts, xhr, 'error', e); |
||||
} |
||||
|
||||
// ordering of these callbacks/triggers is odd, but that's how $.ajax does it
|
||||
if (ok) { |
||||
opts.success(data, 'success'); |
||||
if (g) $.event.trigger("ajaxSuccess", [xhr, opts]); |
||||
} |
||||
if (g) $.event.trigger("ajaxComplete", [xhr, opts]); |
||||
if (g && ! --$.active) $.event.trigger("ajaxStop"); |
||||
if (opts.complete) opts.complete(xhr, ok ? 'success' : 'error'); |
||||
|
||||
// clean up
|
||||
setTimeout(function() { |
||||
$io.remove(); |
||||
xhr.responseXML = null; |
||||
}, 100); |
||||
}; |
||||
|
||||
function toXml(s, doc) { |
||||
if (window.ActiveXObject) { |
||||
doc = new ActiveXObject('Microsoft.XMLDOM'); |
||||
doc.async = 'false'; |
||||
doc.loadXML(s); |
||||
} |
||||
else |
||||
doc = (new DOMParser()).parseFromString(s, 'text/xml'); |
||||
return (doc && doc.documentElement && doc.documentElement.tagName != 'parsererror') ? doc : null; |
||||
}; |
||||
}; |
||||
}; |
||||
|
||||
/** |
||||
* ajaxForm() provides a mechanism for fully automating form submission. |
||||
* |
||||
* The advantages of using this method instead of ajaxSubmit() are: |
||||
* |
||||
* 1: This method will include coordinates for <input type="image" /> elements (if the element |
||||
* is used to submit the form). |
||||
* 2. This method will include the submit element's name/value data (for the element that was |
||||
* used to submit the form). |
||||
* 3. This method binds the submit() method to the form for you. |
||||
* |
||||
* 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); |
||||
return false; |
||||
}).bind('click.form-plugin', function(e) { |
||||
var target = e.target; |
||||
var $el = $(target); |
||||
if (!($el.is(":submit,input:image"))) { |
||||
// is this a child element of the submit el? (ex: a span within a button)
|
||||
var t = $el.closest(':submit'); |
||||
if (t.length == 0) |
||||
return; |
||||
target = t[0]; |
||||
} |
||||
var form = this; |
||||
form.clk = target; |
||||
if (target.type == 'image') { |
||||
if (e.offsetX != undefined) { |
||||
form.clk_x = e.offsetX; |
||||
form.clk_y = e.offsetY; |
||||
} else if (typeof $.fn.offset == 'function') { // try to use dimensions plugin
|
||||
var offset = $el.offset(); |
||||
form.clk_x = e.pageX - offset.left; |
||||
form.clk_y = e.pageY - offset.top; |
||||
} else { |
||||
form.clk_x = e.pageX - target.offsetLeft; |
||||
form.clk_y = e.pageY - target.offsetTop; |
||||
} |
||||
} |
||||
// clear form vars
|
||||
setTimeout(function() { form.clk = form.clk_x = form.clk_y = null; }, 100); |
||||
}); |
||||
}; |
||||
|
||||
// ajaxFormUnbind unbinds the event handlers that were bound by ajaxForm
|
||||
$.fn.ajaxFormUnbind = function() { |
||||
return this.unbind('submit.form-plugin click.form-plugin'); |
||||
}; |
||||
|
||||
/** |
||||
* formToArray() gathers form element data into an array of objects that can |
||||
* be passed to any of the following ajax functions: $.get, $.post, or load. |
||||
* Each object in the array has both a 'name' and 'value' property. An example of |
||||
* an array for a simple login form might be: |
||||
* |
||||
* [ { name: 'username', value: 'jresig' }, { name: 'password', value: 'secret' } ] |
||||
* |
||||
* It is this array that is passed to pre-submit callback functions provided to the |
||||
* ajaxSubmit() and ajaxForm() methods. |
||||
*/ |
||||
$.fn.formToArray = function(semantic) { |
||||
var a = []; |
||||
if (this.length == 0) return a; |
||||
|
||||
var form = this[0]; |
||||
var els = semantic ? form.getElementsByTagName('*') : form.elements; |
||||
if (!els) return a; |
||||
for(var i=0, max=els.length; i < max; i++) { |
||||
var el = els[i]; |
||||
var n = el.name; |
||||
if (!n) continue; |
||||
|
||||
if (semantic && form.clk && el.type == "image") { |
||||
// handle image inputs on the fly when semantic == true
|
||||
if(!el.disabled && form.clk == el) { |
||||
a.push({name: n, value: $(el).val()}); |
||||
a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y}); |
||||
} |
||||
continue; |
||||
} |
||||
|
||||
var v = $.fieldValue(el, true); |
||||
if (v && v.constructor == Array) { |
||||
for(var j=0, jmax=v.length; j < jmax; j++) |
||||
a.push({name: n, value: v[j]}); |
||||
} |
||||
else if (v !== null && typeof v != 'undefined') |
||||
a.push({name: n, value: v}); |
||||
} |
||||
|
||||
if (!semantic && form.clk) { |
||||
// input type=='image' are not found in elements array! handle it here
|
||||
var $input = $(form.clk), input = $input[0], n = input.name; |
||||
if (n && !input.disabled && input.type == 'image') { |
||||
a.push({name: n, value: $input.val()}); |
||||
a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y}); |
||||
} |
||||
} |
||||
return a; |
||||
}; |
||||
|
||||
/** |
||||
* Serializes form data into a 'submittable' string. This method will return a string |
||||
* in the format: name1=value1&name2=value2 |
||||
*/ |
||||
$.fn.formSerialize = function(semantic) { |
||||
//hand off to jQuery.param for proper encoding
|
||||
return $.param(this.formToArray(semantic)); |
||||
}; |
||||
|
||||
/** |
||||
* Serializes all field elements in the jQuery object into a query string. |
||||
* This method will return a string in the format: name1=value1&name2=value2 |
||||
*/ |
||||
$.fn.fieldSerialize = function(successful) { |
||||
var a = []; |
||||
this.each(function() { |
||||
var n = this.name; |
||||
if (!n) return; |
||||
var v = $.fieldValue(this, successful); |
||||
if (v && v.constructor == Array) { |
||||
for (var i=0,max=v.length; i < max; i++) |
||||
a.push({name: n, value: v[i]}); |
||||
} |
||||
else if (v !== null && typeof v != 'undefined') |
||||
a.push({name: this.name, value: v}); |
||||
}); |
||||
//hand off to jQuery.param for proper encoding
|
||||
return $.param(a); |
||||
}; |
||||
|
||||
/** |
||||
* Returns the value(s) of the element in the matched set. For example, consider the following form: |
||||
* |
||||
* <form><fieldset> |
||||
* <input name="A" type="text" /> |
||||
* <input name="A" type="text" /> |
||||
* <input name="B" type="checkbox" value="B1" /> |
||||
* <input name="B" type="checkbox" value="B2"/> |
||||
* <input name="C" type="radio" value="C1" /> |
||||
* <input name="C" type="radio" value="C2" /> |
||||
* </fieldset></form> |
||||
* |
||||
* var v = $(':text').fieldValue(); |
||||
* // if no values are entered into the text inputs
|
||||
* v == ['',''] |
||||
* // if values entered into the text inputs are 'foo' and 'bar'
|
||||
* v == ['foo','bar'] |
||||
* |
||||
* var v = $(':checkbox').fieldValue(); |
||||
* // if neither checkbox is checked
|
||||
* v === undefined |
||||
* // if both checkboxes are checked
|
||||
* v == ['B1', 'B2'] |
||||
* |
||||
* var v = $(':radio').fieldValue(); |
||||
* // if neither radio is checked
|
||||
* v === undefined |
||||
* // if first radio is checked
|
||||
* v == ['C1'] |
||||
* |
||||
* The successful argument controls whether or not the field element must be 'successful' |
||||
* (per http://www.w3.org/TR/html4/interact/forms.html#successful-controls).
|
||||
* The default value of the successful argument is true. If this value is false the value(s) |
||||
* for each element is returned. |
||||
* |
||||
* Note: This method *always* returns an array. If no valid value can be determined the |
||||
* array will be empty, otherwise it will contain one or more values. |
||||
*/ |
||||
$.fn.fieldValue = function(successful) { |
||||
for (var val=[], i=0, max=this.length; i < max; i++) { |
||||
var el = this[i]; |
||||
var v = $.fieldValue(el, successful); |
||||
if (v === null || typeof v == 'undefined' || (v.constructor == Array && !v.length)) |
||||
continue; |
||||
v.constructor == Array ? $.merge(val, v) : val.push(v); |
||||
} |
||||
return val; |
||||
}; |
||||
|
||||
/** |
||||
* Returns the value of the field element. |
||||
*/ |
||||
$.fieldValue = function(el, successful) { |
||||
var n = el.name, t = el.type, tag = el.tagName.toLowerCase(); |
||||
if (typeof successful == 'undefined') successful = true; |
||||
|
||||
if (successful && (!n || el.disabled || t == 'reset' || t == 'button' || |
||||
(t == 'checkbox' || t == 'radio') && !el.checked || |
||||
(t == 'submit' || t == 'image') && el.form && el.form.clk != el || |
||||
tag == 'select' && el.selectedIndex == -1)) |
||||
return null; |
||||
|
||||
if (tag == 'select') { |
||||
var index = el.selectedIndex; |
||||
if (index < 0) return null; |
||||
var a = [], ops = el.options; |
||||
var one = (t == 'select-one'); |
||||
var max = (one ? index+1 : ops.length); |
||||
for(var i=(one ? index : 0); i < max; i++) { |
||||
var op = ops[i]; |
||||
if (op.selected) { |
||||
var v = op.value; |
||||
if (!v) // extra pain for IE...
|
||||
v = (op.attributes && op.attributes['value'] && !(op.attributes['value'].specified)) ? op.text : op.value; |
||||
if (one) return v; |
||||
a.push(v); |
||||
} |
||||
} |
||||
return a; |
||||
} |
||||
return el.value; |
||||
}; |
||||
|
||||
/** |
||||
* Clears the form data. Takes the following actions on the form's input fields: |
||||
* - input text fields will have their 'value' property set to the empty string |
||||
* - select elements will have their 'selectedIndex' property set to -1 |
||||
* - checkbox and radio inputs will have their 'checked' property set to false |
||||
* - inputs of type submit, button, reset, and hidden will *not* be effected |
||||
* - button elements will *not* be effected |
||||
*/ |
||||
$.fn.clearForm = function() { |
||||
return this.each(function() { |
||||
$('input,select,textarea', this).clearFields(); |
||||
}); |
||||
}; |
||||
|
||||
/** |
||||
* Clears the selected form elements. |
||||
*/ |
||||
$.fn.clearFields = $.fn.clearInputs = function() { |
||||
return this.each(function() { |
||||
var t = this.type, tag = this.tagName.toLowerCase(); |
||||
if (t == 'text' || t == 'password' || tag == 'textarea') |
||||
this.value = ''; |
||||
else if (t == 'checkbox' || t == 'radio') |
||||
this.checked = false; |
||||
else if (tag == 'select') |
||||
this.selectedIndex = -1; |
||||
}); |
||||
}; |
||||
|
||||
/** |
||||
* Resets the form data. Causes all form elements to be reset to their original value. |
||||
*/ |
||||
$.fn.resetForm = function() { |
||||
return this.each(function() { |
||||
// guard against an input with the name of 'reset'
|
||||
// note that IE reports the reset function as an 'object'
|
||||
if (typeof this.reset == 'function' || (typeof this.reset == 'object' && !this.reset.nodeType)) |
||||
this.reset(); |
||||
}); |
||||
}; |
||||
|
||||
/** |
||||
* Enables or disables any matching elements. |
||||
*/ |
||||
$.fn.enable = function(b) { |
||||
if (b == undefined) b = true; |
||||
return this.each(function() { |
||||
this.disabled = !b; |
||||
}); |
||||
}; |
||||
|
||||
/** |
||||
* Checks/unchecks any matching checkboxes or radio buttons and |
||||
* selects/deselects and matching option elements. |
||||
*/ |
||||
$.fn.selected = function(select) { |
||||
if (select == undefined) select = true; |
||||
return this.each(function() { |
||||
var t = this.type; |
||||
if (t == 'checkbox' || t == 'radio') |
||||
this.checked = select; |
||||
else if (this.tagName.toLowerCase() == 'option') { |
||||
var $sel = $(this).parent('select'); |
||||
if (select && $sel[0] && $sel[0].type == 'select-one') { |
||||
// deselect all other options
|
||||
$sel.find('option').selected(false); |
||||
} |
||||
this.selected = select; |
||||
} |
||||
}); |
||||
}; |
||||
|
||||
// helper fn for console logging
|
||||
// set $.fn.ajaxSubmit.debug to true to enable debug logging
|
||||
function log() { |
||||
if ($.fn.ajaxSubmit.debug && window.console && window.console.log) |
||||
window.console.log('[jquery.form] ' + Array.prototype.join.call(arguments,'')); |
||||
}; |
||||
|
||||
})(jQuery); |
||||
@ -0,0 +1,7 @@ |
||||
/* |
||||
HeatColor, by Josh Nathanson |
||||
A plugin for jQuery |
||||
Complete documentation at http://www.jnathanson.com/blog/client/jquery/heatcolor/index.cfm
|
||||
*/ |
||||
|
||||
eval(function(p,a,c,k,e,r){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)r[e(c)]=k[c]||e(c);k=[function(e){return r[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('p.V.10=9(j,k){4 l={M:9(){v p(8)},q:0,t:0,N:0.Z,F:\'E\',y:X};u(k){p.W(l,k)};4 m={K:9(a,c,d){4 e=(a-c)/(d-c);4 f=l.F==\'E\'?0.5*e+1.7*(1-e):e+0.2+5.5*(1-e);4 h=Y;4 i=2*6.A;4 x=f+e*i;x=l.F!=\'E\'?-x:x;4 r=8.w(6.z((6.D(x)+1)*h));4 g=8.w(6.z((6.D(x+6.A/2)+1)*h));4 b=8.w(6.z((6.D(x+6.A)+1)*h));v\'#\'+r+g+b},w:9(a){4 n=6.z(a+l.N*(U-a));4 s=n.T(S);s=s.C==1?\'0\'+s:s;v s},J:9(c){4 d=[];c.I(9(){d.R(j.B(p(8)))});d=d.Q(9(a,b){v a-b});l.t=!l.y?d[d.C-1]:d[0];l.q=!l.y?d[0]:d[d.C-1]}};u(!l.q&&!l.t)m.J(p(8));H u(l.y){4 o=l.q;l.q=l.t;l.t=o}p(8).I(9(){4 a=p(8);4 b=j.B(a);4 c=m.K(b,l.q,l.t);4 d=l.M.B(a);u(d[0].L==1)d.O("P-G",c);H u(d[0].L==3)d.O("G",c)});v(8)}',62,63,'||||var||Math||this|function||||||||||||||||jQuery|minval|||maxval|if|return|process||reverseOrder|floor|PI|apply|length|cos|roygbiv|colorStyle|color|else|each|setMaxAndMin|findcolor|nodeType|elementFunction|lightness|css|background|sort|push|16|toString|256|fn|extend|false|128|75|heatcolor'.split('|'),0,{})) |
||||
@ -0,0 +1,152 @@ |
||||
/*! |
||||
* jQuery JavaScript Library v1.4.1 |
||||
* http://jquery.com/
|
||||
* |
||||
* Copyright 2010, John Resig |
||||
* Dual licensed under the MIT or GPL Version 2 licenses. |
||||
* http://jquery.org/license
|
||||
* |
||||
* Includes Sizzle.js |
||||
* http://sizzlejs.com/
|
||||
* Copyright 2010, The Dojo Foundation |
||||
* Released under the MIT, BSD, and GPL Licenses. |
||||
* |
||||
* Date: Mon Jan 25 19:43:33 2010 -0500 |
||||
*/ |
||||
(function(z,v){function la(){if(!c.isReady){try{r.documentElement.doScroll("left")}catch(a){setTimeout(la,1);return}c.ready()}}function Ma(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function X(a,b,d,f,e,i){var j=a.length;if(typeof b==="object"){for(var n in b)X(a,n,b[n],f,e,d);return a}if(d!==v){f=!i&&f&&c.isFunction(d);for(n=0;n<j;n++)e(a[n],b,f?d.call(a[n],n,e(a[n],b)):d,i);return a}return j? |
||||
e(a[0],b):null}function J(){return(new Date).getTime()}function Y(){return false}function Z(){return true}function ma(a,b,d){d[0].type=a;return c.event.handle.apply(b,d)}function na(a){var b,d=[],f=[],e=arguments,i,j,n,o,m,s,x=c.extend({},c.data(this,"events").live);if(!(a.button&&a.type==="click")){for(o in x){j=x[o];if(j.live===a.type||j.altLive&&c.inArray(a.type,j.altLive)>-1){i=j.data;i.beforeFilter&&i.beforeFilter[a.type]&&!i.beforeFilter[a.type](a)||f.push(j.selector)}else delete x[o]}i=c(a.target).closest(f, |
||||
a.currentTarget);m=0;for(s=i.length;m<s;m++)for(o in x){j=x[o];n=i[m].elem;f=null;if(i[m].selector===j.selector){if(j.live==="mouseenter"||j.live==="mouseleave")f=c(a.relatedTarget).closest(j.selector)[0];if(!f||f!==n)d.push({elem:n,fn:j})}}m=0;for(s=d.length;m<s;m++){i=d[m];a.currentTarget=i.elem;a.data=i.fn.data;if(i.fn.apply(i.elem,e)===false){b=false;break}}return b}}function oa(a,b){return"live."+(a?a+".":"")+b.replace(/\./g,"`").replace(/ /g,"&")}function pa(a){return!a||!a.parentNode||a.parentNode.nodeType=== |
||||
11}function qa(a,b){var d=0;b.each(function(){if(this.nodeName===(a[d]&&a[d].nodeName)){var f=c.data(a[d++]),e=c.data(this,f);if(f=f&&f.events){delete e.handle;e.events={};for(var i in f)for(var j in f[i])c.event.add(this,i,f[i][j],f[i][j].data)}}})}function ra(a,b,d){var f,e,i;if(a.length===1&&typeof a[0]==="string"&&a[0].length<512&&a[0].indexOf("<option")<0&&(c.support.checkClone||!sa.test(a[0]))){e=true;if(i=c.fragments[a[0]])if(i!==1)f=i}if(!f){b=b&&b[0]?b[0].ownerDocument||b[0]:r;f=b.createDocumentFragment(); |
||||
c.clean(a,b,f,d)}if(e)c.fragments[a[0]]=i?f:1;return{fragment:f,cacheable:e}}function K(a,b){var d={};c.each(ta.concat.apply([],ta.slice(0,b)),function(){d[this]=a});return d}function ua(a){return"scrollTo"in a&&a.document?a:a.nodeType===9?a.defaultView||a.parentWindow:false}var c=function(a,b){return new c.fn.init(a,b)},Na=z.jQuery,Oa=z.$,r=z.document,S,Pa=/^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/,Qa=/^.[^:#\[\.,]*$/,Ra=/\S/,Sa=/^(\s|\u00A0)+|(\s|\u00A0)+$/g,Ta=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,O=navigator.userAgent, |
||||
va=false,P=[],L,$=Object.prototype.toString,aa=Object.prototype.hasOwnProperty,ba=Array.prototype.push,Q=Array.prototype.slice,wa=Array.prototype.indexOf;c.fn=c.prototype={init:function(a,b){var d,f;if(!a)return this;if(a.nodeType){this.context=this[0]=a;this.length=1;return this}if(typeof a==="string")if((d=Pa.exec(a))&&(d[1]||!b))if(d[1]){f=b?b.ownerDocument||b:r;if(a=Ta.exec(a))if(c.isPlainObject(b)){a=[r.createElement(a[1])];c.fn.attr.call(a,b,true)}else a=[f.createElement(a[1])];else{a=ra([d[1]], |
||||
[f]);a=(a.cacheable?a.fragment.cloneNode(true):a.fragment).childNodes}}else{if(b=r.getElementById(d[2])){if(b.id!==d[2])return S.find(a);this.length=1;this[0]=b}this.context=r;this.selector=a;return this}else if(!b&&/^\w+$/.test(a)){this.selector=a;this.context=r;a=r.getElementsByTagName(a)}else return!b||b.jquery?(b||S).find(a):c(b).find(a);else if(c.isFunction(a))return S.ready(a);if(a.selector!==v){this.selector=a.selector;this.context=a.context}return c.isArray(a)?this.setArray(a):c.makeArray(a, |
||||
this)},selector:"",jquery:"1.4.1",length:0,size:function(){return this.length},toArray:function(){return Q.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this.slice(a)[0]:this[a]},pushStack:function(a,b,d){a=c(a||null);a.prevObject=this;a.context=this.context;if(b==="find")a.selector=this.selector+(this.selector?" ":"")+d;else if(b)a.selector=this.selector+"."+b+"("+d+")";return a},setArray:function(a){this.length=0;ba.apply(this,a);return this},each:function(a,b){return c.each(this, |
||||
a,b)},ready:function(a){c.bindReady();if(c.isReady)a.call(r,c);else P&&P.push(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(Q.apply(this,arguments),"slice",Q.call(arguments).join(","))},map:function(a){return this.pushStack(c.map(this,function(b,d){return a.call(b,d,b)}))},end:function(){return this.prevObject||c(null)},push:ba,sort:[].sort,splice:[].splice}; |
||||
c.fn.init.prototype=c.fn;c.extend=c.fn.extend=function(){var a=arguments[0]||{},b=1,d=arguments.length,f=false,e,i,j,n;if(typeof a==="boolean"){f=a;a=arguments[1]||{};b=2}if(typeof a!=="object"&&!c.isFunction(a))a={};if(d===b){a=this;--b}for(;b<d;b++)if((e=arguments[b])!=null)for(i in e){j=a[i];n=e[i];if(a!==n)if(f&&n&&(c.isPlainObject(n)||c.isArray(n))){j=j&&(c.isPlainObject(j)||c.isArray(j))?j:c.isArray(n)?[]:{};a[i]=c.extend(f,j,n)}else if(n!==v)a[i]=n}return a};c.extend({noConflict:function(a){z.$= |
||||
Oa;if(a)z.jQuery=Na;return c},isReady:false,ready:function(){if(!c.isReady){if(!r.body)return setTimeout(c.ready,13);c.isReady=true;if(P){for(var a,b=0;a=P[b++];)a.call(r,c);P=null}c.fn.triggerHandler&&c(r).triggerHandler("ready")}},bindReady:function(){if(!va){va=true;if(r.readyState==="complete")return c.ready();if(r.addEventListener){r.addEventListener("DOMContentLoaded",L,false);z.addEventListener("load",c.ready,false)}else if(r.attachEvent){r.attachEvent("onreadystatechange",L);z.attachEvent("onload", |
||||
c.ready);var a=false;try{a=z.frameElement==null}catch(b){}r.documentElement.doScroll&&a&&la()}}},isFunction:function(a){return $.call(a)==="[object Function]"},isArray:function(a){return $.call(a)==="[object Array]"},isPlainObject:function(a){if(!a||$.call(a)!=="[object Object]"||a.nodeType||a.setInterval)return false;if(a.constructor&&!aa.call(a,"constructor")&&!aa.call(a.constructor.prototype,"isPrototypeOf"))return false;var b;for(b in a);return b===v||aa.call(a,b)},isEmptyObject:function(a){for(var b in a)return false; |
||||
return true},error:function(a){throw a;},parseJSON:function(a){if(typeof a!=="string"||!a)return null;if(/^[\],:{}\s]*$/.test(a.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,"")))return z.JSON&&z.JSON.parse?z.JSON.parse(a):(new Function("return "+a))();else c.error("Invalid JSON: "+a)},noop:function(){},globalEval:function(a){if(a&&Ra.test(a)){var b=r.getElementsByTagName("head")[0]|| |
||||
r.documentElement,d=r.createElement("script");d.type="text/javascript";if(c.support.scriptEval)d.appendChild(r.createTextNode(a));else d.text=a;b.insertBefore(d,b.firstChild);b.removeChild(d)}},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,b,d){var f,e=0,i=a.length,j=i===v||c.isFunction(a);if(d)if(j)for(f in a){if(b.apply(a[f],d)===false)break}else for(;e<i;){if(b.apply(a[e++],d)===false)break}else if(j)for(f in a){if(b.call(a[f],f,a[f])===false)break}else for(d= |
||||
a[0];e<i&&b.call(d,e,d)!==false;d=a[++e]);return a},trim:function(a){return(a||"").replace(Sa,"")},makeArray:function(a,b){b=b||[];if(a!=null)a.length==null||typeof a==="string"||c.isFunction(a)||typeof a!=="function"&&a.setInterval?ba.call(b,a):c.merge(b,a);return b},inArray:function(a,b){if(b.indexOf)return b.indexOf(a);for(var d=0,f=b.length;d<f;d++)if(b[d]===a)return d;return-1},merge:function(a,b){var d=a.length,f=0;if(typeof b.length==="number")for(var e=b.length;f<e;f++)a[d++]=b[f];else for(;b[f]!== |
||||
v;)a[d++]=b[f++];a.length=d;return a},grep:function(a,b,d){for(var f=[],e=0,i=a.length;e<i;e++)!d!==!b(a[e],e)&&f.push(a[e]);return f},map:function(a,b,d){for(var f=[],e,i=0,j=a.length;i<j;i++){e=b(a[i],i,d);if(e!=null)f[f.length]=e}return f.concat.apply([],f)},guid:1,proxy:function(a,b,d){if(arguments.length===2)if(typeof b==="string"){d=a;a=d[b];b=v}else if(b&&!c.isFunction(b)){d=b;b=v}if(!b&&a)b=function(){return a.apply(d||this,arguments)};if(a)b.guid=a.guid=a.guid||b.guid||c.guid++;return b}, |
||||
uaMatch:function(a){a=a.toLowerCase();a=/(webkit)[ \/]([\w.]+)/.exec(a)||/(opera)(?:.*version)?[ \/]([\w.]+)/.exec(a)||/(msie) ([\w.]+)/.exec(a)||!/compatible/.test(a)&&/(mozilla)(?:.*? rv:([\w.]+))?/.exec(a)||[];return{browser:a[1]||"",version:a[2]||"0"}},browser:{}});O=c.uaMatch(O);if(O.browser){c.browser[O.browser]=true;c.browser.version=O.version}if(c.browser.webkit)c.browser.safari=true;if(wa)c.inArray=function(a,b){return wa.call(b,a)};S=c(r);if(r.addEventListener)L=function(){r.removeEventListener("DOMContentLoaded", |
||||
L,false);c.ready()};else if(r.attachEvent)L=function(){if(r.readyState==="complete"){r.detachEvent("onreadystatechange",L);c.ready()}};(function(){c.support={};var a=r.documentElement,b=r.createElement("script"),d=r.createElement("div"),f="script"+J();d.style.display="none";d.innerHTML=" <link/><table></table><a href='/a' style='color:red;float:left;opacity:.55;'>a</a><input type='checkbox'/>";var e=d.getElementsByTagName("*"),i=d.getElementsByTagName("a")[0];if(!(!e||!e.length||!i)){c.support= |
||||
{leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(i.getAttribute("style")),hrefNormalized:i.getAttribute("href")==="/a",opacity:/^0.55$/.test(i.style.opacity),cssFloat:!!i.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:r.createElement("select").appendChild(r.createElement("option")).selected,checkClone:false,scriptEval:false,noCloneEvent:true,boxModel:null}; |
||||
b.type="text/javascript";try{b.appendChild(r.createTextNode("window."+f+"=1;"))}catch(j){}a.insertBefore(b,a.firstChild);if(z[f]){c.support.scriptEval=true;delete z[f]}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function n(){c.support.noCloneEvent=false;d.detachEvent("onclick",n)});d.cloneNode(true).fireEvent("onclick")}d=r.createElement("div");d.innerHTML="<input type='radio' name='radiotest' checked='checked'/>";a=r.createDocumentFragment();a.appendChild(d.firstChild); |
||||
c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var n=r.createElement("div");n.style.width=n.style.paddingLeft="1px";r.body.appendChild(n);c.boxModel=c.support.boxModel=n.offsetWidth===2;r.body.removeChild(n).style.display="none"});a=function(n){var o=r.createElement("div");n="on"+n;var m=n in o;if(!m){o.setAttribute(n,"return;");m=typeof o[n]==="function"}return m};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=e=i=null}})();c.props= |
||||
{"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",frameborder:"frameBorder"};var G="jQuery"+J(),Ua=0,xa={},Va={};c.extend({cache:{},expando:G,noData:{embed:true,object:true,applet:true},data:function(a,b,d){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==z?xa:a;var f=a[G],e=c.cache;if(!b&&!f)return null;f||(f=++Ua);if(typeof b==="object"){a[G]=f;e=e[f]=c.extend(true, |
||||
{},b)}else e=e[f]?e[f]:typeof d==="undefined"?Va:(e[f]={});if(d!==v){a[G]=f;e[b]=d}return typeof b==="string"?e[b]:e}},removeData:function(a,b){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==z?xa:a;var d=a[G],f=c.cache,e=f[d];if(b){if(e){delete e[b];c.isEmptyObject(e)&&c.removeData(a)}}else{try{delete a[G]}catch(i){a.removeAttribute&&a.removeAttribute(G)}delete f[d]}}}});c.fn.extend({data:function(a,b){if(typeof a==="undefined"&&this.length)return c.data(this[0]);else if(typeof a==="object")return this.each(function(){c.data(this, |
||||
a)});var d=a.split(".");d[1]=d[1]?"."+d[1]:"";if(b===v){var f=this.triggerHandler("getData"+d[1]+"!",[d[0]]);if(f===v&&this.length)f=c.data(this[0],a);return f===v&&d[1]?this.data(d[0]):f}else return this.trigger("setData"+d[1]+"!",[d[0],b]).each(function(){c.data(this,a,b)})},removeData:function(a){return this.each(function(){c.removeData(this,a)})}});c.extend({queue:function(a,b,d){if(a){b=(b||"fx")+"queue";var f=c.data(a,b);if(!d)return f||[];if(!f||c.isArray(d))f=c.data(a,b,c.makeArray(d));else f.push(d); |
||||
return f}},dequeue:function(a,b){b=b||"fx";var d=c.queue(a,b),f=d.shift();if(f==="inprogress")f=d.shift();if(f){b==="fx"&&d.unshift("inprogress");f.call(a,function(){c.dequeue(a,b)})}}});c.fn.extend({queue:function(a,b){if(typeof a!=="string"){b=a;a="fx"}if(b===v)return c.queue(this[0],a);return this.each(function(){var d=c.queue(this,a,b);a==="fx"&&d[0]!=="inprogress"&&c.dequeue(this,a)})},dequeue:function(a){return this.each(function(){c.dequeue(this,a)})},delay:function(a,b){a=c.fx?c.fx.speeds[a]|| |
||||
a:a;b=b||"fx";return this.queue(b,function(){var d=this;setTimeout(function(){c.dequeue(d,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])}});var ya=/[\n\t]/g,ca=/\s+/,Wa=/\r/g,Xa=/href|src|style/,Ya=/(button|input)/i,Za=/(button|input|object|select|textarea)/i,$a=/^(a|area)$/i,za=/radio|checkbox/;c.fn.extend({attr:function(a,b){return X(this,a,b,true,c.attr)},removeAttr:function(a){return this.each(function(){c.attr(this,a,"");this.nodeType===1&&this.removeAttribute(a)})},addClass:function(a){if(c.isFunction(a))return this.each(function(o){var m= |
||||
c(this);m.addClass(a.call(this,o,m.attr("class")))});if(a&&typeof a==="string")for(var b=(a||"").split(ca),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1)if(e.className)for(var i=" "+e.className+" ",j=0,n=b.length;j<n;j++){if(i.indexOf(" "+b[j]+" ")<0)e.className+=" "+b[j]}else e.className=a}return this},removeClass:function(a){if(c.isFunction(a))return this.each(function(o){var m=c(this);m.removeClass(a.call(this,o,m.attr("class")))});if(a&&typeof a==="string"||a===v)for(var b=(a||"").split(ca), |
||||
d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1&&e.className)if(a){for(var i=(" "+e.className+" ").replace(ya," "),j=0,n=b.length;j<n;j++)i=i.replace(" "+b[j]+" "," ");e.className=i.substring(1,i.length-1)}else e.className=""}return this},toggleClass:function(a,b){var d=typeof a,f=typeof b==="boolean";if(c.isFunction(a))return this.each(function(e){var i=c(this);i.toggleClass(a.call(this,e,i.attr("class"),b),b)});return this.each(function(){if(d==="string")for(var e,i=0,j=c(this),n=b,o= |
||||
a.split(ca);e=o[i++];){n=f?n:!j.hasClass(e);j[n?"addClass":"removeClass"](e)}else if(d==="undefined"||d==="boolean"){this.className&&c.data(this,"__className__",this.className);this.className=this.className||a===false?"":c.data(this,"__className__")||""}})},hasClass:function(a){a=" "+a+" ";for(var b=0,d=this.length;b<d;b++)if((" "+this[b].className+" ").replace(ya," ").indexOf(a)>-1)return true;return false},val:function(a){if(a===v){var b=this[0];if(b){if(c.nodeName(b,"option"))return(b.attributes.value|| |
||||
{}).specified?b.value:b.text;if(c.nodeName(b,"select")){var d=b.selectedIndex,f=[],e=b.options;b=b.type==="select-one";if(d<0)return null;var i=b?d:0;for(d=b?d+1:e.length;i<d;i++){var j=e[i];if(j.selected){a=c(j).val();if(b)return a;f.push(a)}}return f}if(za.test(b.type)&&!c.support.checkOn)return b.getAttribute("value")===null?"on":b.value;return(b.value||"").replace(Wa,"")}return v}var n=c.isFunction(a);return this.each(function(o){var m=c(this),s=a;if(this.nodeType===1){if(n)s=a.call(this,o,m.val()); |
||||
if(typeof s==="number")s+="";if(c.isArray(s)&&za.test(this.type))this.checked=c.inArray(m.val(),s)>=0;else if(c.nodeName(this,"select")){var x=c.makeArray(s);c("option",this).each(function(){this.selected=c.inArray(c(this).val(),x)>=0});if(!x.length)this.selectedIndex=-1}else this.value=s}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},attr:function(a,b,d,f){if(!a||a.nodeType===3||a.nodeType===8)return v;if(f&&b in c.attrFn)return c(a)[b](d); |
||||
f=a.nodeType!==1||!c.isXMLDoc(a);var e=d!==v;b=f&&c.props[b]||b;if(a.nodeType===1){var i=Xa.test(b);if(b in a&&f&&!i){if(e){b==="type"&&Ya.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed");a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&&b.specified?b.value:Za.test(a.nodeName)||$a.test(a.nodeName)&&a.href?0:v;return a[b]}if(!c.support.style&&f&&b==="style"){if(e)a.style.cssText= |
||||
""+d;return a.style.cssText}e&&a.setAttribute(b,""+d);a=!c.support.hrefNormalized&&f&&i?a.getAttribute(b,2):a.getAttribute(b);return a===null?v:a}return c.style(a,b,d)}});var ab=function(a){return a.replace(/[^\w\s\.\|`]/g,function(b){return"\\"+b})};c.event={add:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){if(a.setInterval&&a!==z&&!a.frameElement)a=z;if(!d.guid)d.guid=c.guid++;if(f!==v){d=c.proxy(d);d.data=f}var e=c.data(a,"events")||c.data(a,"events",{}),i=c.data(a,"handle"),j;if(!i){j= |
||||
function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(j.elem,arguments):v};i=c.data(a,"handle",j)}if(i){i.elem=a;b=b.split(/\s+/);for(var n,o=0;n=b[o++];){var m=n.split(".");n=m.shift();if(o>1){d=c.proxy(d);if(f!==v)d.data=f}d.type=m.slice(0).sort().join(".");var s=e[n],x=this.special[n]||{};if(!s){s=e[n]={};if(!x.setup||x.setup.call(a,f,m,d)===false)if(a.addEventListener)a.addEventListener(n,i,false);else a.attachEvent&&a.attachEvent("on"+n,i)}if(x.add)if((m=x.add.call(a, |
||||
d,f,m,s))&&c.isFunction(m)){m.guid=m.guid||d.guid;m.data=m.data||d.data;m.type=m.type||d.type;d=m}s[d.guid]=d;this.global[n]=true}a=null}}},global:{},remove:function(a,b,d){if(!(a.nodeType===3||a.nodeType===8)){var f=c.data(a,"events"),e,i,j;if(f){if(b===v||typeof b==="string"&&b.charAt(0)===".")for(i in f)this.remove(a,i+(b||""));else{if(b.type){d=b.handler;b=b.type}b=b.split(/\s+/);for(var n=0;i=b[n++];){var o=i.split(".");i=o.shift();var m=!o.length,s=c.map(o.slice(0).sort(),ab);s=new RegExp("(^|\\.)"+ |
||||
s.join("\\.(?:.*\\.)?")+"(\\.|$)");var x=this.special[i]||{};if(f[i]){if(d){j=f[i][d.guid];delete f[i][d.guid]}else for(var A in f[i])if(m||s.test(f[i][A].type))delete f[i][A];x.remove&&x.remove.call(a,o,j);for(e in f[i])break;if(!e){if(!x.teardown||x.teardown.call(a,o)===false)if(a.removeEventListener)a.removeEventListener(i,c.data(a,"handle"),false);else a.detachEvent&&a.detachEvent("on"+i,c.data(a,"handle"));e=null;delete f[i]}}}}for(e in f)break;if(!e){if(A=c.data(a,"handle"))A.elem=null;c.removeData(a, |
||||
"events");c.removeData(a,"handle")}}}},trigger:function(a,b,d,f){var e=a.type||a;if(!f){a=typeof a==="object"?a[G]?a:c.extend(c.Event(e),a):c.Event(e);if(e.indexOf("!")>=0){a.type=e=e.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();this.global[e]&&c.each(c.cache,function(){this.events&&this.events[e]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType===8)return v;a.result=v;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(f=c.data(d,"handle"))&&f.apply(d, |
||||
b);f=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+e]&&d["on"+e].apply(d,b)===false)a.result=false}catch(i){}if(!a.isPropagationStopped()&&f)c.event.trigger(a,b,f,true);else if(!a.isDefaultPrevented()){d=a.target;var j;if(!(c.nodeName(d,"a")&&e==="click")&&!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()])){try{if(d[e]){if(j=d["on"+e])d["on"+e]=null;this.triggered=true;d[e]()}}catch(n){}if(j)d["on"+e]=j;this.triggered=false}}},handle:function(a){var b, |
||||
d;a=arguments[0]=c.event.fix(a||z.event);a.currentTarget=this;d=a.type.split(".");a.type=d.shift();b=!d.length&&!a.exclusive;var f=new RegExp("(^|\\.)"+d.slice(0).sort().join("\\.(?:.*\\.)?")+"(\\.|$)");d=(c.data(this,"events")||{})[a.type];for(var e in d){var i=d[e];if(b||f.test(i.type)){a.handler=i;a.data=i.data;i=i.apply(this,arguments);if(i!==v){a.result=i;if(i===false){a.preventDefault();a.stopPropagation()}}if(a.isImmediatePropagationStopped())break}}return a.result},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "), |
||||
fix:function(a){if(a[G])return a;var b=a;a=c.Event(b);for(var d=this.props.length,f;d;){f=this.props[--d];a[f]=b[f]}if(!a.target)a.target=a.srcElement||r;if(a.target.nodeType===3)a.target=a.target.parentNode;if(!a.relatedTarget&&a.fromElement)a.relatedTarget=a.fromElement===a.target?a.toElement:a.fromElement;if(a.pageX==null&&a.clientX!=null){b=r.documentElement;d=r.body;a.pageX=a.clientX+(b&&b.scrollLeft||d&&d.scrollLeft||0)-(b&&b.clientLeft||d&&d.clientLeft||0);a.pageY=a.clientY+(b&&b.scrollTop|| |
||||
d&&d.scrollTop||0)-(b&&b.clientTop||d&&d.clientTop||0)}if(!a.which&&(a.charCode||a.charCode===0?a.charCode:a.keyCode))a.which=a.charCode||a.keyCode;if(!a.metaKey&&a.ctrlKey)a.metaKey=a.ctrlKey;if(!a.which&&a.button!==v)a.which=a.button&1?1:a.button&2?3:a.button&4?2:0;return a},guid:1E8,proxy:c.proxy,special:{ready:{setup:c.bindReady,teardown:c.noop},live:{add:function(a,b){c.extend(a,b||{});a.guid+=b.selector+b.live;b.liveProxy=a;c.event.add(this,b.live,na,b)},remove:function(a){if(a.length){var b= |
||||
0,d=new RegExp("(^|\\.)"+a[0]+"(\\.|$)");c.each(c.data(this,"events").live||{},function(){d.test(this.type)&&b++});b<1&&c.event.remove(this,a[0],na)}},special:{}},beforeunload:{setup:function(a,b,d){if(this.setInterval)this.onbeforeunload=d;return false},teardown:function(a,b){if(this.onbeforeunload===b)this.onbeforeunload=null}}}};c.Event=function(a){if(!this.preventDefault)return new c.Event(a);if(a&&a.type){this.originalEvent=a;this.type=a.type}else this.type=a;this.timeStamp=J();this[G]=true}; |
||||
c.Event.prototype={preventDefault:function(){this.isDefaultPrevented=Z;var a=this.originalEvent;if(a){a.preventDefault&&a.preventDefault();a.returnValue=false}},stopPropagation:function(){this.isPropagationStopped=Z;var a=this.originalEvent;if(a){a.stopPropagation&&a.stopPropagation();a.cancelBubble=true}},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=Z;this.stopPropagation()},isDefaultPrevented:Y,isPropagationStopped:Y,isImmediatePropagationStopped:Y};var Aa=function(a){for(var b= |
||||
a.relatedTarget;b&&b!==this;)try{b=b.parentNode}catch(d){break}if(b!==this){a.type=a.data;c.event.handle.apply(this,arguments)}},Ba=function(a){a.type=a.data;c.event.handle.apply(this,arguments)};c.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){c.event.special[a]={setup:function(d){c.event.add(this,b,d&&d.selector?Ba:Aa,a)},teardown:function(d){c.event.remove(this,b,d&&d.selector?Ba:Aa)}}});if(!c.support.submitBubbles)c.event.special.submit={setup:function(a,b,d){if(this.nodeName.toLowerCase()!== |
||||
"form"){c.event.add(this,"click.specialSubmit."+d.guid,function(f){var e=f.target,i=e.type;if((i==="submit"||i==="image")&&c(e).closest("form").length)return ma("submit",this,arguments)});c.event.add(this,"keypress.specialSubmit."+d.guid,function(f){var e=f.target,i=e.type;if((i==="text"||i==="password")&&c(e).closest("form").length&&f.keyCode===13)return ma("submit",this,arguments)})}else return false},remove:function(a,b){c.event.remove(this,"click.specialSubmit"+(b?"."+b.guid:""));c.event.remove(this, |
||||
"keypress.specialSubmit"+(b?"."+b.guid:""))}};if(!c.support.changeBubbles){var da=/textarea|input|select/i;function Ca(a){var b=a.type,d=a.value;if(b==="radio"||b==="checkbox")d=a.checked;else if(b==="select-multiple")d=a.selectedIndex>-1?c.map(a.options,function(f){return f.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d}function ea(a,b){var d=a.target,f,e;if(!(!da.test(d.nodeName)||d.readOnly)){f=c.data(d,"_change_data");e=Ca(d);if(a.type!=="focusout"|| |
||||
d.type!=="radio")c.data(d,"_change_data",e);if(!(f===v||e===f))if(f!=null||e){a.type="change";return c.event.trigger(a,b,d)}}}c.event.special.change={filters:{focusout:ea,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return ea.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return ea.call(this,a)},beforeactivate:function(a){a= |
||||
a.target;a.nodeName.toLowerCase()==="input"&&a.type==="radio"&&c.data(a,"_change_data",Ca(a))}},setup:function(a,b,d){for(var f in T)c.event.add(this,f+".specialChange."+d.guid,T[f]);return da.test(this.nodeName)},remove:function(a,b){for(var d in T)c.event.remove(this,d+".specialChange"+(b?"."+b.guid:""),T[d]);return da.test(this.nodeName)}};var T=c.event.special.change.filters}r.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(f){f=c.event.fix(f);f.type=b;return c.event.handle.call(this, |
||||
f)}c.event.special[b]={setup:function(){this.addEventListener(a,d,true)},teardown:function(){this.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,f,e){if(typeof d==="object"){for(var i in d)this[b](i,f,d[i],e);return this}if(c.isFunction(f)){e=f;f=v}var j=b==="one"?c.proxy(e,function(n){c(this).unbind(n,j);return e.apply(this,arguments)}):e;return d==="unload"&&b!=="one"?this.one(d,f,e):this.each(function(){c.event.add(this,d,j,f)})}});c.fn.extend({unbind:function(a, |
||||
b){if(typeof a==="object"&&!a.preventDefault){for(var d in a)this.unbind(d,a[d]);return this}return this.each(function(){c.event.remove(this,a,b)})},trigger:function(a,b){return this.each(function(){c.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0]){a=c.Event(a);a.preventDefault();a.stopPropagation();c.event.trigger(a,b,this[0]);return a.result}},toggle:function(a){for(var b=arguments,d=1;d<b.length;)c.proxy(a,b[d++]);return this.click(c.proxy(a,function(f){var e=(c.data(this,"lastToggle"+ |
||||
a.guid)||0)%d;c.data(this,"lastToggle"+a.guid,e+1);f.preventDefault();return b[e].apply(this,arguments)||false}))},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}});c.each(["live","die"],function(a,b){c.fn[b]=function(d,f,e){var i,j=0;if(c.isFunction(f)){e=f;f=v}for(d=(d||"").split(/\s+/);(i=d[j++])!=null;){i=i==="focus"?"focusin":i==="blur"?"focusout":i==="hover"?d.push("mouseleave")&&"mouseenter":i;b==="live"?c(this.context).bind(oa(i,this.selector),{data:f,selector:this.selector, |
||||
live:i},e):c(this.context).unbind(oa(i,this.selector),e?{guid:e.guid+this.selector+i}:null)}return this}});c.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error".split(" "),function(a,b){c.fn[b]=function(d){return d?this.bind(b,d):this.trigger(b)};if(c.attrFn)c.attrFn[b]=true});z.attachEvent&&!z.addEventListener&&z.attachEvent("onunload",function(){for(var a in c.cache)if(c.cache[a].handle)try{c.event.remove(c.cache[a].handle.elem)}catch(b){}}); |
||||
(function(){function a(g){for(var h="",k,l=0;g[l];l++){k=g[l];if(k.nodeType===3||k.nodeType===4)h+=k.nodeValue;else if(k.nodeType!==8)h+=a(k.childNodes)}return h}function b(g,h,k,l,q,p){q=0;for(var u=l.length;q<u;q++){var t=l[q];if(t){t=t[g];for(var y=false;t;){if(t.sizcache===k){y=l[t.sizset];break}if(t.nodeType===1&&!p){t.sizcache=k;t.sizset=q}if(t.nodeName.toLowerCase()===h){y=t;break}t=t[g]}l[q]=y}}}function d(g,h,k,l,q,p){q=0;for(var u=l.length;q<u;q++){var t=l[q];if(t){t=t[g];for(var y=false;t;){if(t.sizcache=== |
||||
k){y=l[t.sizset];break}if(t.nodeType===1){if(!p){t.sizcache=k;t.sizset=q}if(typeof h!=="string"){if(t===h){y=true;break}}else if(o.filter(h,[t]).length>0){y=t;break}}t=t[g]}l[q]=y}}}var f=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,e=0,i=Object.prototype.toString,j=false,n=true;[0,0].sort(function(){n=false;return 0});var o=function(g,h,k,l){k=k||[];var q=h=h||r;if(h.nodeType!==1&&h.nodeType!==9)return[];if(!g|| |
||||
typeof g!=="string")return k;for(var p=[],u,t,y,R,H=true,M=w(h),I=g;(f.exec(""),u=f.exec(I))!==null;){I=u[3];p.push(u[1]);if(u[2]){R=u[3];break}}if(p.length>1&&s.exec(g))if(p.length===2&&m.relative[p[0]])t=fa(p[0]+p[1],h);else for(t=m.relative[p[0]]?[h]:o(p.shift(),h);p.length;){g=p.shift();if(m.relative[g])g+=p.shift();t=fa(g,t)}else{if(!l&&p.length>1&&h.nodeType===9&&!M&&m.match.ID.test(p[0])&&!m.match.ID.test(p[p.length-1])){u=o.find(p.shift(),h,M);h=u.expr?o.filter(u.expr,u.set)[0]:u.set[0]}if(h){u= |
||||
l?{expr:p.pop(),set:A(l)}:o.find(p.pop(),p.length===1&&(p[0]==="~"||p[0]==="+")&&h.parentNode?h.parentNode:h,M);t=u.expr?o.filter(u.expr,u.set):u.set;if(p.length>0)y=A(t);else H=false;for(;p.length;){var D=p.pop();u=D;if(m.relative[D])u=p.pop();else D="";if(u==null)u=h;m.relative[D](y,u,M)}}else y=[]}y||(y=t);y||o.error(D||g);if(i.call(y)==="[object Array]")if(H)if(h&&h.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&E(h,y[g])))k.push(t[g])}else for(g=0;y[g]!=null;g++)y[g]&& |
||||
y[g].nodeType===1&&k.push(t[g]);else k.push.apply(k,y);else A(y,k);if(R){o(R,q,k,l);o.uniqueSort(k)}return k};o.uniqueSort=function(g){if(C){j=n;g.sort(C);if(j)for(var h=1;h<g.length;h++)g[h]===g[h-1]&&g.splice(h--,1)}return g};o.matches=function(g,h){return o(g,null,null,h)};o.find=function(g,h,k){var l,q;if(!g)return[];for(var p=0,u=m.order.length;p<u;p++){var t=m.order[p];if(q=m.leftMatch[t].exec(g)){var y=q[1];q.splice(1,1);if(y.substr(y.length-1)!=="\\"){q[1]=(q[1]||"").replace(/\\/g,"");l=m.find[t](q, |
||||
h,k);if(l!=null){g=g.replace(m.match[t],"");break}}}}l||(l=h.getElementsByTagName("*"));return{set:l,expr:g}};o.filter=function(g,h,k,l){for(var q=g,p=[],u=h,t,y,R=h&&h[0]&&w(h[0]);g&&h.length;){for(var H in m.filter)if((t=m.leftMatch[H].exec(g))!=null&&t[2]){var M=m.filter[H],I,D;D=t[1];y=false;t.splice(1,1);if(D.substr(D.length-1)!=="\\"){if(u===p)p=[];if(m.preFilter[H])if(t=m.preFilter[H](t,u,k,p,l,R)){if(t===true)continue}else y=I=true;if(t)for(var U=0;(D=u[U])!=null;U++)if(D){I=M(D,t,U,u);var Da= |
||||
l^!!I;if(k&&I!=null)if(Da)y=true;else u[U]=false;else if(Da){p.push(D);y=true}}if(I!==v){k||(u=p);g=g.replace(m.match[H],"");if(!y)return[];break}}}if(g===q)if(y==null)o.error(g);else break;q=g}return u};o.error=function(g){throw"Syntax error, unrecognized expression: "+g;};var m=o.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF-]|\\.)+)/,CLASS:/\.((?:[\w\u00c0-\uFFFF-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/, |
||||
TAG:/^((?:[\w\u00c0-\uFFFF\*-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(g){return g.getAttribute("href")}},relative:{"+":function(g,h){var k=typeof h==="string",l=k&&!/\W/.test(h);k=k&&!l;if(l)h=h.toLowerCase();l=0;for(var q=g.length, |
||||
p;l<q;l++)if(p=g[l]){for(;(p=p.previousSibling)&&p.nodeType!==1;);g[l]=k||p&&p.nodeName.toLowerCase()===h?p||false:p===h}k&&o.filter(h,g,true)},">":function(g,h){var k=typeof h==="string";if(k&&!/\W/.test(h)){h=h.toLowerCase();for(var l=0,q=g.length;l<q;l++){var p=g[l];if(p){k=p.parentNode;g[l]=k.nodeName.toLowerCase()===h?k:false}}}else{l=0;for(q=g.length;l<q;l++)if(p=g[l])g[l]=k?p.parentNode:p.parentNode===h;k&&o.filter(h,g,true)}},"":function(g,h,k){var l=e++,q=d;if(typeof h==="string"&&!/\W/.test(h)){var p= |
||||
h=h.toLowerCase();q=b}q("parentNode",h,l,g,p,k)},"~":function(g,h,k){var l=e++,q=d;if(typeof h==="string"&&!/\W/.test(h)){var p=h=h.toLowerCase();q=b}q("previousSibling",h,l,g,p,k)}},find:{ID:function(g,h,k){if(typeof h.getElementById!=="undefined"&&!k)return(g=h.getElementById(g[1]))?[g]:[]},NAME:function(g,h){if(typeof h.getElementsByName!=="undefined"){var k=[];h=h.getElementsByName(g[1]);for(var l=0,q=h.length;l<q;l++)h[l].getAttribute("name")===g[1]&&k.push(h[l]);return k.length===0?null:k}}, |
||||
TAG:function(g,h){return h.getElementsByTagName(g[1])}},preFilter:{CLASS:function(g,h,k,l,q,p){g=" "+g[1].replace(/\\/g,"")+" ";if(p)return g;p=0;for(var u;(u=h[p])!=null;p++)if(u)if(q^(u.className&&(" "+u.className+" ").replace(/[\t\n]/g," ").indexOf(g)>=0))k||l.push(u);else if(k)h[p]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()},CHILD:function(g){if(g[1]==="nth"){var h=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&& |
||||
"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=h[1]+(h[2]||1)-0;g[3]=h[3]-0}g[0]=e++;return g},ATTR:function(g,h,k,l,q,p){h=g[1].replace(/\\/g,"");if(!p&&m.attrMap[h])g[1]=m.attrMap[h];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,h,k,l,q){if(g[1]==="not")if((f.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=o(g[3],null,null,h);else{g=o.filter(g[3],h,k,true^q);k||l.push.apply(l,g);return false}else if(m.match.POS.test(g[0])||m.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true); |
||||
return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled===true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,h,k){return!!o(k[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)},text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"=== |
||||
g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"===g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}},setFilters:{first:function(g,h){return h===0},last:function(g,h,k,l){return h===l.length-1},even:function(g,h){return h%2=== |
||||
0},odd:function(g,h){return h%2===1},lt:function(g,h,k){return h<k[3]-0},gt:function(g,h,k){return h>k[3]-0},nth:function(g,h,k){return k[3]-0===h},eq:function(g,h,k){return k[3]-0===h}},filter:{PSEUDO:function(g,h,k,l){var q=h[1],p=m.filters[q];if(p)return p(g,k,h,l);else if(q==="contains")return(g.textContent||g.innerText||a([g])||"").indexOf(h[3])>=0;else if(q==="not"){h=h[3];k=0;for(l=h.length;k<l;k++)if(h[k]===g)return false;return true}else o.error("Syntax error, unrecognized expression: "+ |
||||
q)},CHILD:function(g,h){var k=h[1],l=g;switch(k){case "only":case "first":for(;l=l.previousSibling;)if(l.nodeType===1)return false;if(k==="first")return true;l=g;case "last":for(;l=l.nextSibling;)if(l.nodeType===1)return false;return true;case "nth":k=h[2];var q=h[3];if(k===1&&q===0)return true;h=h[0];var p=g.parentNode;if(p&&(p.sizcache!==h||!g.nodeIndex)){var u=0;for(l=p.firstChild;l;l=l.nextSibling)if(l.nodeType===1)l.nodeIndex=++u;p.sizcache=h}g=g.nodeIndex-q;return k===0?g===0:g%k===0&&g/k>= |
||||
0}},ID:function(g,h){return g.nodeType===1&&g.getAttribute("id")===h},TAG:function(g,h){return h==="*"&&g.nodeType===1||g.nodeName.toLowerCase()===h},CLASS:function(g,h){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(h)>-1},ATTR:function(g,h){var k=h[1];g=m.attrHandle[k]?m.attrHandle[k](g):g[k]!=null?g[k]:g.getAttribute(k);k=g+"";var l=h[2];h=h[4];return g==null?l==="!=":l==="="?k===h:l==="*="?k.indexOf(h)>=0:l==="~="?(" "+k+" ").indexOf(h)>=0:!h?k&&g!==false:l==="!="?k!==h:l==="^="? |
||||
k.indexOf(h)===0:l==="$="?k.substr(k.length-h.length)===h:l==="|="?k===h||k.substr(0,h.length+1)===h+"-":false},POS:function(g,h,k,l){var q=m.setFilters[h[2]];if(q)return q(g,k,h,l)}}},s=m.match.POS;for(var x in m.match){m.match[x]=new RegExp(m.match[x].source+/(?![^\[]*\])(?![^\(]*\))/.source);m.leftMatch[x]=new RegExp(/(^(?:.|\r|\n)*?)/.source+m.match[x].source.replace(/\\(\d+)/g,function(g,h){return"\\"+(h-0+1)}))}var A=function(g,h){g=Array.prototype.slice.call(g,0);if(h){h.push.apply(h,g);return h}return g}; |
||||
try{Array.prototype.slice.call(r.documentElement.childNodes,0)}catch(B){A=function(g,h){h=h||[];if(i.call(g)==="[object Array]")Array.prototype.push.apply(h,g);else if(typeof g.length==="number")for(var k=0,l=g.length;k<l;k++)h.push(g[k]);else for(k=0;g[k];k++)h.push(g[k]);return h}}var C;if(r.documentElement.compareDocumentPosition)C=function(g,h){if(!g.compareDocumentPosition||!h.compareDocumentPosition){if(g==h)j=true;return g.compareDocumentPosition?-1:1}g=g.compareDocumentPosition(h)&4?-1:g=== |
||||
h?0:1;if(g===0)j=true;return g};else if("sourceIndex"in r.documentElement)C=function(g,h){if(!g.sourceIndex||!h.sourceIndex){if(g==h)j=true;return g.sourceIndex?-1:1}g=g.sourceIndex-h.sourceIndex;if(g===0)j=true;return g};else if(r.createRange)C=function(g,h){if(!g.ownerDocument||!h.ownerDocument){if(g==h)j=true;return g.ownerDocument?-1:1}var k=g.ownerDocument.createRange(),l=h.ownerDocument.createRange();k.setStart(g,0);k.setEnd(g,0);l.setStart(h,0);l.setEnd(h,0);g=k.compareBoundaryPoints(Range.START_TO_END, |
||||
l);if(g===0)j=true;return g};(function(){var g=r.createElement("div"),h="script"+(new Date).getTime();g.innerHTML="<a name='"+h+"'/>";var k=r.documentElement;k.insertBefore(g,k.firstChild);if(r.getElementById(h)){m.find.ID=function(l,q,p){if(typeof q.getElementById!=="undefined"&&!p)return(q=q.getElementById(l[1]))?q.id===l[1]||typeof q.getAttributeNode!=="undefined"&&q.getAttributeNode("id").nodeValue===l[1]?[q]:v:[]};m.filter.ID=function(l,q){var p=typeof l.getAttributeNode!=="undefined"&&l.getAttributeNode("id"); |
||||
return l.nodeType===1&&p&&p.nodeValue===q}}k.removeChild(g);k=g=null})();(function(){var g=r.createElement("div");g.appendChild(r.createComment(""));if(g.getElementsByTagName("*").length>0)m.find.TAG=function(h,k){k=k.getElementsByTagName(h[1]);if(h[1]==="*"){h=[];for(var l=0;k[l];l++)k[l].nodeType===1&&h.push(k[l]);k=h}return k};g.innerHTML="<a href='#'></a>";if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")m.attrHandle.href=function(h){return h.getAttribute("href", |
||||
2)};g=null})();r.querySelectorAll&&function(){var g=o,h=r.createElement("div");h.innerHTML="<p class='TEST'></p>";if(!(h.querySelectorAll&&h.querySelectorAll(".TEST").length===0)){o=function(l,q,p,u){q=q||r;if(!u&&q.nodeType===9&&!w(q))try{return A(q.querySelectorAll(l),p)}catch(t){}return g(l,q,p,u)};for(var k in g)o[k]=g[k];h=null}}();(function(){var g=r.createElement("div");g.innerHTML="<div class='test e'></div><div class='test'></div>";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length=== |
||||
0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){m.order.splice(1,0,"CLASS");m.find.CLASS=function(h,k,l){if(typeof k.getElementsByClassName!=="undefined"&&!l)return k.getElementsByClassName(h[1])};g=null}}})();var E=r.compareDocumentPosition?function(g,h){return g.compareDocumentPosition(h)&16}:function(g,h){return g!==h&&(g.contains?g.contains(h):true)},w=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false},fa=function(g,h){var k=[], |
||||
l="",q;for(h=h.nodeType?[h]:h;q=m.match.PSEUDO.exec(g);){l+=q[0];g=g.replace(m.match.PSEUDO,"")}g=m.relative[g]?g+"*":g;q=0;for(var p=h.length;q<p;q++)o(g,h[q],k);return o.filter(l,k)};c.find=o;c.expr=o.selectors;c.expr[":"]=c.expr.filters;c.unique=o.uniqueSort;c.getText=a;c.isXMLDoc=w;c.contains=E})();var bb=/Until$/,cb=/^(?:parents|prevUntil|prevAll)/,db=/,/;Q=Array.prototype.slice;var Ea=function(a,b,d){if(c.isFunction(b))return c.grep(a,function(e,i){return!!b.call(e,i,e)===d});else if(b.nodeType)return c.grep(a, |
||||
function(e){return e===b===d});else if(typeof b==="string"){var f=c.grep(a,function(e){return e.nodeType===1});if(Qa.test(b))return c.filter(b,f,!d);else b=c.filter(b,f)}return c.grep(a,function(e){return c.inArray(e,b)>=0===d})};c.fn.extend({find:function(a){for(var b=this.pushStack("","find",a),d=0,f=0,e=this.length;f<e;f++){d=b.length;c.find(a,this[f],b);if(f>0)for(var i=d;i<b.length;i++)for(var j=0;j<d;j++)if(b[j]===b[i]){b.splice(i--,1);break}}return b},has:function(a){var b=c(a);return this.filter(function(){for(var d= |
||||
0,f=b.length;d<f;d++)if(c.contains(this,b[d]))return true})},not:function(a){return this.pushStack(Ea(this,a,false),"not",a)},filter:function(a){return this.pushStack(Ea(this,a,true),"filter",a)},is:function(a){return!!a&&c.filter(a,this).length>0},closest:function(a,b){if(c.isArray(a)){var d=[],f=this[0],e,i={},j;if(f&&a.length){e=0;for(var n=a.length;e<n;e++){j=a[e];i[j]||(i[j]=c.expr.match.POS.test(j)?c(j,b||this.context):j)}for(;f&&f.ownerDocument&&f!==b;){for(j in i){e=i[j];if(e.jquery?e.index(f)> |
||||
-1:c(f).is(e)){d.push({selector:j,elem:f});delete i[j]}}f=f.parentNode}}return d}var o=c.expr.match.POS.test(a)?c(a,b||this.context):null;return this.map(function(m,s){for(;s&&s.ownerDocument&&s!==b;){if(o?o.index(s)>-1:c(s).is(a))return s;s=s.parentNode}return null})},index:function(a){if(!a||typeof a==="string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){a=typeof a==="string"?c(a,b||this.context):c.makeArray(a);b=c.merge(this.get(), |
||||
a);return this.pushStack(pa(a[0])||pa(b[0])?b:c.unique(b))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode",d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a,2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")}, |
||||
nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a,b){c.fn[a]=function(d,f){var e=c.map(this,b,d);bb.test(a)||(f=d);if(f&&typeof f==="string")e=c.filter(f,e);e=this.length>1?c.unique(e): |
||||
e;if((this.length>1||db.test(f))&&cb.test(a))e=e.reverse();return this.pushStack(e,a,Q.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return c.find.matches(a,b)},dir:function(a,b,d){var f=[];for(a=a[b];a&&a.nodeType!==9&&(d===v||a.nodeType!==1||!c(a).is(d));){a.nodeType===1&&f.push(a);a=a[b]}return f},nth:function(a,b,d){b=b||1;for(var f=0;a;a=a[d])if(a.nodeType===1&&++f===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!== |
||||
b&&d.push(a);return d}});var Fa=/ jQuery\d+="(?:\d+|null)"/g,V=/^\s+/,Ga=/(<([\w:]+)[^>]*?)\/>/g,eb=/^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,Ha=/<([\w:]+)/,fb=/<tbody/i,gb=/<|&\w+;/,sa=/checked\s*(?:[^=]|=\s*.checked.)/i,Ia=function(a,b,d){return eb.test(d)?a:b+"></"+d+">"},F={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"], |
||||
col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]};F.optgroup=F.option;F.tbody=F.tfoot=F.colgroup=F.caption=F.thead;F.th=F.td;if(!c.support.htmlSerialize)F._default=[1,"div<div>","</div>"];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d=c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==v)return this.empty().append((this[0]&&this[0].ownerDocument||r).createTextNode(a));return c.getText(this)}, |
||||
wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this},wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length? |
||||
d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments, |
||||
false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&& |
||||
!c.isXMLDoc(this)){var d=this.outerHTML,f=this.ownerDocument;if(!d){d=f.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(Fa,"").replace(V,"")],f)[0]}else return this.cloneNode(true)});if(a===true){qa(this,b);qa(this.find("*"),b.find("*"))}return b},html:function(a){if(a===v)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(Fa,""):null;else if(typeof a==="string"&&!/<script/i.test(a)&&(c.support.leadingWhitespace||!V.test(a))&&!F[(Ha.exec(a)|| |
||||
["",""])[1].toLowerCase()]){a=a.replace(Ga,Ia);try{for(var b=0,d=this.length;b<d;b++)if(this[b].nodeType===1){c.cleanData(this[b].getElementsByTagName("*"));this[b].innerHTML=a}}catch(f){this.empty().append(a)}}else c.isFunction(a)?this.each(function(e){var i=c(this),j=i.html();i.empty().append(function(){return a.call(this,e,j)})}):this.empty().append(a);return this},replaceWith:function(a){if(this[0]&&this[0].parentNode){if(c.isFunction(a))return this.each(function(b){var d=c(this),f=d.html();d.replaceWith(a.call(this, |
||||
b,f))});else a=c(a).detach();return this.each(function(){var b=this.nextSibling,d=this.parentNode;c(this).remove();b?c(b).before(a):c(d).append(a)})}else return this.pushStack(c(c.isFunction(a)?a():a),"replaceWith",a)},detach:function(a){return this.remove(a,true)},domManip:function(a,b,d){function f(s){return c.nodeName(s,"table")?s.getElementsByTagName("tbody")[0]||s.appendChild(s.ownerDocument.createElement("tbody")):s}var e,i,j=a[0],n=[];if(!c.support.checkClone&&arguments.length===3&&typeof j=== |
||||
"string"&&sa.test(j))return this.each(function(){c(this).domManip(a,b,d,true)});if(c.isFunction(j))return this.each(function(s){var x=c(this);a[0]=j.call(this,s,b?x.html():v);x.domManip(a,b,d)});if(this[0]){e=a[0]&&a[0].parentNode&&a[0].parentNode.nodeType===11?{fragment:a[0].parentNode}:ra(a,this,n);if(i=e.fragment.firstChild){b=b&&c.nodeName(i,"tr");for(var o=0,m=this.length;o<m;o++)d.call(b?f(this[o],i):this[o],e.cacheable||this.length>1||o>0?e.fragment.cloneNode(true):e.fragment)}n&&c.each(n, |
||||
Ma)}return this}});c.fragments={};c.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=function(d){var f=[];d=c(d);for(var e=0,i=d.length;e<i;e++){var j=(e>0?this.clone(true):this).get();c.fn[b].apply(c(d[e]),j);f=f.concat(j)}return this.pushStack(f,a,d.selector)}});c.each({remove:function(a,b){if(!a||c.filter(a,[this]).length){if(!b&&this.nodeType===1){c.cleanData(this.getElementsByTagName("*"));c.cleanData([this])}this.parentNode&& |
||||
this.parentNode.removeChild(this)}},empty:function(){for(this.nodeType===1&&c.cleanData(this.getElementsByTagName("*"));this.firstChild;)this.removeChild(this.firstChild)}},function(a,b){c.fn[a]=function(){return this.each(b,arguments)}});c.extend({clean:function(a,b,d,f){b=b||r;if(typeof b.createElement==="undefined")b=b.ownerDocument||b[0]&&b[0].ownerDocument||r;var e=[];c.each(a,function(i,j){if(typeof j==="number")j+="";if(j){if(typeof j==="string"&&!gb.test(j))j=b.createTextNode(j);else if(typeof j=== |
||||
"string"){j=j.replace(Ga,Ia);var n=(Ha.exec(j)||["",""])[1].toLowerCase(),o=F[n]||F._default,m=o[0];i=b.createElement("div");for(i.innerHTML=o[1]+j+o[2];m--;)i=i.lastChild;if(!c.support.tbody){m=fb.test(j);n=n==="table"&&!m?i.firstChild&&i.firstChild.childNodes:o[1]==="<table>"&&!m?i.childNodes:[];for(o=n.length-1;o>=0;--o)c.nodeName(n[o],"tbody")&&!n[o].childNodes.length&&n[o].parentNode.removeChild(n[o])}!c.support.leadingWhitespace&&V.test(j)&&i.insertBefore(b.createTextNode(V.exec(j)[0]),i.firstChild); |
||||
j=c.makeArray(i.childNodes)}if(j.nodeType)e.push(j);else e=c.merge(e,j)}});if(d)for(a=0;e[a];a++)if(f&&c.nodeName(e[a],"script")&&(!e[a].type||e[a].type.toLowerCase()==="text/javascript"))f.push(e[a].parentNode?e[a].parentNode.removeChild(e[a]):e[a]);else{e[a].nodeType===1&&e.splice.apply(e,[a+1,0].concat(c.makeArray(e[a].getElementsByTagName("script"))));d.appendChild(e[a])}return e},cleanData:function(a){for(var b=0,d;(d=a[b])!=null;b++){c.event.remove(d);c.removeData(d)}}});var hb=/z-?index|font-?weight|opacity|zoom|line-?height/i, |
||||
Ja=/alpha\([^)]*\)/,Ka=/opacity=([^)]*)/,ga=/float/i,ha=/-([a-z])/ig,ib=/([A-Z])/g,jb=/^-?\d+(?:px)?$/i,kb=/^-?\d/,lb={position:"absolute",visibility:"hidden",display:"block"},mb=["Left","Right"],nb=["Top","Bottom"],ob=r.defaultView&&r.defaultView.getComputedStyle,La=c.support.cssFloat?"cssFloat":"styleFloat",ia=function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){return X(this,a,b,true,function(d,f,e){if(e===v)return c.curCSS(d,f);if(typeof e==="number"&&!hb.test(f))e+="px";c.style(d,f,e)})}; |
||||
c.extend({style:function(a,b,d){if(!a||a.nodeType===3||a.nodeType===8)return v;if((b==="width"||b==="height")&&parseFloat(d)<0)d=v;var f=a.style||a,e=d!==v;if(!c.support.opacity&&b==="opacity"){if(e){f.zoom=1;b=parseInt(d,10)+""==="NaN"?"":"alpha(opacity="+d*100+")";a=f.filter||c.curCSS(a,"filter")||"";f.filter=Ja.test(a)?a.replace(Ja,b):b}return f.filter&&f.filter.indexOf("opacity=")>=0?parseFloat(Ka.exec(f.filter)[1])/100+"":""}if(ga.test(b))b=La;b=b.replace(ha,ia);if(e)f[b]=d;return f[b]},css:function(a, |
||||
b,d,f){if(b==="width"||b==="height"){var e,i=b==="width"?mb:nb;function j(){e=b==="width"?a.offsetWidth:a.offsetHeight;f!=="border"&&c.each(i,function(){f||(e-=parseFloat(c.curCSS(a,"padding"+this,true))||0);if(f==="margin")e+=parseFloat(c.curCSS(a,"margin"+this,true))||0;else e-=parseFloat(c.curCSS(a,"border"+this+"Width",true))||0})}a.offsetWidth!==0?j():c.swap(a,lb,j);return Math.max(0,Math.round(e))}return c.curCSS(a,b,d)},curCSS:function(a,b,d){var f,e=a.style;if(!c.support.opacity&&b==="opacity"&& |
||||
a.currentStyle){f=Ka.test(a.currentStyle.filter||"")?parseFloat(RegExp.$1)/100+"":"";return f===""?"1":f}if(ga.test(b))b=La;if(!d&&e&&e[b])f=e[b];else if(ob){if(ga.test(b))b="float";b=b.replace(ib,"-$1").toLowerCase();e=a.ownerDocument.defaultView;if(!e)return null;if(a=e.getComputedStyle(a,null))f=a.getPropertyValue(b);if(b==="opacity"&&f==="")f="1"}else if(a.currentStyle){d=b.replace(ha,ia);f=a.currentStyle[b]||a.currentStyle[d];if(!jb.test(f)&&kb.test(f)){b=e.left;var i=a.runtimeStyle.left;a.runtimeStyle.left= |
||||
a.currentStyle.left;e.left=d==="fontSize"?"1em":f||0;f=e.pixelLeft+"px";e.left=b;a.runtimeStyle.left=i}}return f},swap:function(a,b,d){var f={};for(var e in b){f[e]=a.style[e];a.style[e]=b[e]}d.call(a);for(e in b)a.style[e]=f[e]}});if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b=a.offsetWidth,d=a.offsetHeight,f=a.nodeName.toLowerCase()==="tr";return b===0&&d===0&&!f?true:b>0&&d>0&&!f?false:c.curCSS(a,"display")==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var pb= |
||||
J(),qb=/<script(.|\s)*?\/script>/gi,rb=/select|textarea/i,sb=/color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i,N=/=\?(&|$)/,ja=/\?/,tb=/(\?|&)_=.*?(&|$)/,ub=/^(\w+:)?\/\/([^\/?#]+)/,vb=/%20/g;c.fn.extend({_load:c.fn.load,load:function(a,b,d){if(typeof a!=="string")return this._load(a);else if(!this.length)return this;var f=a.indexOf(" ");if(f>=0){var e=a.slice(f,a.length);a=a.slice(0,f)}f="GET";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b==="object"){b= |
||||
c.param(b,c.ajaxSettings.traditional);f="POST"}var i=this;c.ajax({url:a,type:f,dataType:"html",data:b,complete:function(j,n){if(n==="success"||n==="notmodified")i.html(e?c("<div />").append(j.responseText.replace(qb,"")).find(e):j.responseText);d&&i.each(d,[j.responseText,n,j])}});return this},serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&& |
||||
(this.checked||rb.test(this.nodeName)||sb.test(this.type))}).map(function(a,b){a=c(this).val();return a==null?null:c.isArray(a)?c.map(a,function(d){return{name:b.name,value:d}}):{name:b.name,value:a}}).get()}});c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:f})},getScript:function(a, |
||||
b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:f})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href,global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:z.XMLHttpRequest&&(z.location.protocol!=="file:"||!z.ActiveXObject)?function(){return new z.XMLHttpRequest}: |
||||
function(){try{return new z.ActiveXObject("Microsoft.XMLHTTP")}catch(a){}},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},etag:{},ajax:function(a){function b(){e.success&&e.success.call(o,n,j,w);e.global&&f("ajaxSuccess",[w,e])}function d(){e.complete&&e.complete.call(o,w,j);e.global&&f("ajaxComplete",[w,e]);e.global&&!--c.active&&c.event.trigger("ajaxStop")} |
||||
function f(q,p){(e.context?c(e.context):c.event).trigger(q,p)}var e=c.extend(true,{},c.ajaxSettings,a),i,j,n,o=a&&a.context||e,m=e.type.toUpperCase();if(e.data&&e.processData&&typeof e.data!=="string")e.data=c.param(e.data,e.traditional);if(e.dataType==="jsonp"){if(m==="GET")N.test(e.url)||(e.url+=(ja.test(e.url)?"&":"?")+(e.jsonp||"callback")+"=?");else if(!e.data||!N.test(e.data))e.data=(e.data?e.data+"&":"")+(e.jsonp||"callback")+"=?";e.dataType="json"}if(e.dataType==="json"&&(e.data&&N.test(e.data)|| |
||||
N.test(e.url))){i=e.jsonpCallback||"jsonp"+pb++;if(e.data)e.data=(e.data+"").replace(N,"="+i+"$1");e.url=e.url.replace(N,"="+i+"$1");e.dataType="script";z[i]=z[i]||function(q){n=q;b();d();z[i]=v;try{delete z[i]}catch(p){}A&&A.removeChild(B)}}if(e.dataType==="script"&&e.cache===null)e.cache=false;if(e.cache===false&&m==="GET"){var s=J(),x=e.url.replace(tb,"$1_="+s+"$2");e.url=x+(x===e.url?(ja.test(e.url)?"&":"?")+"_="+s:"")}if(e.data&&m==="GET")e.url+=(ja.test(e.url)?"&":"?")+e.data;e.global&&!c.active++&& |
||||
c.event.trigger("ajaxStart");s=(s=ub.exec(e.url))&&(s[1]&&s[1]!==location.protocol||s[2]!==location.host);if(e.dataType==="script"&&m==="GET"&&s){var A=r.getElementsByTagName("head")[0]||r.documentElement,B=r.createElement("script");B.src=e.url;if(e.scriptCharset)B.charset=e.scriptCharset;if(!i){var C=false;B.onload=B.onreadystatechange=function(){if(!C&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){C=true;b();d();B.onload=B.onreadystatechange=null;A&&B.parentNode&& |
||||
A.removeChild(B)}}}A.insertBefore(B,A.firstChild);return v}var E=false,w=e.xhr();if(w){e.username?w.open(m,e.url,e.async,e.username,e.password):w.open(m,e.url,e.async);try{if(e.data||a&&a.contentType)w.setRequestHeader("Content-Type",e.contentType);if(e.ifModified){c.lastModified[e.url]&&w.setRequestHeader("If-Modified-Since",c.lastModified[e.url]);c.etag[e.url]&&w.setRequestHeader("If-None-Match",c.etag[e.url])}s||w.setRequestHeader("X-Requested-With","XMLHttpRequest");w.setRequestHeader("Accept", |
||||
e.dataType&&e.accepts[e.dataType]?e.accepts[e.dataType]+", */*":e.accepts._default)}catch(fa){}if(e.beforeSend&&e.beforeSend.call(o,w,e)===false){e.global&&!--c.active&&c.event.trigger("ajaxStop");w.abort();return false}e.global&&f("ajaxSend",[w,e]);var g=w.onreadystatechange=function(q){if(!w||w.readyState===0||q==="abort"){E||d();E=true;if(w)w.onreadystatechange=c.noop}else if(!E&&w&&(w.readyState===4||q==="timeout")){E=true;w.onreadystatechange=c.noop;j=q==="timeout"?"timeout":!c.httpSuccess(w)? |
||||
"error":e.ifModified&&c.httpNotModified(w,e.url)?"notmodified":"success";var p;if(j==="success")try{n=c.httpData(w,e.dataType,e)}catch(u){j="parsererror";p=u}if(j==="success"||j==="notmodified")i||b();else c.handleError(e,w,j,p);d();q==="timeout"&&w.abort();if(e.async)w=null}};try{var h=w.abort;w.abort=function(){w&&h.call(w);g("abort")}}catch(k){}e.async&&e.timeout>0&&setTimeout(function(){w&&!E&&g("timeout")},e.timeout);try{w.send(m==="POST"||m==="PUT"||m==="DELETE"?e.data:null)}catch(l){c.handleError(e, |
||||
w,null,l);d()}e.async||g();return w}},handleError:function(a,b,d,f){if(a.error)a.error.call(a.context||a,b,d,f);if(a.global)(a.context?c(a.context):c.event).trigger("ajaxError",[b,a,f])},active:0,httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status===1223||a.status===0}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"),f=a.getResponseHeader("Etag");if(d)c.lastModified[b]=d;if(f)c.etag[b]= |
||||
f;return a.status===304||a.status===0},httpData:function(a,b,d){var f=a.getResponseHeader("content-type")||"",e=b==="xml"||!b&&f.indexOf("xml")>=0;a=e?a.responseXML:a.responseText;e&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b==="json"||!b&&f.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&f.indexOf("javascript")>=0)c.globalEval(a);return a},param:function(a,b){function d(j,n){if(c.isArray(n))c.each(n, |
||||
function(o,m){b?f(j,m):d(j+"["+(typeof m==="object"||c.isArray(m)?o:"")+"]",m)});else!b&&n!=null&&typeof n==="object"?c.each(n,function(o,m){d(j+"["+o+"]",m)}):f(j,n)}function f(j,n){n=c.isFunction(n)?n():n;e[e.length]=encodeURIComponent(j)+"="+encodeURIComponent(n)}var e=[];if(b===v)b=c.ajaxSettings.traditional;if(c.isArray(a)||a.jquery)c.each(a,function(){f(this.name,this.value)});else for(var i in a)d(i,a[i]);return e.join("&").replace(vb,"+")}});var ka={},wb=/toggle|show|hide/,xb=/^([+-]=)?([\d+-.]+)(.*)$/, |
||||
W,ta=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];c.fn.extend({show:function(a,b){if(a||a===0)return this.animate(K("show",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");this[a].style.display=d||"";if(c.css(this[a],"display")==="none"){d=this[a].nodeName;var f;if(ka[d])f=ka[d];else{var e=c("<"+d+" />").appendTo("body");f=e.css("display");if(f==="none")f="block";e.remove(); |
||||
ka[d]=f}c.data(this[a],"olddisplay",f)}}a=0;for(b=this.length;a<b;a++)this[a].style.display=c.data(this[a],"olddisplay")||"";return this}},hide:function(a,b){if(a||a===0)return this.animate(K("hide",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");!d&&d!=="none"&&c.data(this[a],"olddisplay",c.css(this[a],"display"))}a=0;for(b=this.length;a<b;a++)this[a].style.display="none";return this}},_toggle:c.fn.toggle,toggle:function(a,b){var d=typeof a==="boolean";if(c.isFunction(a)&& |
||||
c.isFunction(b))this._toggle.apply(this,arguments);else a==null||d?this.each(function(){var f=d?a:c(this).is(":hidden");c(this)[f?"show":"hide"]()}):this.animate(K("toggle",3),a,b);return this},fadeTo:function(a,b,d){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,d)},animate:function(a,b,d,f){var e=c.speed(b,d,f);if(c.isEmptyObject(a))return this.each(e.complete);return this[e.queue===false?"each":"queue"](function(){var i=c.extend({},e),j,n=this.nodeType===1&&c(this).is(":hidden"), |
||||
o=this;for(j in a){var m=j.replace(ha,ia);if(j!==m){a[m]=a[j];delete a[j];j=m}if(a[j]==="hide"&&n||a[j]==="show"&&!n)return i.complete.call(this);if((j==="height"||j==="width")&&this.style){i.display=c.css(this,"display");i.overflow=this.style.overflow}if(c.isArray(a[j])){(i.specialEasing=i.specialEasing||{})[j]=a[j][1];a[j]=a[j][0]}}if(i.overflow!=null)this.style.overflow="hidden";i.curAnim=c.extend({},a);c.each(a,function(s,x){var A=new c.fx(o,i,s);if(wb.test(x))A[x==="toggle"?n?"show":"hide":x](a); |
||||
else{var B=xb.exec(x),C=A.cur(true)||0;if(B){x=parseFloat(B[2]);var E=B[3]||"px";if(E!=="px"){o.style[s]=(x||1)+E;C=(x||1)/A.cur(true)*C;o.style[s]=C+E}if(B[1])x=(B[1]==="-="?-1:1)*x+C;A.custom(C,x,E)}else A.custom(C,x,"")}});return true})},stop:function(a,b){var d=c.timers;a&&this.queue([]);this.each(function(){for(var f=d.length-1;f>=0;f--)if(d[f].elem===this){b&&d[f](true);d.splice(f,1)}});b||this.dequeue();return this}});c.each({slideDown:K("show",1),slideUp:K("hide",1),slideToggle:K("toggle", |
||||
1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(a,b){c.fn[a]=function(d,f){return this.animate(b,d,f)}});c.extend({speed:function(a,b,d){var f=a&&typeof a==="object"?a:{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};f.duration=c.fx.off?0:typeof f.duration==="number"?f.duration:c.fx.speeds[f.duration]||c.fx.speeds._default;f.old=f.complete;f.complete=function(){f.queue!==false&&c(this).dequeue();c.isFunction(f.old)&&f.old.call(this)};return f},easing:{linear:function(a, |
||||
b,d,f){return d+f*a},swing:function(a,b,d,f){return(-Math.cos(a*Math.PI)/2+0.5)*f+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]||c.fx.step._default)(this);if((this.prop==="height"||this.prop==="width")&&this.elem.style)this.elem.style.display="block"},cur:function(a){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]== |
||||
null))return this.elem[this.prop];return(a=parseFloat(c.css(this.elem,this.prop,a)))&&a>-10000?a:parseFloat(c.curCSS(this.elem,this.prop))||0},custom:function(a,b,d){function f(i){return e.step(i)}this.startTime=J();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start;this.pos=this.state=0;var e=this;f.elem=this.elem;if(f()&&c.timers.push(f)&&!W)W=setInterval(c.fx.tick,13)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop=== |
||||
"width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(a){var b=J(),d=true;if(a||b>=this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var f in this.options.curAnim)if(this.options.curAnim[f]!==true)d=false;if(d){if(this.options.display!=null){this.elem.style.overflow= |
||||
this.options.overflow;a=c.data(this.elem,"olddisplay");this.elem.style.display=a?a:this.options.display;if(c.css(this.elem,"display")==="none")this.elem.style.display="block"}this.options.hide&&c(this.elem).hide();if(this.options.hide||this.options.show)for(var e in this.options.curAnim)c.style(this.elem,e,this.options.orig[e]);this.options.complete.call(this.elem)}return false}else{e=b-this.startTime;this.state=e/this.options.duration;a=this.options.easing||(c.easing.swing?"swing":"linear");this.pos= |
||||
c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||a](this.state,e,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a=c.timers,b=0;b<a.length;b++)a[b]()||a.splice(b--,1);a.length||c.fx.stop()},stop:function(){clearInterval(W);W=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){c.style(a.elem,"opacity",a.now)},_default:function(a){if(a.elem.style&&a.elem.style[a.prop]!= |
||||
null)a.elem.style[a.prop]=(a.prop==="width"||a.prop==="height"?Math.max(0,a.now):a.now)+a.unit;else a.elem[a.prop]=a.now}}});if(c.expr&&c.expr.filters)c.expr.filters.animated=function(a){return c.grep(c.timers,function(b){return a===b.elem}).length};c.fn.offset="getBoundingClientRect"in r.documentElement?function(a){var b=this[0];if(a)return this.each(function(e){c.offset.setOffset(this,a,e)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);var d=b.getBoundingClientRect(), |
||||
f=b.ownerDocument;b=f.body;f=f.documentElement;return{top:d.top+(self.pageYOffset||c.support.boxModel&&f.scrollTop||b.scrollTop)-(f.clientTop||b.clientTop||0),left:d.left+(self.pageXOffset||c.support.boxModel&&f.scrollLeft||b.scrollLeft)-(f.clientLeft||b.clientLeft||0)}}:function(a){var b=this[0];if(a)return this.each(function(s){c.offset.setOffset(this,a,s)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);c.offset.initialize();var d=b.offsetParent,f= |
||||
b,e=b.ownerDocument,i,j=e.documentElement,n=e.body;f=(e=e.defaultView)?e.getComputedStyle(b,null):b.currentStyle;for(var o=b.offsetTop,m=b.offsetLeft;(b=b.parentNode)&&b!==n&&b!==j;){if(c.offset.supportsFixedPosition&&f.position==="fixed")break;i=e?e.getComputedStyle(b,null):b.currentStyle;o-=b.scrollTop;m-=b.scrollLeft;if(b===d){o+=b.offsetTop;m+=b.offsetLeft;if(c.offset.doesNotAddBorder&&!(c.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(b.nodeName))){o+=parseFloat(i.borderTopWidth)|| |
||||
0;m+=parseFloat(i.borderLeftWidth)||0}f=d;d=b.offsetParent}if(c.offset.subtractsBorderForOverflowNotVisible&&i.overflow!=="visible"){o+=parseFloat(i.borderTopWidth)||0;m+=parseFloat(i.borderLeftWidth)||0}f=i}if(f.position==="relative"||f.position==="static"){o+=n.offsetTop;m+=n.offsetLeft}if(c.offset.supportsFixedPosition&&f.position==="fixed"){o+=Math.max(j.scrollTop,n.scrollTop);m+=Math.max(j.scrollLeft,n.scrollLeft)}return{top:o,left:m}};c.offset={initialize:function(){var a=r.body,b=r.createElement("div"), |
||||
d,f,e,i=parseFloat(c.curCSS(a,"marginTop",true))||0;c.extend(b.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"});b.innerHTML="<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>";a.insertBefore(b,a.firstChild); |
||||
d=b.firstChild;f=d.firstChild;e=d.nextSibling.firstChild.firstChild;this.doesNotAddBorder=f.offsetTop!==5;this.doesAddBorderForTableAndCells=e.offsetTop===5;f.style.position="fixed";f.style.top="20px";this.supportsFixedPosition=f.offsetTop===20||f.offsetTop===15;f.style.position=f.style.top="";d.style.overflow="hidden";d.style.position="relative";this.subtractsBorderForOverflowNotVisible=f.offsetTop===-5;this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==i;a.removeChild(b);c.offset.initialize=c.noop}, |
||||
bodyOffset:function(a){var b=a.offsetTop,d=a.offsetLeft;c.offset.initialize();if(c.offset.doesNotIncludeMarginInBodyOffset){b+=parseFloat(c.curCSS(a,"marginTop",true))||0;d+=parseFloat(c.curCSS(a,"marginLeft",true))||0}return{top:b,left:d}},setOffset:function(a,b,d){if(/static/.test(c.curCSS(a,"position")))a.style.position="relative";var f=c(a),e=f.offset(),i=parseInt(c.curCSS(a,"top",true),10)||0,j=parseInt(c.curCSS(a,"left",true),10)||0;if(c.isFunction(b))b=b.call(a,d,e);d={top:b.top-e.top+i,left:b.left- |
||||
e.left+j};"using"in b?b.using.call(a,d):f.css(d)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),f=/^body|html$/i.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.curCSS(a,"marginTop",true))||0;d.left-=parseFloat(c.curCSS(a,"marginLeft",true))||0;f.top+=parseFloat(c.curCSS(b[0],"borderTopWidth",true))||0;f.left+=parseFloat(c.curCSS(b[0],"borderLeftWidth",true))||0;return{top:d.top-f.top,left:d.left-f.left}},offsetParent:function(){return this.map(function(){for(var a= |
||||
this.offsetParent||r.body;a&&!/^body|html$/i.test(a.nodeName)&&c.css(a,"position")==="static";)a=a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(f){var e=this[0],i;if(!e)return null;if(f!==v)return this.each(function(){if(i=ua(this))i.scrollTo(!a?f:c(i).scrollLeft(),a?f:c(i).scrollTop());else this[d]=f});else return(i=ua(e))?"pageXOffset"in i?i[a?"pageYOffset":"pageXOffset"]:c.support.boxModel&&i.document.documentElement[d]||i.document.body[d]:e[d]}}); |
||||
c.each(["Height","Width"],function(a,b){var d=b.toLowerCase();c.fn["inner"+b]=function(){return this[0]?c.css(this[0],d,false,"padding"):null};c.fn["outer"+b]=function(f){return this[0]?c.css(this[0],d,false,f?"margin":"border"):null};c.fn[d]=function(f){var e=this[0];if(!e)return f==null?null:this;if(c.isFunction(f))return this.each(function(i){var j=c(this);j[d](f.call(this,i,j[d]()))});return"scrollTo"in e&&e.document?e.document.compatMode==="CSS1Compat"&&e.document.documentElement["client"+b]|| |
||||
e.document.body["client"+b]:e.nodeType===9?Math.max(e.documentElement["client"+b],e.body["scroll"+b],e.documentElement["scroll"+b],e.body["offset"+b],e.documentElement["offset"+b]):f===v?c.css(e,d):this.css(d,typeof f==="string"?f:f+"px")}});z.jQuery=z.$=c})(window); |
||||
Binary file not shown.
@ -0,0 +1,28 @@ |
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> |
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"> |
||||
<head> |
||||
<title>SWFObject 2 static publishing example page</title> |
||||
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> |
||||
<script type="text/javascript" src="swfobject.js"></script> |
||||
<script type="text/javascript"> |
||||
swfobject.registerObject("myId", "9.0.0", "expressInstall.swf"); |
||||
</script> |
||||
</head> |
||||
<body> |
||||
<div> |
||||
<object id="myId" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="300" height="120"> |
||||
<param name="movie" value="test.swf" /> |
||||
<!--[if !IE]>--> |
||||
<object type="application/x-shockwave-flash" data="test.swf" width="300" height="120"> |
||||
<!--<![endif]--> |
||||
<div> |
||||
<h1>Alternative content</h1> |
||||
<p><a href="http://www.adobe.com/go/getflashplayer"><img src="http://www.adobe.com/images/shared/download_buttons/get_flash_player.gif" alt="Get Adobe Flash player" /></a></p> |
||||
</div> |
||||
<!--[if !IE]>--> |
||||
</object> |
||||
<!--<![endif]--> |
||||
</object> |
||||
</div> |
||||
</body> |
||||
</html> |
||||
@ -0,0 +1,17 @@ |
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> |
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"> |
||||
<head> |
||||
<title>SWFObject 2 dynamic publishing example page</title> |
||||
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> |
||||
<script type="text/javascript" src="swfobject.js"></script> |
||||
<script type="text/javascript"> |
||||
swfobject.embedSWF("test.swf", "myContent", "300", "120", "9.0.0", "expressInstall.swf"); |
||||
</script> |
||||
</head> |
||||
<body> |
||||
<div id="myContent"> |
||||
<h1>Alternative content</h1> |
||||
<p><a href="http://www.adobe.com/go/getflashplayer"><img src="http://www.adobe.com/images/shared/download_buttons/get_flash_player.gif" alt="Get Adobe Flash player" /></a></p> |
||||
</div> |
||||
</body> |
||||
</html> |
||||
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
@ -0,0 +1,708 @@ |
||||
"""Unit tests for emencia.django.newsletter""" |
||||
from datetime import datetime |
||||
from datetime import timedelta |
||||
from tempfile import NamedTemporaryFile |
||||
|
||||
from django.test import TestCase |
||||
from django.http import Http404 |
||||
from django.db import IntegrityError |
||||
from django.core.files import File |
||||
|
||||
from emencia.django.newsletter.mailer import Mailer |
||||
from emencia.django.newsletter.models import Link |
||||
from emencia.django.newsletter.models import Contact |
||||
from emencia.django.newsletter.models import MailingList |
||||
from emencia.django.newsletter.models import SMTPServer |
||||
from emencia.django.newsletter.models import Newsletter |
||||
from emencia.django.newsletter.models import Attachment |
||||
from emencia.django.newsletter.models import ContactMailingStatus |
||||
from emencia.django.newsletter.utils.tokens import tokenize |
||||
from emencia.django.newsletter.utils.tokens import untokenize |
||||
from emencia.django.newsletter.utils.statistics import get_newsletter_opening_statistics |
||||
from emencia.django.newsletter.utils.statistics import get_newsletter_on_site_opening_statistics |
||||
from emencia.django.newsletter.utils.statistics import get_newsletter_unsubscription_statistics |
||||
from emencia.django.newsletter.utils.statistics import get_newsletter_clicked_link_statistics |
||||
from emencia.django.newsletter.utils.statistics import get_newsletter_top_links |
||||
from emencia.django.newsletter.utils.statistics import get_newsletter_statistics |
||||
|
||||
|
||||
class FakeSMTP(object): |
||||
mails_sent = 0 |
||||
|
||||
def sendmail(self, *ka, **kw): |
||||
self.mails_sent += 1 |
||||
return {} |
||||
|
||||
def quit(*ka, **kw): |
||||
pass |
||||
|
||||
|
||||
class SMTPServerTestCase(TestCase): |
||||
"""Tests for the SMTPServer model""" |
||||
|
||||
def setUp(self): |
||||
self.server = SMTPServer.objects.create(name='Test SMTP', |
||||
host='smtp.domain.com') |
||||
self.server_2 = SMTPServer.objects.create(name='Test SMTP 2', |
||||
host='smtp.domain2.com') |
||||
self.contact = Contact.objects.create(email='test@domain.com') |
||||
self.mailinglist = MailingList.objects.create(name='Test MailingList') |
||||
self.mailinglist.subscribers.add(self.contact) |
||||
self.newsletter = Newsletter.objects.create(title='Test Newsletter', |
||||
content='Test Newsletter Content', |
||||
mailing_list=self.mailinglist, |
||||
server=self.server, slug='test-nl') |
||||
|
||||
self.newsletter_2 = Newsletter.objects.create(title='Test Newsletter 2', |
||||
content='Test Newsletter 2 Content', |
||||
mailing_list=self.mailinglist, |
||||
server=self.server, slug='test-nl-2') |
||||
self.newsletter_3 = Newsletter.objects.create(title='Test Newsletter 2', |
||||
content='Test Newsletter 2 Content', |
||||
mailing_list=self.mailinglist, |
||||
server=self.server_2, slug='test-nl-3') |
||||
|
||||
def test_credits(self): |
||||
# Testing unlimited account |
||||
self.assertEquals(self.server.credits(), 10000) |
||||
# Testing default limit |
||||
self.server.mails_hour = 42 |
||||
self.assertEquals(self.server.credits(), 42) |
||||
|
||||
# Testing credits status, with multiple server case |
||||
ContactMailingStatus.objects.create(newsletter=self.newsletter, |
||||
contact=self.contact, |
||||
status=ContactMailingStatus.SENT) |
||||
self.assertEquals(self.server.credits(), 41) |
||||
ContactMailingStatus.objects.create(newsletter=self.newsletter, |
||||
contact=self.contact, |
||||
status=ContactMailingStatus.SENT_TEST) |
||||
self.assertEquals(self.server.credits(), 40) |
||||
# Testing with a fake status |
||||
ContactMailingStatus.objects.create(newsletter=self.newsletter, |
||||
contact=self.contact, |
||||
status=ContactMailingStatus.ERROR) |
||||
self.assertEquals(self.server.credits(), 40) |
||||
# Testing with a second newsletter sharing the server |
||||
ContactMailingStatus.objects.create(newsletter=self.newsletter_2, |
||||
contact=self.contact, |
||||
status=ContactMailingStatus.SENT) |
||||
self.assertEquals(self.server.credits(), 39) |
||||
# Testing with a third newsletter with another server |
||||
ContactMailingStatus.objects.create(newsletter=self.newsletter_3, |
||||
contact=self.contact, |
||||
status=ContactMailingStatus.SENT) |
||||
self.assertEquals(self.server.credits(), 39) |
||||
|
||||
def test_custom_headers(self): |
||||
self.assertEquals(self.server.custom_headers, {}) |
||||
self.server.headers = 'key_1: val_1\r\nkey_2 : val_2' |
||||
self.assertEquals(len(self.server.custom_headers), 2) |
||||
|
||||
|
||||
class ContactTestCase(TestCase): |
||||
"""Tests for the Contact model""" |
||||
|
||||
def setUp(self): |
||||
self.mailinglist_1 = MailingList.objects.create(name='Test MailingList') |
||||
self.mailinglist_2 = MailingList.objects.create(name='Test MailingList 2') |
||||
|
||||
def test_unique(self): |
||||
Contact(email='test@domain.com').save() |
||||
self.assertRaises(IntegrityError, Contact(email='test@domain.com').save) |
||||
|
||||
def test_mail_format(self): |
||||
contact = Contact(email='test@domain.com') |
||||
self.assertEquals(contact.mail_format(), 'test@domain.com') |
||||
contact = Contact(email='test@domain.com', first_name='Toto') |
||||
self.assertEquals(contact.mail_format(), 'test@domain.com') |
||||
contact = Contact(email='test@domain.com', first_name='Toto', last_name='Titi') |
||||
self.assertEquals(contact.mail_format(), 'Titi Toto <test@domain.com>') |
||||
|
||||
def test_vcard_format(self): |
||||
contact = Contact(email='test@domain.com', first_name='Toto', last_name='Titi') |
||||
self.assertEquals(contact.vcard_format(), 'BEGIN:VCARD\r\nVERSION:3.0\r\n'\ |
||||
'EMAIL;TYPE=INTERNET:test@domain.com\r\nFN:Toto Titi\r\n'\ |
||||
'N:Titi;Toto;;;\r\nEND:VCARD\r\n') |
||||
|
||||
def test_subscriptions(self): |
||||
contact = Contact.objects.create(email='test@domain.com') |
||||
self.assertEquals(len(contact.subscriptions()), 0) |
||||
|
||||
self.mailinglist_1.subscribers.add(contact) |
||||
self.assertEquals(len(contact.subscriptions()), 1) |
||||
self.mailinglist_2.subscribers.add(contact) |
||||
self.assertEquals(len(contact.subscriptions()), 2) |
||||
|
||||
def test_unsubscriptions(self): |
||||
contact = Contact.objects.create(email='test@domain.com') |
||||
self.assertEquals(len(contact.unsubscriptions()), 0) |
||||
|
||||
self.mailinglist_1.unsubscribers.add(contact) |
||||
self.assertEquals(len(contact.unsubscriptions()), 1) |
||||
self.mailinglist_2.unsubscribers.add(contact) |
||||
self.assertEquals(len(contact.unsubscriptions()), 2) |
||||
|
||||
|
||||
class MailingListTestCase(TestCase): |
||||
"""Tests for the MailingList model""" |
||||
|
||||
def setUp(self): |
||||
self.contact_1 = Contact.objects.create(email='test1@domain.com') |
||||
self.contact_2 = Contact.objects.create(email='test2@domain.com', valid=False) |
||||
self.contact_3 = Contact.objects.create(email='test3@domain.com', subscriber=False) |
||||
self.contact_4 = Contact.objects.create(email='test4@domain.com') |
||||
|
||||
def test_subscribers_count(self): |
||||
mailinglist = MailingList(name='Test MailingList') |
||||
mailinglist.save() |
||||
self.assertEquals(mailinglist.subscribers_count(), 0) |
||||
mailinglist.subscribers.add(self.contact_1, self.contact_2, self.contact_3) |
||||
self.assertEquals(mailinglist.subscribers_count(), 3) |
||||
|
||||
def test_unsubscribers_count(self): |
||||
mailinglist = MailingList.objects.create(name='Test MailingList') |
||||
self.assertEquals(mailinglist.unsubscribers_count(), 0) |
||||
mailinglist.unsubscribers.add(self.contact_1, self.contact_2, self.contact_3) |
||||
self.assertEquals(mailinglist.unsubscribers_count(), 3) |
||||
|
||||
def test_expedition_set(self): |
||||
mailinglist = MailingList.objects.create(name='Test MailingList') |
||||
self.assertEquals(len(mailinglist.expedition_set()), 0) |
||||
mailinglist.subscribers.add(self.contact_1, self.contact_2, self.contact_3) |
||||
self.assertEquals(len(mailinglist.expedition_set()), 1) |
||||
mailinglist.subscribers.add(self.contact_4) |
||||
self.assertEquals(len(mailinglist.expedition_set()), 2) |
||||
mailinglist.unsubscribers.add(self.contact_4) |
||||
self.assertEquals(len(mailinglist.expedition_set()), 1) |
||||
|
||||
|
||||
class NewsletterTestCase(TestCase): |
||||
"""Tests for the Newsletter model""" |
||||
|
||||
def setUp(self): |
||||
self.server = SMTPServer.objects.create(name='Test SMTP', |
||||
host='smtp.domain.com') |
||||
self.contact = Contact.objects.create(email='test@domain.com') |
||||
self.mailinglist = MailingList.objects.create(name='Test MailingList') |
||||
self.newsletter = Newsletter.objects.create(title='Test Newsletter', |
||||
content='Test Newsletter Content', |
||||
mailing_list=self.mailinglist, |
||||
server=self.server) |
||||
|
||||
def test_mails_sent(self): |
||||
self.assertEquals(self.newsletter.mails_sent(), 0) |
||||
ContactMailingStatus.objects.create(newsletter=self.newsletter, |
||||
contact=self.contact, |
||||
status=ContactMailingStatus.SENT) |
||||
ContactMailingStatus.objects.create(newsletter=self.newsletter, |
||||
contact=self.contact, |
||||
status=ContactMailingStatus.SENT_TEST) |
||||
self.assertEquals(self.newsletter.mails_sent(), 1) |
||||
|
||||
|
||||
class TokenizationTestCase(TestCase): |
||||
"""Tests for the tokenization process""" |
||||
|
||||
def setUp(self): |
||||
self.contact = Contact.objects.create(email='test@domain.com') |
||||
|
||||
def test_tokenize_untokenize(self): |
||||
uidb36, token = tokenize(self.contact) |
||||
self.assertEquals(untokenize(uidb36, token), self.contact) |
||||
self.assertRaises(Http404, untokenize, 'toto', token) |
||||
self.assertRaises(Http404, untokenize, uidb36, 'toto') |
||||
|
||||
|
||||
class MailerTestCase(TestCase): |
||||
"""Tests for the Mailer object""" |
||||
|
||||
def setUp(self): |
||||
self.server = SMTPServer.objects.create(name='Test SMTP', |
||||
host='smtp.domain.com', |
||||
mails_hour=100) |
||||
self.contacts = [Contact.objects.create(email='test1@domain.com'), |
||||
Contact.objects.create(email='test2@domain.com'), |
||||
Contact.objects.create(email='test3@domain.com'), |
||||
Contact.objects.create(email='test4@domain.com')] |
||||
self.mailinglist = MailingList.objects.create(name='Test MailingList') |
||||
self.mailinglist.subscribers.add(*self.contacts) |
||||
self.newsletter = Newsletter.objects.create(title='Test Newsletter', |
||||
content='Test Newsletter Content', |
||||
slug='test-newsletter', |
||||
mailing_list=self.mailinglist, |
||||
server=self.server, |
||||
status=Newsletter.WAITING) |
||||
self.newsletter.test_contacts.add(*self.contacts[:2]) |
||||
self.attachment = Attachment.objects.create(newsletter=self.newsletter, |
||||
title='Test attachment', |
||||
file_attachment=File(NamedTemporaryFile())) |
||||
|
||||
def test_expedition_list(self): |
||||
mailer = Mailer(self.newsletter, test=True) |
||||
self.assertEquals(len(mailer.expedition_list), 2) |
||||
self.server.mails_hour = 1 |
||||
self.assertEquals(len(mailer.expedition_list), 1) |
||||
|
||||
self.server.mails_hour = 100 |
||||
mailer = Mailer(self.newsletter) |
||||
self.assertEquals(len(mailer.expedition_list), 4) |
||||
self.server.mails_hour = 3 |
||||
self.assertEquals(len(mailer.expedition_list), 3) |
||||
|
||||
self.server.mails_hour = 100 |
||||
ContactMailingStatus.objects.create(newsletter=self.newsletter, |
||||
contact=self.contacts[0], |
||||
status=ContactMailingStatus.SENT) |
||||
ContactMailingStatus.objects.create(newsletter=self.newsletter, |
||||
contact=self.contacts[1], |
||||
status=ContactMailingStatus.SENT) |
||||
ContactMailingStatus.objects.create(newsletter=self.newsletter, |
||||
contact=self.contacts[1], |
||||
status=ContactMailingStatus.SENT) |
||||
self.assertEquals(len(mailer.expedition_list), 2) |
||||
self.assertFalse(self.contacts[0] in mailer.expedition_list) |
||||
|
||||
def test_can_send(self): |
||||
mailer = Mailer(self.newsletter) |
||||
self.assertTrue(mailer.can_send) |
||||
|
||||
# Checks credits |
||||
self.server.mails_hour = 1 |
||||
ContactMailingStatus.objects.create(newsletter=self.newsletter, |
||||
contact=self.contacts[0], |
||||
status=ContactMailingStatus.SENT) |
||||
mailer = Mailer(self.newsletter) |
||||
self.assertFalse(mailer.can_send) |
||||
self.server.mails_hour = 10 |
||||
mailer = Mailer(self.newsletter) |
||||
self.assertTrue(mailer.can_send) |
||||
|
||||
# Checks statut |
||||
self.newsletter.status = Newsletter.DRAFT |
||||
mailer = Mailer(self.newsletter) |
||||
self.assertFalse(mailer.can_send) |
||||
mailer = Mailer(self.newsletter, test=True) |
||||
self.assertTrue(mailer.can_send) |
||||
|
||||
# Checks expedition time |
||||
self.newsletter.status = Newsletter.WAITING |
||||
self.newsletter.sending_date = datetime.now() + timedelta(hours=1) |
||||
mailer = Mailer(self.newsletter) |
||||
self.assertFalse(mailer.can_send) |
||||
self.newsletter.sending_date = datetime.now() |
||||
mailer = Mailer(self.newsletter) |
||||
self.assertTrue(mailer.can_send) |
||||
|
||||
def test_run(self): |
||||
mailer = Mailer(self.newsletter) |
||||
mailer.smtp = FakeSMTP() |
||||
mailer.run() |
||||
self.assertEquals(mailer.smtp.mails_sent, 4) |
||||
self.assertEquals(ContactMailingStatus.objects.filter( |
||||
status=ContactMailingStatus.SENT, newsletter=self.newsletter).count(), 4) |
||||
|
||||
mailer = Mailer(self.newsletter, test=True) |
||||
mailer.smtp = FakeSMTP() |
||||
|
||||
mailer.run() |
||||
self.assertEquals(mailer.smtp.mails_sent, 2) |
||||
self.assertEquals(ContactMailingStatus.objects.filter( |
||||
status=ContactMailingStatus.SENT_TEST, newsletter=self.newsletter).count(), 2) |
||||
|
||||
mailer.smtp = None |
||||
|
||||
def test_update_newsletter_status(self): |
||||
mailer = Mailer(self.newsletter, test=True) |
||||
self.assertEquals(self.newsletter.status, Newsletter.WAITING) |
||||
mailer.update_newsletter_status() |
||||
self.assertEquals(self.newsletter.status, Newsletter.WAITING) |
||||
|
||||
mailer = Mailer(self.newsletter) |
||||
self.assertEquals(self.newsletter.status, Newsletter.WAITING) |
||||
mailer.update_newsletter_status() |
||||
self.assertEquals(self.newsletter.status, Newsletter.SENDING) |
||||
|
||||
for contact in self.contacts: |
||||
ContactMailingStatus.objects.create(newsletter=self.newsletter, |
||||
contact=contact, |
||||
status=ContactMailingStatus.SENT) |
||||
mailer.update_newsletter_status() |
||||
self.assertEquals(self.newsletter.status, Newsletter.SENT) |
||||
|
||||
def test_update_newsletter_status_advanced(self): |
||||
self.server.mails_hour = 2 |
||||
self.server.save() |
||||
|
||||
mailer = Mailer(self.newsletter) |
||||
mailer.smtp = FakeSMTP() |
||||
mailer.run() |
||||
|
||||
self.assertEquals(mailer.smtp.mails_sent, 2) |
||||
self.assertEquals(ContactMailingStatus.objects.filter( |
||||
status=ContactMailingStatus.SENT, newsletter=self.newsletter).count(), 2) |
||||
self.assertEquals(self.newsletter.status, Newsletter.SENDING) |
||||
|
||||
self.server.mails_hour = 0 |
||||
self.server.save() |
||||
|
||||
mailer = Mailer(self.newsletter) |
||||
mailer.smtp = FakeSMTP() |
||||
mailer.run() |
||||
|
||||
self.assertEquals(mailer.smtp.mails_sent, 2) |
||||
self.assertEquals(ContactMailingStatus.objects.filter( |
||||
status=ContactMailingStatus.SENT, newsletter=self.newsletter).count(), 4) |
||||
self.assertEquals(self.newsletter.status, Newsletter.SENT) |
||||
|
||||
def test_recipients_refused(self): |
||||
server = SMTPServer.objects.create(name='Local SMTP', |
||||
host='localhost') |
||||
contact = Contact.objects.create(email='thisisaninvalidemail') |
||||
self.newsletter.test_contacts.clear() |
||||
self.newsletter.test_contacts.add(contact) |
||||
self.newsletter.server = server |
||||
self.newsletter.save() |
||||
|
||||
self.assertEquals(contact.valid, True) |
||||
self.assertEquals(ContactMailingStatus.objects.filter( |
||||
status=ContactMailingStatus.INVALID, newsletter=self.newsletter).count(), 0) |
||||
|
||||
mailer = Mailer(self.newsletter, test=True) |
||||
mailer.run() |
||||
|
||||
self.assertEquals(Contact.objects.get(email='thisisaninvalidemail').valid, False) |
||||
self.assertEquals(ContactMailingStatus.objects.filter( |
||||
status=ContactMailingStatus.INVALID, newsletter=self.newsletter).count(), 1) |
||||
|
||||
|
||||
class StatisticsTestCase(TestCase): |
||||
"""Tests for the statistics functions""" |
||||
|
||||
def setUp(self): |
||||
self.server = SMTPServer.objects.create(name='Test SMTP', |
||||
host='smtp.domain.com') |
||||
self.contacts = [Contact.objects.create(email='test1@domain.com'), |
||||
Contact.objects.create(email='test2@domain.com'), |
||||
Contact.objects.create(email='test3@domain.com'), |
||||
Contact.objects.create(email='test4@domain.com')] |
||||
self.mailinglist = MailingList.objects.create(name='Test MailingList') |
||||
self.mailinglist.subscribers.add(*self.contacts) |
||||
self.newsletter = Newsletter.objects.create(title='Test Newsletter', |
||||
content='Test Newsletter Content', |
||||
mailing_list=self.mailinglist, |
||||
server=self.server, |
||||
status=Newsletter.SENT) |
||||
self.links = [Link.objects.create(title='link 1', url='htt://link.1'), |
||||
Link.objects.create(title='link 2', url='htt://link.2')] |
||||
|
||||
for contact in self.contacts: |
||||
ContactMailingStatus.objects.create(newsletter=self.newsletter, |
||||
contact=contact, |
||||
status=ContactMailingStatus.SENT) |
||||
ContactMailingStatus.objects.create(newsletter=self.newsletter, |
||||
contact=self.contacts[0], |
||||
status=ContactMailingStatus.SENT_TEST) |
||||
|
||||
self.recipients = len(self.contacts) |
||||
self.status = ContactMailingStatus.objects.filter(newsletter=self.newsletter) |
||||
|
||||
def test_get_newsletter_opening_statistics(self): |
||||
stats = get_newsletter_opening_statistics(self.status, self.recipients) |
||||
self.assertEquals(stats['total_openings'], 0) |
||||
self.assertEquals(stats['unique_openings'], 0) |
||||
self.assertEquals(stats['double_openings'], 0) |
||||
self.assertEquals(stats['unique_openings_percent'], 0) |
||||
self.assertEquals(stats['unknow_openings'], 0) |
||||
self.assertEquals(stats['unknow_openings_percent'], 0) |
||||
self.assertEquals(stats['opening_average'], 0) |
||||
|
||||
ContactMailingStatus.objects.create(newsletter=self.newsletter, |
||||
contact=self.contacts[0], |
||||
status=ContactMailingStatus.OPENED) |
||||
ContactMailingStatus.objects.create(newsletter=self.newsletter, |
||||
contact=self.contacts[0], |
||||
status=ContactMailingStatus.OPENED) |
||||
ContactMailingStatus.objects.create(newsletter=self.newsletter, |
||||
contact=self.contacts[1], |
||||
status=ContactMailingStatus.OPENED) |
||||
ContactMailingStatus.objects.create(newsletter=self.newsletter, |
||||
contact=self.contacts[2], |
||||
status=ContactMailingStatus.OPENED_ON_SITE) |
||||
ContactMailingStatus.objects.create(newsletter=self.newsletter, |
||||
contact=self.contacts[2], |
||||
status=ContactMailingStatus.LINK_OPENED) |
||||
status = ContactMailingStatus.objects.filter(newsletter=self.newsletter) |
||||
|
||||
stats = get_newsletter_opening_statistics(status, self.recipients) |
||||
self.assertEquals(stats['total_openings'], 4) |
||||
self.assertEquals(stats['unique_openings'], 3) |
||||
self.assertEquals(stats['double_openings'], 1) |
||||
self.assertEquals(stats['unique_openings_percent'], 75.0) |
||||
self.assertEquals(stats['unknow_openings'], 1) |
||||
self.assertEquals(stats['unknow_openings_percent'], 25.0) |
||||
self.assertEquals(stats['opening_average'], 1.3333333333333333) |
||||
self.assertEquals(stats['opening_deducted'], 0) |
||||
|
||||
ContactMailingStatus.objects.create(newsletter=self.newsletter, |
||||
contact=self.contacts[3], |
||||
status=ContactMailingStatus.LINK_OPENED) |
||||
ContactMailingStatus.objects.create(newsletter=self.newsletter, |
||||
contact=self.contacts[3], |
||||
status=ContactMailingStatus.LINK_OPENED) |
||||
status = ContactMailingStatus.objects.filter(newsletter=self.newsletter) |
||||
|
||||
stats = get_newsletter_opening_statistics(status, self.recipients) |
||||
self.assertEquals(stats['total_openings'], 5) |
||||
self.assertEquals(stats['unique_openings'], 4) |
||||
self.assertEquals(stats['double_openings'], 1) |
||||
self.assertEquals(stats['unique_openings_percent'], 100.0) |
||||
self.assertEquals(stats['unknow_openings'], 0) |
||||
self.assertEquals(stats['unknow_openings_percent'], 0.0) |
||||
self.assertEquals(stats['opening_average'], 1.25) |
||||
self.assertEquals(stats['opening_deducted'], 1) |
||||
|
||||
def test_get_newsletter_on_site_opening_statistics(self): |
||||
stats = get_newsletter_on_site_opening_statistics(self.status) |
||||
self.assertEquals(stats['total_on_site_openings'], 0) |
||||
self.assertEquals(stats['unique_on_site_openings'], 0) |
||||
|
||||
ContactMailingStatus.objects.create(newsletter=self.newsletter, |
||||
contact=self.contacts[0], |
||||
status=ContactMailingStatus.OPENED_ON_SITE) |
||||
ContactMailingStatus.objects.create(newsletter=self.newsletter, |
||||
contact=self.contacts[0], |
||||
status=ContactMailingStatus.OPENED_ON_SITE) |
||||
ContactMailingStatus.objects.create(newsletter=self.newsletter, |
||||
contact=self.contacts[1], |
||||
status=ContactMailingStatus.OPENED_ON_SITE) |
||||
ContactMailingStatus.objects.create(newsletter=self.newsletter, |
||||
contact=self.contacts[2], |
||||
status=ContactMailingStatus.OPENED_ON_SITE) |
||||
status = ContactMailingStatus.objects.filter(newsletter=self.newsletter) |
||||
|
||||
stats = get_newsletter_on_site_opening_statistics(status) |
||||
self.assertEquals(stats['total_on_site_openings'], 4) |
||||
self.assertEquals(stats['unique_on_site_openings'], 3) |
||||
|
||||
def test_get_newsletter_clicked_link_statistics(self): |
||||
stats = get_newsletter_clicked_link_statistics(self.status, self.recipients, 0) |
||||
self.assertEquals(stats['total_clicked_links'], 0) |
||||
self.assertEquals(stats['total_clicked_links_percent'], 0) |
||||
self.assertEquals(stats['double_clicked_links'], 0) |
||||
self.assertEquals(stats['double_clicked_links_percent'], 0.0) |
||||
self.assertEquals(stats['unique_clicked_links'], 0) |
||||
self.assertEquals(stats['unique_clicked_links_percent'], 0) |
||||
self.assertEquals(stats['clicked_links_by_openings'], 0.0) |
||||
self.assertEquals(stats['clicked_links_average'], 0.0) |
||||
|
||||
ContactMailingStatus.objects.create(newsletter=self.newsletter, |
||||
contact=self.contacts[0], |
||||
link=self.links[0], |
||||
status=ContactMailingStatus.LINK_OPENED) |
||||
ContactMailingStatus.objects.create(newsletter=self.newsletter, |
||||
contact=self.contacts[0], |
||||
link=self.links[1], |
||||
status=ContactMailingStatus.LINK_OPENED) |
||||
ContactMailingStatus.objects.create(newsletter=self.newsletter, |
||||
contact=self.contacts[0], |
||||
link=self.links[1], |
||||
status=ContactMailingStatus.LINK_OPENED) |
||||
ContactMailingStatus.objects.create(newsletter=self.newsletter, |
||||
contact=self.contacts[1], |
||||
link=self.links[0], |
||||
status=ContactMailingStatus.LINK_OPENED) |
||||
ContactMailingStatus.objects.create(newsletter=self.newsletter, |
||||
contact=self.contacts[2], |
||||
link=self.links[0], |
||||
status=ContactMailingStatus.LINK_OPENED) |
||||
status = ContactMailingStatus.objects.filter(newsletter=self.newsletter) |
||||
|
||||
stats = get_newsletter_clicked_link_statistics(status, self.recipients, 3) |
||||
self.assertEquals(stats['total_clicked_links'], 5) |
||||
self.assertEquals(stats['total_clicked_links_percent'], 125.0) |
||||
self.assertEquals(stats['double_clicked_links'], 2) |
||||
self.assertEquals(stats['double_clicked_links_percent'], 50.0) |
||||
self.assertEquals(stats['unique_clicked_links'], 3) |
||||
self.assertEquals(stats['unique_clicked_links_percent'], 75.0) |
||||
self.assertEquals(stats['clicked_links_by_openings'], 166.66666666666669) |
||||
self.assertEquals(stats['clicked_links_average'], 1.6666666666666667) |
||||
|
||||
def test_get_newsletter_unsubscription_statistics(self): |
||||
stats = get_newsletter_unsubscription_statistics(self.status, self.recipients) |
||||
self.assertEquals(stats['total_unsubscriptions'], 0) |
||||
self.assertEquals(stats['total_unsubscriptions_percent'], 0.0) |
||||
|
||||
ContactMailingStatus.objects.create(newsletter=self.newsletter, |
||||
contact=self.contacts[0], |
||||
status=ContactMailingStatus.UNSUBSCRIPTION) |
||||
ContactMailingStatus.objects.create(newsletter=self.newsletter, |
||||
contact=self.contacts[1], |
||||
status=ContactMailingStatus.UNSUBSCRIPTION) |
||||
|
||||
status = ContactMailingStatus.objects.filter(newsletter=self.newsletter) |
||||
|
||||
stats = get_newsletter_unsubscription_statistics(status, self.recipients) |
||||
self.assertEquals(stats['total_unsubscriptions'], 2) |
||||
self.assertEquals(stats['total_unsubscriptions_percent'], 50.0) |
||||
|
||||
def test_get_newsletter_unsubscription_statistics_fix_doublon(self): |
||||
stats = get_newsletter_unsubscription_statistics(self.status, self.recipients) |
||||
self.assertEquals(stats['total_unsubscriptions'], 0) |
||||
self.assertEquals(stats['total_unsubscriptions_percent'], 0.0) |
||||
|
||||
ContactMailingStatus.objects.create(newsletter=self.newsletter, |
||||
contact=self.contacts[0], |
||||
status=ContactMailingStatus.UNSUBSCRIPTION) |
||||
ContactMailingStatus.objects.create(newsletter=self.newsletter, |
||||
contact=self.contacts[1], |
||||
status=ContactMailingStatus.UNSUBSCRIPTION) |
||||
ContactMailingStatus.objects.create(newsletter=self.newsletter, |
||||
contact=self.contacts[1], |
||||
status=ContactMailingStatus.UNSUBSCRIPTION) |
||||
|
||||
status = ContactMailingStatus.objects.filter(newsletter=self.newsletter) |
||||
|
||||
stats = get_newsletter_unsubscription_statistics(status, self.recipients) |
||||
self.assertEquals(stats['total_unsubscriptions'], 2) |
||||
self.assertEquals(stats['total_unsubscriptions_percent'], 50.0) |
||||
|
||||
def test_get_newsletter_top_links(self): |
||||
stats = get_newsletter_top_links(self.status) |
||||
self.assertEquals(stats['top_links'], []) |
||||
|
||||
ContactMailingStatus.objects.create(newsletter=self.newsletter, |
||||
contact=self.contacts[0], |
||||
link=self.links[0], |
||||
status=ContactMailingStatus.LINK_OPENED) |
||||
ContactMailingStatus.objects.create(newsletter=self.newsletter, |
||||
contact=self.contacts[0], |
||||
link=self.links[0], |
||||
status=ContactMailingStatus.LINK_OPENED) |
||||
ContactMailingStatus.objects.create(newsletter=self.newsletter, |
||||
contact=self.contacts[0], |
||||
link=self.links[1], |
||||
status=ContactMailingStatus.LINK_OPENED) |
||||
ContactMailingStatus.objects.create(newsletter=self.newsletter, |
||||
contact=self.contacts[1], |
||||
link=self.links[0], |
||||
status=ContactMailingStatus.LINK_OPENED) |
||||
ContactMailingStatus.objects.create(newsletter=self.newsletter, |
||||
contact=self.contacts[2], |
||||
link=self.links[0], |
||||
status=ContactMailingStatus.LINK_OPENED) |
||||
status = ContactMailingStatus.objects.filter(newsletter=self.newsletter) |
||||
|
||||
stats = get_newsletter_top_links(status) |
||||
self.assertEquals(len(stats['top_links']), 2) |
||||
self.assertEquals(stats['top_links'][0]['link'], self.links[0]) |
||||
self.assertEquals(stats['top_links'][0]['total_clicks'], 4) |
||||
self.assertEquals(stats['top_links'][0]['unique_clicks'], 3) |
||||
self.assertEquals(stats['top_links'][1]['link'], self.links[1]) |
||||
self.assertEquals(stats['top_links'][1]['total_clicks'], 1) |
||||
self.assertEquals(stats['top_links'][1]['unique_clicks'], 1) |
||||
|
||||
def test_get_newsletter_statistics(self): |
||||
stats = get_newsletter_statistics(self.newsletter) |
||||
|
||||
self.assertEquals(stats['clicked_links_average'], 0.0) |
||||
self.assertEquals(stats['clicked_links_by_openings'], 0.0) |
||||
self.assertEquals(stats['double_clicked_links'], 0) |
||||
self.assertEquals(stats['double_clicked_links_percent'], 00.0) |
||||
self.assertEquals(stats['double_openings'], 0) |
||||
self.assertEquals(stats['mails_sent'], 4) |
||||
self.assertEquals(stats['mails_to_send'], 4) |
||||
self.assertEquals(stats['opening_average'], 0) |
||||
self.assertEquals(stats['remaining_mails'], 0) |
||||
self.assertEquals(stats['tests_sent'], 1) |
||||
self.assertEquals(stats['top_links'], []) |
||||
self.assertEquals(stats['total_clicked_links'], 0) |
||||
self.assertEquals(stats['total_clicked_links_percent'], 0.0) |
||||
self.assertEquals(stats['total_on_site_openings'], 0) |
||||
self.assertEquals(stats['total_openings'], 0) |
||||
self.assertEquals(stats['total_unsubscriptions'], 0) |
||||
self.assertEquals(stats['total_unsubscriptions_percent'], 0.0) |
||||
self.assertEquals(stats['unique_clicked_links'], 0) |
||||
self.assertEquals(stats['unique_clicked_links_percent'], 0.0) |
||||
self.assertEquals(stats['unique_on_site_openings'], 0) |
||||
self.assertEquals(stats['unique_openings'], 0) |
||||
self.assertEquals(stats['unique_openings_percent'], 0) |
||||
self.assertEquals(stats['unknow_openings'], 0) |
||||
self.assertEquals(stats['unknow_openings_percent'], 0.0) |
||||
|
||||
ContactMailingStatus.objects.create(newsletter=self.newsletter, |
||||
contact=self.contacts[0], |
||||
status=ContactMailingStatus.OPENED) |
||||
ContactMailingStatus.objects.create(newsletter=self.newsletter, |
||||
contact=self.contacts[0], |
||||
status=ContactMailingStatus.OPENED) |
||||
ContactMailingStatus.objects.create(newsletter=self.newsletter, |
||||
contact=self.contacts[1], |
||||
status=ContactMailingStatus.OPENED) |
||||
ContactMailingStatus.objects.create(newsletter=self.newsletter, |
||||
contact=self.contacts[0], |
||||
status=ContactMailingStatus.OPENED_ON_SITE) |
||||
ContactMailingStatus.objects.create(newsletter=self.newsletter, |
||||
contact=self.contacts[2], |
||||
status=ContactMailingStatus.OPENED_ON_SITE) |
||||
ContactMailingStatus.objects.create(newsletter=self.newsletter, |
||||
contact=self.contacts[0], |
||||
link=self.links[0], |
||||
status=ContactMailingStatus.LINK_OPENED) |
||||
ContactMailingStatus.objects.create(newsletter=self.newsletter, |
||||
contact=self.contacts[0], |
||||
link=self.links[1], |
||||
status=ContactMailingStatus.LINK_OPENED) |
||||
ContactMailingStatus.objects.create(newsletter=self.newsletter, |
||||
contact=self.contacts[0], |
||||
link=self.links[1], |
||||
status=ContactMailingStatus.LINK_OPENED) |
||||
ContactMailingStatus.objects.create(newsletter=self.newsletter, |
||||
contact=self.contacts[1], |
||||
link=self.links[0], |
||||
status=ContactMailingStatus.LINK_OPENED) |
||||
ContactMailingStatus.objects.create(newsletter=self.newsletter, |
||||
contact=self.contacts[2], |
||||
link=self.links[0], |
||||
status=ContactMailingStatus.LINK_OPENED) |
||||
ContactMailingStatus.objects.create(newsletter=self.newsletter, |
||||
contact=self.contacts[0], |
||||
status=ContactMailingStatus.UNSUBSCRIPTION) |
||||
|
||||
stats = get_newsletter_statistics(self.newsletter) |
||||
|
||||
self.assertEquals(stats['clicked_links_average'], 1.6666666666666667) |
||||
self.assertEquals(stats['clicked_links_by_openings'], 100.0) |
||||
self.assertEquals(stats['double_clicked_links'], 2) |
||||
self.assertEquals(stats['double_clicked_links_percent'], 50.0) |
||||
self.assertEquals(stats['double_openings'], 2) |
||||
self.assertEquals(stats['mails_sent'], 4) |
||||
self.assertEquals(stats['mails_to_send'], 4) |
||||
self.assertEquals(stats['opening_average'], 1.6666666666666667) |
||||
self.assertEquals(stats['remaining_mails'], 0) |
||||
self.assertEquals(stats['tests_sent'], 1) |
||||
self.assertEquals(stats['total_clicked_links'], 5) |
||||
self.assertEquals(stats['total_clicked_links_percent'], 125.0) |
||||
self.assertEquals(stats['total_on_site_openings'], 2) |
||||
self.assertEquals(stats['total_openings'], 5) |
||||
self.assertEquals(stats['total_unsubscriptions'], 1) |
||||
self.assertEquals(stats['total_unsubscriptions_percent'], 25.0) |
||||
self.assertEquals(stats['unique_clicked_links'], 3) |
||||
self.assertEquals(stats['unique_clicked_links_percent'], 75.0) |
||||
self.assertEquals(stats['unique_on_site_openings'], 2) |
||||
self.assertEquals(stats['unique_openings'], 3) |
||||
self.assertEquals(stats['unique_openings_percent'], 75) |
||||
self.assertEquals(stats['unknow_openings'], 1) |
||||
self.assertEquals(stats['unknow_openings_percent'], 25.0) |
||||
|
||||
def test_get_newsletter_statistics_division_by_zero(self): |
||||
"""Try to have a ZeroDivisionError by unsubscribing all contacts, |
||||
and creating a ContactMailingStatus for more code coverage. |
||||
Bug : http://github.com/Fantomas42/emencia-django-newsletter/issues#issue/9""" |
||||
get_newsletter_statistics(self.newsletter) |
||||
|
||||
self.mailinglist.unsubscribers.add(*self.contacts) |
||||
ContactMailingStatus.objects.create(newsletter=self.newsletter, |
||||
contact=self.contacts[0], |
||||
status=ContactMailingStatus.OPENED) |
||||
get_newsletter_statistics(self.newsletter) |
||||
@ -0,0 +1,16 @@ |
||||
"""Settings for testing emencia.django.newsletter""" |
||||
|
||||
SITE_ID = 1 |
||||
|
||||
USE_I18N = False |
||||
|
||||
ROOT_URLCONF = 'emencia.django.newsletter.urls' |
||||
|
||||
DATABASES = {'default': {'NAME': 'newsletter_tests.db', |
||||
'ENGINE': 'django.db.backends.sqlite3'}} |
||||
|
||||
INSTALLED_APPS = ['django.contrib.contenttypes', |
||||
'django.contrib.sites', |
||||
'django.contrib.auth', |
||||
'tagging', |
||||
'emencia.django.newsletter'] |
||||
@ -0,0 +1,11 @@ |
||||
"""Default urls for the emencia.django.newsletter""" |
||||
from django.conf.urls.defaults import url |
||||
from django.conf.urls.defaults import include |
||||
from django.conf.urls.defaults import patterns |
||||
|
||||
urlpatterns = patterns('', |
||||
url(r'^mailing/', include('emencia.django.newsletter.urls.mailing_list')), |
||||
url(r'^tracking/', include('emencia.django.newsletter.urls.tracking')), |
||||
url(r'^statistics/', include('emencia.django.newsletter.urls.statistics')), |
||||
url(r'^', include('emencia.django.newsletter.urls.newsletter')), |
||||
) |
||||
@ -0,0 +1,20 @@ |
||||
"""Urls for the emencia.django.newsletter Mailing List""" |
||||
from django.conf.urls.defaults import url |
||||
from django.conf.urls.defaults import patterns |
||||
|
||||
from emencia.django.newsletter.forms import MailingListSubscriptionForm |
||||
from emencia.django.newsletter.forms import AllMailingListSubscriptionForm |
||||
|
||||
urlpatterns = patterns('emencia.django.newsletter.views.mailing_list', |
||||
url(r'^unsubscribe/(?P<slug>[-\w]+)/(?P<uidb36>[0-9A-Za-z]+)-(?P<token>.+)/$', |
||||
'view_mailinglist_unsubscribe', |
||||
name='newsletter_mailinglist_unsubscribe'), |
||||
url(r'^subscribe/(?P<mailing_list_id>\d+)/', |
||||
'view_mailinglist_subscribe', |
||||
{'form_class': MailingListSubscriptionForm}, |
||||
name='newsletter_mailinglist_subscribe'), |
||||
url(r'^subscribe/', |
||||
'view_mailinglist_subscribe', |
||||
{'form_class': AllMailingListSubscriptionForm}, |
||||
name='newsletter_mailinglist_subscribe_all'), |
||||
) |
||||
@ -0,0 +1,12 @@ |
||||
"""Urls for the emencia.django.newsletter Newsletter""" |
||||
from django.conf.urls.defaults import url |
||||
from django.conf.urls.defaults import patterns |
||||
|
||||
urlpatterns = patterns('emencia.django.newsletter.views.newsletter', |
||||
url(r'^preview/(?P<slug>[-\w]+)/$', |
||||
'view_newsletter_preview', |
||||
name='newsletter_newsletter_preview'), |
||||
url(r'^(?P<slug>[-\w]+)/(?P<uidb36>[0-9A-Za-z]+)-(?P<token>.+)/$', |
||||
'view_newsletter_contact', |
||||
name='newsletter_newsletter_contact'), |
||||
) |
||||
@ -0,0 +1,18 @@ |
||||
"""Urls for the emencia.django.newsletter statistics""" |
||||
from django.conf.urls.defaults import url |
||||
from django.conf.urls.defaults import patterns |
||||
|
||||
urlpatterns = patterns('emencia.django.newsletter.views.statistics', |
||||
url(r'^(?P<slug>[-\w]+)/$', |
||||
'view_newsletter_statistics', |
||||
name='newsletter_newsletter_statistics'), |
||||
url(r'^report/(?P<slug>[-\w]+)/$', |
||||
'view_newsletter_report', |
||||
name='newsletter_newsletter_report'), |
||||
url(r'^charts/(?P<slug>[-\w]+)/$', |
||||
'view_newsletter_charts', |
||||
name='newsletter_newsletter_charts'), |
||||
url(r'^density/(?P<slug>[-\w]+)/$', |
||||
'view_newsletter_density', |
||||
name='newsletter_newsletter_density'), |
||||
) |
||||
@ -0,0 +1,15 @@ |
||||
"""Urls for the emencia.django.newsletter Tracking""" |
||||
from django.conf.urls.defaults import url |
||||
from django.conf.urls.defaults import patterns |
||||
|
||||
urlpatterns = patterns('emencia.django.newsletter.views.tracking', |
||||
url(r'^newsletter/(?P<slug>[-\w]+)/(?P<uidb36>[0-9A-Za-z]+)-(?P<token>.+)\.(?P<format>png|gif|jpg)$', |
||||
'view_newsletter_tracking', |
||||
name='newsletter_newsletter_tracking'), |
||||
url(r'^link/(?P<slug>[-\w]+)/(?P<uidb36>[0-9A-Za-z]+)-(?P<token>.+)/(?P<link_id>\d+)/$', |
||||
'view_newsletter_tracking_link', |
||||
name='newsletter_newsletter_tracking_link'), |
||||
url(r'^historic/(?P<slug>[-\w]+)/$', |
||||
'view_newsletter_historic', |
||||
name='newsletter_newsletter_historic'), |
||||
) |
||||
@ -1,4 +1,4 @@ |
||||
"""ExcelResponse for newsletter""" |
||||
"""ExcelResponse for emencia.django.newsletter""" |
||||
# Based on http://www.djangosnippets.org/snippets/1151/ |
||||
import datetime |
||||
|
||||
@ -1,4 +1,4 @@ |
||||
"""Premailer for newsletter |
||||
"""Premailer for emencia.django.newsletter |
||||
Used for converting a page with CSS inline and links corrected. |
||||
Based on http://www.peterbe.com/plog/premailer.py""" |
||||
import re |
||||
@ -1,7 +1,7 @@ |
||||
"""Statistics for newsletter""" |
||||
"""Statistics for emencia.django.newsletter""" |
||||
from django.db.models import Q |
||||
|
||||
from newsletter.models import ContactMailingStatus as Status |
||||
from emencia.django.newsletter.models import ContactMailingStatus as Status |
||||
|
||||
|
||||
def smart_division(a, b): |
||||
@ -1,9 +1,9 @@ |
||||
"""Tokens system for newsletter""" |
||||
"""Tokens system for emencia.django.newsletter""" |
||||
from django.conf import settings |
||||
from django.http import Http404 |
||||
from django.utils.http import int_to_base36, base36_to_int |
||||
|
||||
from newsletter.models import Contact |
||||
from emencia.django.newsletter.models import Contact |
||||
|
||||
|
||||
class ContactTokenGenerator(object): |
||||
@ -1,5 +1,5 @@ |
||||
"""Utils for workgroups""" |
||||
from newsletter.models import WorkGroup |
||||
from emencia.django.newsletter.models import WorkGroup |
||||
|
||||
|
||||
def request_workgroups(request): |
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue