diff --git a/MANIFEST.in b/MANIFEST.in index 383827a5..55bb6eaa 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,3 +1,4 @@ include README LICENSE recursive-include symposion/templates *.html *.txt recursive-include symposion/static * +recursive-include symposion/locale * diff --git a/README.rst b/README.rst index ebb3c6de..d2b22367 100644 --- a/README.rst +++ b/README.rst @@ -1,6 +1,9 @@ Symposion --------- +.. image:: http://slack.pinaxproject.com/badge.svg + :target: http://slack.pinaxproject.com/ + .. image:: https://img.shields.io/travis/pinax/symposion.svg :target: https://travis-ci.org/pinax/symposion @@ -17,12 +20,18 @@ Symposion :target: https://pypi.python.org/pypi/symposion/ +Pinax +------ -A conference management solution from Eldarion. +Pinax is an open-source platform built on the Django Web Framework. It is an ecosystem of reusable Django apps, themes, and starter project templates. +This collection can be found at http://pinaxproject.com. -Built with the generous support of the Python Software Foundation. -See http://eldarion.com/symposion/ for commercial support, customization and hosting +symposion +---------- + +symposion is a conference management solution from Eldarion. It was built with the generous support of the Python Software Foundation. See http://eldarion.com/symposion/ for commercial support, customization and hosting. + Quickstart ========== @@ -36,3 +45,23 @@ customize and manage your Symposion installation. We have built a [basic Django startproject template that includes Symposion][1]. [1]: https://github.com/pinax/pinax-project-symposion + + +Documentation +--------------- + +The Pinax documentation is available at http://pinaxproject.com/pinax/. + + +Code of Conduct +---------------- + +In order to foster a kind, inclusive, and harassment-free community, the Pinax Project has a code of conduct, which can be found here http://pinaxproject.com/pinax/code_of_conduct/. + + +Pinax Project Blog and Twitter +------------------------------- + +For updates and news regarding the Pinax Project, please follow us on Twitter at @pinaxproject and check out our blog http://blog.pinaxproject.com. + + diff --git a/symposion/conference/admin.py b/symposion/conference/admin.py index c8e57441..f7145303 100644 --- a/symposion/conference/admin.py +++ b/symposion/conference/admin.py @@ -3,7 +3,18 @@ from django.contrib import admin from symposion.conference.models import Conference, Section -admin.site.register(Conference, list_display=("title", "start_date", "end_date")) +class SectionInline(admin.TabularInline): + model = Section + prepopulated_fields = {"slug": ("name",)} + extra = 1 + + +class ConferenceAdmin(admin.ModelAdmin): + list_display = ("title", "start_date", "end_date") + inlines = [SectionInline, ] + + +admin.site.register(Conference, ConferenceAdmin) admin.site.register( Section, prepopulated_fields={"slug": ("name",)}, diff --git a/symposion/forms.py b/symposion/forms.py index 7799e06a..09fc5d4f 100644 --- a/symposion/forms.py +++ b/symposion/forms.py @@ -1,3 +1,8 @@ +try: + from collections import OrderedDict +except ImportError: + OrderedDict = None + from django import forms import account.forms @@ -11,8 +16,7 @@ class SignupForm(account.forms.SignupForm): def __init__(self, *args, **kwargs): super(SignupForm, self).__init__(*args, **kwargs) - del self.fields["username"] - self.fields.keyOrder = [ + key_order = [ "email", "email_confirm", "first_name", @@ -20,6 +24,7 @@ class SignupForm(account.forms.SignupForm): "password", "password_confirm" ] + self.fields = reorder_fields(self.fields, key_order) def clean_email_confirm(self): email = self.cleaned_data.get("email") @@ -29,3 +34,24 @@ class SignupForm(account.forms.SignupForm): raise forms.ValidationError( "Email address must match previously typed email address") return email_confirm + + +def reorder_fields(fields, order): + """Reorder form fields by order, removing items not in order. + + >>> reorder_fields( + ... OrderedDict([('a', 1), ('b', 2), ('c', 3)]), + ... ['b', 'c', 'a']) + OrderedDict([('b', 2), ('c', 3), ('a', 1)]) + """ + for key, v in fields.items(): + if key not in order: + del fields[key] + + if not OrderedDict or hasattr(fields, "keyOrder"): + # fields is SortedDict + fields.keyOrder.sort(key=lambda k: order.index(k[0])) + return fields + else: + # fields is OrderedDict + return OrderedDict(sorted(fields.items(), key=lambda k: order.index(k[0]))) diff --git a/symposion/locale/ja/LC_MESSAGES/django.mo b/symposion/locale/ja/LC_MESSAGES/django.mo new file mode 100644 index 00000000..d3c829d1 Binary files /dev/null and b/symposion/locale/ja/LC_MESSAGES/django.mo differ diff --git a/symposion/locale/ja/LC_MESSAGES/django.po b/symposion/locale/ja/LC_MESSAGES/django.po new file mode 100644 index 00000000..ae9c6ea7 --- /dev/null +++ b/symposion/locale/ja/LC_MESSAGES/django.po @@ -0,0 +1,573 @@ +# Symposion japanese translation. +# Copyright (C) 2014-2015, pinax team +# This file is distributed under the same license as the symposion package. +# Hiroshi Miura , 2015. +# +msgid "" +msgstr "" +"Project-Id-Version: symposion\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2014-07-31 14:47-0600\n" +"PO-Revision-Date: 2015-06-18 10:04+0900\n" +"Last-Translator: Hiroshi Miura \n" +"Language-Team: Japanese translation team \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 1.5.4\n" +"Language: ja\n" + +#: cms/models.py:23 +msgid "Draft" +msgstr "草稿" + +#: cms/models.py:24 +msgid "Public" +msgstr "公開" + +#: cms/models.py:57 +msgid "Path can only contain letters, numbers and hyphens and end with /" +msgstr "" +"パス名には、英数字、ハイフン(-)のみが使えます。また末尾は'/'の必要がありま" +"す。" + +#: conference/models.py:15 +msgid "title" +msgstr "タイトル" + +#: conference/models.py:18 conference/models.py:58 +msgid "start date" +msgstr "開始日" + +#: conference/models.py:19 conference/models.py:59 +msgid "end date" +msgstr "終了日" + +#: conference/models.py:22 +msgid "timezone" +msgstr "タイムゾーン" + +#: conference/models.py:41 conference/models.py:52 sponsorship/models.py:18 +msgid "conference" +msgstr "カンファレンス" + +#: conference/models.py:42 +msgid "conferences" +msgstr "カンファレンス" + +#: conference/models.py:54 sponsorship/models.py:19 sponsorship/models.py:155 +msgid "name" +msgstr "名前" + +#: conference/models.py:65 +msgid "section" +msgstr "セクション" + +#: conference/models.py:66 +msgid "sections" +msgstr "セクション" + +#: proposals/models.py:71 templates/conference/user_list.html:60 +msgid "Name" +msgstr "名前" + +#: proposals/models.py:86 +msgid "Brief Description" +msgstr "概要" + +#: proposals/models.py:88 +msgid "" +"If your proposal is accepted this will be made public and printed in the " +"program. Should be one paragraph, maximum 400 characters." +msgstr "" +"もし提案が受諾されたら、これは公開され、配布されるプログラムに印刷されます。" +"段落は一つのみで、400文字以内で記載してください。" + +#: proposals/models.py:92 +msgid "Detailed Abstract" +msgstr "提案の詳細" + +#: proposals/models.py:93 +msgid "" +"Detailed outline. Will be made public if your proposal is accepted. Edit " +"using Markdown." +msgstr "" +"講演詳細:提案が受諾された場合に公開されます。Markdownを用いて記述してください。" + +#: proposals/models.py:99 +msgid "" +"Anything else you'd like the program committee to know when making their " +"selection: your past experience, etc. This is not made public. Edit using Markdown." +msgstr "" +"プログラム委員に、講演者の過去の経験など、とくに伝えたいことがあれば、記述し" +"てください。これは公開されることはありません。Markdownを用いて記述してください。" + +#: proposals/models.py:153 +msgid "Pending" +msgstr "ペンディング" + +#: proposals/models.py:154 templates/proposals/_pending_proposal_row.html:16 +msgid "Accepted" +msgstr "アクセプト" + +#: proposals/models.py:155 +msgid "Declined" +msgstr "リジェクト" + +#: sponsorship/models.py:20 +msgid "order" +msgstr "順序" + +#: sponsorship/models.py:21 +msgid "cost" +msgstr "経費" + +#: sponsorship/models.py:22 sponsorship/models.py:156 +msgid "description" +msgstr "記述" + +#: sponsorship/models.py:22 +msgid "This is private." +msgstr "これは非公開です。" + +#: sponsorship/models.py:26 +msgid "sponsor level" +msgstr "スポンサーのグレード" + +#: sponsorship/models.py:27 +msgid "sponsor levels" +msgstr "スポンサーのグレード" + +#: sponsorship/models.py:38 +msgid "applicant" +msgstr "応募者" + +#: sponsorship/models.py:41 +msgid "Sponsor Name" +msgstr "スポンサー名" + +#: sponsorship/models.py:42 +msgid "external URL" +msgstr "外部URL" + +#: sponsorship/models.py:43 +msgid "annotation" +msgstr "" + +#: sponsorship/models.py:44 +msgid "Contact Name" +msgstr "連絡先の名前" + +#: sponsorship/models.py:45 +msgid "Contact Email" +msgstr "連絡先のEmail" + +#: sponsorship/models.py:46 sponsorship/models.py:167 +msgid "level" +msgstr "グレード" + +#: sponsorship/models.py:47 +msgid "added" +msgstr "" + +#: sponsorship/models.py:48 +msgid "active" +msgstr "有効" + +#: sponsorship/models.py:60 sponsorship/models.py:182 +msgid "sponsor" +msgstr "スポンサー" + +#: sponsorship/models.py:61 +msgid "sponsors" +msgstr "スポンサー" + +#: sponsorship/models.py:157 +msgid "type" +msgstr "タイプ" + +#: sponsorship/models.py:166 sponsorship/models.py:183 +msgid "benefit" +msgstr "" + +#: sponsorship/models.py:170 sponsorship/models.py:187 +msgid "max words" +msgstr "" + +#: sponsorship/models.py:171 sponsorship/models.py:188 +msgid "other limits" +msgstr "他の制限" + +#: sponsorship/models.py:192 +msgid "text" +msgstr "テキスト" + +#: sponsorship/models.py:193 +msgid "file" +msgstr "ファイル" + +#: templates/dashboard.html:16 +msgid "Speaking" +msgstr "講演" + +#: templates/dashboard.html:92 templates/sponsorship/detail.html:8 +msgid "Sponsorship" +msgstr "スポンサー" + +#: templates/dashboard.html:132 templates/reviews/review_detail.html:75 +msgid "Reviews" +msgstr "レビュー" + +#: templates/dashboard.html:177 +msgid "Teams" +msgstr "チーム" + +#: templates/boxes/box.html:9 +msgid "Editing content:" +msgstr "コンテンツ編集:" + +#: templates/cms/page_edit.html:11 +msgid "Edit page at:" +msgstr "" + +#: templates/conference/user_list.html:59 +msgid "Email" +msgstr "電子メール" + +#: templates/conference/user_list.html:61 +msgid "Speaker Profile?" +msgstr "講演者のプロフィール" + +#: templates/emails/teams_user_applied/message.html:3 +#, python-format +msgid "" +"\n" +"

\n" +" User \"%(username)s\" has applied to join %(team_name)s on " +"%(site_name)s.\n" +"

\n" +"\n" +"

\n" +" To accept this application and see any other pending applications, " +"visit the following url:\n" +" http://%(site_url)s" +"%(team_url)s\n" +"

\n" +msgstr "" +"\n" +"

\n" +" ユーザ \"%(username)s\" は、%(site_name)s の%(team_name)s に" +"応募しました。 \n" +"

\n" +"\n" +"

\n" +" この応募を受諾したり、他の保留されている応募者を確認するには、つぎの" +"URLを参照してください:\n" +" http://%(site_url)s" +"%(team_url)s\n" +"

\n" + +#: templates/emails/teams_user_applied/subject.txt:1 +#, python-format +msgid "%(username)s has applied to to join \"%(team)s\"" +msgstr "%(username)s は、 \"%(team)s\"に応募しました。" + +#: templates/emails/teams_user_invited/message.html:3 +#, python-format +msgid "" +"\n" +"

\n" +" You have been invited to join %(team_name)s on " +"%(site_name)s.\n" +"

\n" +"\n" +"

\n" +" To accept this invitation, visit the following url:\n" +" http://%(site_url)s" +"%(team_url)s\n" +"

\n" +msgstr "" +"\n" +"

\n" +" %(site_name)sの%(team_name)sに参加するよう招待されました。\n" +"

\n" +"\n" +"

\n" +" 招待を受諾するには、つぎのURLをクリックしてください:\n" +" http://%(site_url)s" +"%(team_url)s\n" +"

\n" + +#: templates/emails/teams_user_invited/subject.txt:1 +#, python-format +msgid "You have been invited to join \"%(team)s\"" +msgstr " \"%(team)s\"に参加するよう招待されました。" + +#: templates/proposals/_pending_proposal_row.html:12 +msgid "Cancelled" +msgstr "キャンセル済み" + +#: templates/proposals/_pending_proposal_row.html:18 +msgid "Submitted" +msgstr "投稿済み" + +#: templates/proposals/_pending_proposal_row.html:21 +msgid "Invited" +msgstr "招待された" + +#: templates/proposals/_pending_proposal_row.html:30 +msgid "Choose Response" +msgstr "応答の選択" + +#: templates/proposals/_pending_proposal_row.html:35 +msgid "Accept invitation" +msgstr "招待を受諾" + +#: templates/proposals/_pending_proposal_row.html:37 +msgid "Decline invitation" +msgstr "招待を拒否" + +#: templates/proposals/_proposal_fields.html:4 +msgid "Submitted by" +msgstr "" + +#: templates/proposals/_proposal_fields.html:7 +msgid "Track" +msgstr "トラック" + +#: templates/proposals/_proposal_fields.html:10 +msgid "Audience Level" +msgstr "受講者のレベル" + +#: templates/proposals/_proposal_fields.html:14 +msgid "Additional Speakers" +msgstr "追加の講演者" + +#: templates/proposals/_proposal_fields.html:21 +msgid "Invitation Sent" +msgstr "送信された招待" + +#: templates/proposals/_proposal_fields.html:28 +msgid "Description" +msgstr "記述" + +#: templates/proposals/_proposal_fields.html:31 +msgid "Abstract" +msgstr "講演概要" + +#: templates/proposals/_proposal_fields.html:34 +msgid "Notes" +msgstr "備考" + +#: templates/proposals/_proposal_fields.html:37 +msgid "Speaker Bio" +msgstr "講演者略歴" + +#: templates/proposals/_proposal_fields.html:40 +msgid "Documents" +msgstr "資料" + +#: templates/proposals/proposal_cancel.html:7 +msgid "Cancel Proposal" +msgstr "講演提案のキャンセル" + +#: templates/proposals/proposal_cancel.html:16 +msgid "No, keep it for now" +msgstr "いいえ、このままにします。" + +#: templates/proposals/proposal_detail.html:14 +msgid "Edit this proposal" +msgstr "この提案を編集" + +#: templates/proposals/proposal_detail.html:17 +msgid "Cancel this proposal" +msgstr "この提案をキャンセル" + +#: templates/proposals/proposal_detail.html:21 +msgid "Remove me from this proposal" +msgstr "この提案から自分を削除する。" + +#: templates/proposals/proposal_detail.html:33 +#: templates/reviews/review_detail.html:74 +msgid "Proposal Details" +msgstr "提案の詳細" + +#: templates/proposals/proposal_detail.html:35 +#: templates/proposals/proposal_detail.html:47 +msgid "Supporting Documents" +msgstr "補足資料" + +#: templates/proposals/proposal_detail.html:38 +msgid "Reviewer Feedback" +msgstr "レビュアーからのフィードバック" + +#: templates/proposals/proposal_detail.html:57 +msgid "delete" +msgstr "削除" + +#: templates/proposals/proposal_detail.html:64 +msgid "No supporting documents attached to this proposal." +msgstr "この提案に補足資料は添付されていません。" + +#: templates/proposals/proposal_detail.html:66 +msgid "Add Document" +msgstr "資料の追加" + +#: templates/proposals/proposal_detail.html:73 +msgid "Conversation with Reviewers" +msgstr "レビュアーとの会話記録" + +#: templates/proposals/proposal_detail.html:83 +msgid "Leave a Message" +msgstr "メッセージを残す" + +#: templates/proposals/proposal_detail.html:85 +msgid "You can leave a message for the reviewers here." +msgstr "メッセージをレビューアに残すことができます。" + +#: templates/proposals/proposal_detail.html:94 +msgid "Submit" +msgstr "投稿する" + +#: templates/proposals/proposal_speaker_manage.html:7 +msgid "Proposal:" +msgstr "講演提案:" + +#: templates/proposals/proposal_speaker_manage.html:10 +msgid "Edit proposal" +msgstr "講演提案の編集" + +#: templates/proposals/proposal_speaker_manage.html:14 +msgid "Current Speakers" +msgstr "現在登録されている講演者" + +#: templates/proposals/proposal_speaker_manage.html:20 +msgid "pending invitation" +msgstr "保留されている招待" + +#: templates/proposals/proposal_speaker_manage.html:24 +msgid "Add another speaker" +msgstr "他の講演者を追加" + +#: templates/proposals/proposal_submit.html:6 +msgid "Submit A Proposal" +msgstr "提案を投稿" + +#: templates/reviews/_review_table.html:6 +#: templates/reviews/result_notification.html:45 +msgid "Speaker / Title" +msgstr "講演者/タイトル" + +#: templates/reviews/_review_table.html:7 +#: templates/reviews/result_notification.html:46 +msgid "Category" +msgstr "カテゴリ" + +#: templates/reviews/_review_table.html:9 +msgid "+1" +msgstr "" + +#: templates/reviews/_review_table.html:10 +msgid "+0" +msgstr "" + +#: templates/reviews/_review_table.html:11 +msgid "-0" +msgstr "" + +#: templates/reviews/_review_table.html:12 +msgid "-1" +msgstr "" + +#: templates/reviews/_review_table.html:13 +msgid "Your Rating" +msgstr "あなたの投票" + +#: templates/reviews/base.html:64 +msgid "All Reviews" +msgstr "全てのレビュアー" + +#: templates/reviews/base.html:77 +msgid "Voting Status" +msgstr "投票の状態" + +#: templates/reviews/result_notification.html:47 +msgid "Status" +msgstr "状態" + +#: templates/reviews/result_notification.html:48 +msgid "Notified?" +msgstr "連絡済み?" + +#: templates/reviews/review_detail.html:76 +msgid "Speaker Feedback" +msgstr "講演者へのフィードバック" + +#: templates/reviews/review_detail.html:84 +msgid "Current Results" +msgstr "現在のところの結果" + +#: templates/reviews/review_detail.html:91 +msgid "Total Responses" +msgstr "全応答数" + +#: templates/reviews/review_detail.html:108 +msgid "Submit Review" +msgstr "レビューの投稿" + +#: templates/reviews/review_detail.html:148 +msgid "Conversation with the submitter" +msgstr "投稿者への連絡" + +#: templates/reviews/review_detail.html:162 +msgid "Send a message" +msgstr "メッセージ送信" + +#: templates/reviews/review_detail.html:164 +msgid "" +"\n" +" If you'd like to communicate with the submitter, " +"use the following form and he or she will be\n" +" notified and given the opportunity to respond.\n" +" " +msgstr "" +"\n" +" もし、投稿者と連絡したい場合は、次の投稿フォームを" +"用いて知らせることができます。\n" +" そして、回答の機会を与えることができます。\n" +" " + +#: templates/schedule/_slot_edit.html:5 +msgid "Edit Slot" +msgstr "スロットの編集" + +#: templates/speakers/speaker_create.html:7 +#: templates/speakers/speaker_create.html:14 +msgid "Create Speaker Profile" +msgstr "講演者プロフィールの作成" + +#: templates/speakers/speaker_edit.html:7 +#: templates/speakers/speaker_edit.html:14 +msgid "Edit Speaker Profile" +msgstr "講演者プロフィールの編集" + +#: templates/sponsorship/add.html:7 templates/sponsorship/add.html.py:14 +msgid "Add a Sponsor" +msgstr "スポンサーの追加" + +#: templates/sponsorship/apply.html:7 +msgid "Apply to be a Sponsor" +msgstr "スポンサーに応募する" + +#: templates/sponsorship/apply.html:17 +msgid "Apply to Be a Sponsor" +msgstr "スポンサーに応募する" + +#: templates/sponsorship/list.html:7 templates/sponsorship/list.html.py:14 +msgid "About Our Sponsors" +msgstr "スポンサーについて" diff --git a/symposion/proposals/models.py b/symposion/proposals/models.py index 5cb4baa6..7dea87cd 100644 --- a/symposion/proposals/models.py +++ b/symposion/proposals/models.py @@ -9,6 +9,7 @@ from django.utils.timezone import now from django.contrib.auth.models import User from django.core.exceptions import ObjectDoesNotExist +from django.core.exceptions import ValidationError import reversion @@ -106,8 +107,15 @@ class ProposalBase(models.Model): editable=False, ) speaker = models.ForeignKey(Speaker, related_name="proposals") + + def additional_speaker_validator(self, a_speaker): + if a_speaker.speaker.email == self.speaker.email: + raise ValidationError(_("%s is same as primary speaker.") % a_speaker.speaker.email) + if a_speaker in [self.additional_speakers]: + raise ValidationError(_("%s has already been in speakers.") % a_speaker.speaker.email) + additional_speakers = models.ManyToManyField(Speaker, through="AdditionalSpeaker", - blank=True) + blank=True, validators=[additional_speaker_validator]) cancelled = models.BooleanField(default=False) def can_edit(self): @@ -147,7 +155,6 @@ class ProposalBase(models.Model): "kind": self.kind.name, } - reversion.register(ProposalBase) @@ -170,6 +177,14 @@ class AdditionalSpeaker(models.Model): class Meta: unique_together = ("speaker", "proposalbase") + def __unicode__(self): + if self.status is self.SPEAKING_STATUS_PENDING: + return _(u"pending speaker (%s)") % self.speaker.email + elif self.status is self.SPEAKING_STATUS_DECLINED: + return _(u"declined speaker (%s)") % self.speaker.email + else: + return self.speaker.name + def uuid_filename(instance, filename): ext = filename.split(".")[-1] diff --git a/symposion/schedule/admin.py b/symposion/schedule/admin.py index 68508503..46ac88a7 100644 --- a/symposion/schedule/admin.py +++ b/symposion/schedule/admin.py @@ -3,28 +3,53 @@ from django.contrib import admin from symposion.schedule.models import Schedule, Day, Room, SlotKind, Slot, SlotRoom, Presentation, Session, SessionRole -admin.site.register(Schedule) +class DayInline(admin.StackedInline): + model = Day + extra = 2 + + +class SlotKindInline(admin.StackedInline): + model = SlotKind + + +class ScheduleAdmin(admin.ModelAdmin): + model = Schedule + inlines = [DayInline, SlotKindInline, ] + + +class SlotRoomInline(admin.TabularInline): + model = SlotRoom + extra = 1 + + +class SlotAdmin(admin.ModelAdmin): + list_filter = ("day", "kind") + list_display = ("day", "start", "end", "kind", "content") + inlines = [SlotRoomInline] + +class RoomAdmin(admin.ModelAdmin): + list_display = ["name", "order", "schedule"] + list_filter = ["schedule"] + inlines = [SlotRoomInline] + + +class PresentationAdmin(admin.ModelAdmin): + model = Presentation + list_filter = ("section", "cancelled", "slot") + + admin.site.register(Day) -admin.site.register( - Room, - list_display=("name", "order", "schedule"), - list_filter=("schedule",) -) admin.site.register( SlotKind, - list_display=("label", "schedule"), -) -admin.site.register( - Slot, - list_display=("day", "start", "end", "kind", "content") + list_display=["label", "schedule"], ) admin.site.register( SlotRoom, - list_display=("slot", "room") + list_display=["slot", "room"] ) +admin.site.register(Schedule, ScheduleAdmin) +admin.site.register(Room, RoomAdmin) +admin.site.register(Slot, SlotAdmin) admin.site.register(Session) admin.site.register(SessionRole) -admin.site.register( - Presentation, - list_filter=("section", "cancelled", "slot") -) +admin.site.register(Presentation, PresentationAdmin) diff --git a/symposion/schedule/models.py b/symposion/schedule/models.py index 5b2a9413..a0f88e81 100644 --- a/symposion/schedule/models.py +++ b/symposion/schedule/models.py @@ -125,7 +125,8 @@ class Slot(models.Model): return Room.objects.filter(pk__in=self.slotroom_set.values("room")) def __unicode__(self): - return u"%s %s (%s - %s)" % (self.day, self.kind, self.start, self.end) + roomlist = ' '.join(map(lambda r: r.__unicode__(), self.rooms)) + return u"%s %s (%s - %s) %s" % (self.day, self.kind, self.start, self.end, roomlist) class Meta: ordering = ["day", "start", "end"] diff --git a/symposion/sponsorship/admin.py b/symposion/sponsorship/admin.py index b64c1a13..77a84b8d 100644 --- a/symposion/sponsorship/admin.py +++ b/symposion/sponsorship/admin.py @@ -1,4 +1,7 @@ from django.contrib import admin +from django.utils.html import escape +from django.utils.safestring import mark_safe +from django.utils.translation import ugettext_lazy as _ from symposion.sponsorship.models import SponsorLevel, Sponsor, Benefit, BenefitLevel, \ SponsorBenefit @@ -43,7 +46,16 @@ class SponsorAdmin(admin.ModelAdmin): }) ] inlines = [SponsorBenefitInline] - list_display = ["name", "external_url", "level", "active"] + list_display = ["name", "external_url", "level", "active", "contact", "applicant_field"] + + def contact(self, sponsor): + return mark_safe('%s' % (escape(sponsor.contact_email), escape(sponsor.contact_name))) + + def applicant_field(self, sponsor): + name = sponsor.applicant.get_full_name() + email = sponsor.applicant.email + return mark_safe('%s' % (escape(email), escape(name))) + applicant_field.short_description = _(u"Applicant") def get_form(self, *args, **kwargs): # @@@ kinda ugly but using choices= on NullBooleanField is broken diff --git a/symposion/sponsorship/management/commands/export_sponsors_data.py b/symposion/sponsorship/management/commands/export_sponsors_data.py new file mode 100644 index 00000000..61172d44 --- /dev/null +++ b/symposion/sponsorship/management/commands/export_sponsors_data.py @@ -0,0 +1,77 @@ +import csv +import os +import shutil +import zipfile + +from contextlib import closing + +from django.core.management.base import BaseCommand +from django.template.defaultfilters import slugify + +from sotmjp.sponsorship.models import Sponsor + + +def zipdir(basedir, archivename): + assert os.path.isdir(basedir) + with closing(zipfile.ZipFile(archivename, "w", zipfile.ZIP_DEFLATED)) as z: + for root, dirs, files in os.walk(basedir): + #NOTE: ignore empty directories + for fn in files: + absfn = os.path.join(root, fn) + zfn = absfn[len(basedir) + len(os.sep):] # XXX: relative path + z.write(absfn, zfn) + + +class Command(BaseCommand): + + def handle(self, *args, **options): + try: + os.makedirs(os.path.join(os.getcwd(), "build")) + except: + pass + + csv_file = csv.writer( + open(os.path.join(os.getcwd(), "build", "sponsors.csv"), "wb") + ) + csv_file.writerow(["Name", "URL", "Level", "Description"]) + + for sponsor in Sponsor.objects.all(): + path = os.path.join(os.getcwd(), "build", slugify(sponsor.name)) + try: + os.makedirs(path) + except: + pass + + data = { + "name": sponsor.name, + "url": sponsor.external_url, + "level": sponsor.level.name, + "description": "", + } + for sponsor_benefit in sponsor.sponsor_benefits.all(): + if sponsor_benefit.benefit_id == 2: + data["description"] = sponsor_benefit.text + if sponsor_benefit.benefit_id == 1: + if sponsor_benefit.upload: + data["ad"] = sponsor_benefit.upload.path + if sponsor_benefit.benefit_id == 7: + if sponsor_benefit.upload: + data["logo"] = sponsor_benefit.upload.path + + if "ad" in data: + ad_path = data.pop("ad") + shutil.copy(ad_path, path) + if "logo" in data: + logo_path = data.pop("logo") + shutil.copy(logo_path, path) + + csv_file.writerow([ + data["name"].encode("utf-8"), + data["url"].encode("utf-8"), + data["level"].encode("utf-8"), + data["description"].encode("utf-8") + ]) + + zipdir( + os.path.join(os.getcwd(), "build"), + os.path.join(os.getcwd(), "sponsors.zip")) diff --git a/symposion/sponsorship/models.py b/symposion/sponsorship/models.py index e29ac3d1..d4161176 100644 --- a/symposion/sponsorship/models.py +++ b/symposion/sponsorship/models.py @@ -1,5 +1,6 @@ import datetime +from django.conf import settings from django.core.exceptions import ValidationError from django.core.urlresolvers import reverse from django.db import models @@ -39,6 +40,7 @@ class Sponsor(models.Model): null=True) name = models.CharField(_("Sponsor Name"), max_length=100) + display_url = models.URLField(_("display URL"), blank=True) external_url = models.URLField(_("external URL")) annotation = models.TextField(_("annotation"), blank=True) contact_name = models.CharField(_("Contact Name"), max_length=100) @@ -65,6 +67,12 @@ class Sponsor(models.Model): return reverse("sponsor_detail", kwargs={"pk": self.pk}) return reverse("sponsor_list") + def get_display_url(self): + if self.display_url: + return self.display_url + else: + return self.external_url + @property def website_logo(self): if self.sponsor_logo is None: @@ -144,9 +152,17 @@ post_save.connect(_check_level_change, sender=Sponsor) BENEFIT_TYPE_CHOICES = [ ("text", "Text"), + ("richtext", "Rich Text"), ("file", "File"), ("weblogo", "Web Logo"), - ("simple", "Simple") + ("simple", "Simple"), + ("option", "Option") +] + +CONTENT_TYPE_CHOICES = [ + ("simple", "Simple"), +] + [ + ("listing_text_%s" % lang, "Listing Text (%s)" % label) for lang, label in settings.LANGUAGES ] @@ -154,8 +170,10 @@ class Benefit(models.Model): name = models.CharField(_("name"), max_length=100) description = models.TextField(_("description"), blank=True) - type = models.CharField(_("type"), choices=BENEFIT_TYPE_CHOICES, max_length=10, - default="simple") + type = models.CharField(_("type"), choices=BENEFIT_TYPE_CHOICES, + max_length=10, default="simple") + content_type = models.CharField(_("content type"), choices=CONTENT_TYPE_CHOICES, + max_length=20, default="simple") def __unicode__(self): return self.name diff --git a/symposion/sponsorship/templatetags/sponsorship_tags.py b/symposion/sponsorship/templatetags/sponsorship_tags.py index d88243c4..efbdda09 100644 --- a/symposion/sponsorship/templatetags/sponsorship_tags.py +++ b/symposion/sponsorship/templatetags/sponsorship_tags.py @@ -1,4 +1,5 @@ from django import template +from django.template.defaultfilters import linebreaks, urlize from symposion.conference.models import current_conference from symposion.sponsorship.models import Sponsor, SponsorLevel @@ -75,3 +76,45 @@ def sponsor_levels(parser, token): {% sponsor_levels as levels %} """ return SponsorLevelNode.handle_token(parser, token) + + +class LocalizedTextNode(template.Node): + + @classmethod + def handle_token(cls, parser, token): + bits = token.split_contents() + if len(bits) == 3: + return cls(bits[2], bits[1][1:-1]) + elif len(bits) == 5 and bits[-2] == "as": + return cls(bits[2], bits[1][1:-1], bits[4]) + else: + raise template.TemplateSyntaxError("%r takes 'as var'" % bits[0]) + + def __init__(self, sponsor, content_type, context_var=None): + self.sponsor_var = template.Variable(sponsor) + self.content_type = content_type + self.content_var = context_var + + def render(self, context): + s = '' + try: + sponsor = self.sponsor_var.resolve(context) + content_type = '%s_%s' % (self.content_type, context['request'].LANGUAGE_CODE) + texts = sponsor.sponsor_benefits.filter(benefit__content_type=content_type) + if texts.count() > 0: + s = linebreaks(urlize(texts[0].text, autoescape=True)) + if self.content_var: + context[self.content_var] = s + s = '' + except: + pass + return s + + +@register.tag +def localized_text(parser, token): + """ + {% localized_text "content_type" sponsor %} + {% localized_text "content_type" sponsor as localized_text %} + """ + return LocalizedTextNode.handle_token(parser, token) diff --git a/symposion/sponsorship/tests.py b/symposion/sponsorship/tests.py new file mode 100644 index 00000000..a6eae66b --- /dev/null +++ b/symposion/sponsorship/tests.py @@ -0,0 +1,307 @@ +from cStringIO import StringIO +import os +import shutil +import tempfile +from zipfile import ZipFile + +from django.conf import settings +from django.contrib.auth.models import User +from django.core.exceptions import ValidationError +from django.core.urlresolvers import reverse +from django.test import TestCase +from django.test.utils import override_settings + +from pycon.sponsorship.models import Benefit, Sponsor, SponsorBenefit,\ + SponsorLevel +from symposion.conference.models import current_conference + + +class TestSponsorZipDownload(TestCase): + def setUp(self): + self.user = User.objects.create_user(username='joe', + email='joe@example.com', + password='joe') + self.user.is_staff = True + self.user.save() + self.url = reverse("sponsor_zip_logos") + self.assertTrue(self.client.login(username='joe@example.com', + password='joe')) + + # we need a sponsor + conference = current_conference() + self.sponsor_level = SponsorLevel.objects.create( + conference=conference, name="Lead", cost=1) + self.sponsor = Sponsor.objects.create( + name="Big Daddy", + level=self.sponsor_level, + active=True, + ) + + # Create our benefits, of various types + self.text_benefit = Benefit.objects.create(name="text", type="text") + self.file_benefit = Benefit.objects.create(name="file", type="file") + # These names must be spelled exactly this way: + self.weblogo_benefit = Benefit.objects.create(name="Web logo", type="weblogo") + self.printlogo_benefit = Benefit.objects.create(name="Print logo", type="file") + self.advertisement_benefit = Benefit.objects.create(name="Advertisement", type="file") + + def validate_response(self, rsp, names_and_sizes): + # Ensure a response from the view looks right, contains a valid + # zip archive, has files with the right names and sizes. + self.assertEqual("application/zip", rsp['Content-type']) + prefix = settings.CONFERENCE_URL_PREFIXES[settings.CONFERENCE_ID] + + self.assertEqual( + 'attachment; filename="pycon_%s_sponsorlogos.zip"' % prefix, + rsp['Content-Disposition']) + zipfile = ZipFile(StringIO(rsp.content), "r") + # Check out the zip - testzip() returns None if no errors found + self.assertIsNone(zipfile.testzip()) + # Compare contents to what is expected + infolist = zipfile.infolist() + self.assertEqual(len(names_and_sizes), len(infolist)) + for info, name_and_size in zip(infolist, names_and_sizes): + name, size = name_and_size + self.assertEqual(name, info.filename) + self.assertEqual(size, info.file_size) + + def make_temp_file(self, name, size=0): + # Create a temp file with the given name and size under self.temp_dir + path = os.path.join(self.temp_dir, name) + with open(path, "wb") as f: + f.write(size * "x") + + def test_must_be_logged_in(self): + # Must be logged in to use the view + # If not logged in, doesn't redirect, just serves up a login view + self.client.logout() + rsp = self.client.get(self.url) + self.assertEqual(200, rsp.status_code) + self.assertIn("""""", rsp.content) + + def test_must_be_staff(self): + # Only staff can use the view + # If not staff, doesn't show error, just serves up a login view + # Also, the dashboard doesn't show the download button + self.user.is_staff = False + self.user.save() + rsp = self.client.get(self.url) + self.assertEqual(200, rsp.status_code) + self.assertIn("""""", rsp.content) + rsp = self.client.get(reverse('dashboard')) + self.assertNotIn(self.url, rsp.content) + + def test_no_files(self): + # If there are no sponsor files, we still work + # And the dashboard shows our download button + rsp = self.client.get(self.url) + self.validate_response(rsp, []) + rsp = self.client.get(reverse('dashboard')) + self.assertIn(self.url, rsp.content) + + def test_different_benefit_types(self): + # We only get files from the benefits named "Print logo" and "Web logo" + # And we ignore any non-existent files + try: + # Create a temp dir for media files + self.temp_dir = tempfile.mkdtemp() + with override_settings(MEDIA_ROOT=self.temp_dir): + + # Give our sponsor some benefits + SponsorBenefit.objects.create( + sponsor=self.sponsor, + benefit=self.text_benefit, + text="Foo!" + ) + + self.make_temp_file("file1", 10) + SponsorBenefit.objects.create( + sponsor=self.sponsor, + benefit=self.file_benefit, + upload="file1" + ) + + self.make_temp_file("file2", 20) + SponsorBenefit.objects.create( + sponsor=self.sponsor, + benefit=self.weblogo_benefit, + upload="file2" + ) + + # Benefit whose file is missing from the disk + SponsorBenefit.objects.create( + sponsor=self.sponsor, + benefit=self.weblogo_benefit, + upload="file3" + ) + + # print logo benefit + self.make_temp_file("file4", 40) + SponsorBenefit.objects.create( + sponsor=self.sponsor, + benefit=self.printlogo_benefit, + upload="file4" + ) + + self.make_temp_file("file5", 50) + SponsorBenefit.objects.create( + sponsor=self.sponsor, + benefit=self.advertisement_benefit, + upload="file5" + ) + + rsp = self.client.get(self.url) + expected = [ + ('web_logos/lead/big_daddy/file2', 20), + ('print_logos/lead/big_daddy/file4', 40), + ('advertisement/lead/big_daddy/file5', 50) + ] + self.validate_response(rsp, expected) + finally: + if hasattr(self, 'temp_dir'): + # Clean up any temp media files + shutil.rmtree(self.temp_dir) + + def test_file_org(self): + # The zip file is organized into directories: + # {print_logos,web_logos,advertisement}/// + + # Add another sponsor at a different sponsor level + conference = current_conference() + self.sponsor_level2 = SponsorLevel.objects.create( + conference=conference, name="Silly putty", cost=1) + self.sponsor2 = Sponsor.objects.create( + name="Big Mama", + level=self.sponsor_level2, + active=True, + ) + # + try: + # Create a temp dir for media files + self.temp_dir = tempfile.mkdtemp() + with override_settings(MEDIA_ROOT=self.temp_dir): + + # Give our sponsors some benefits + self.make_temp_file("file1", 10) + SponsorBenefit.objects.create( + sponsor=self.sponsor, + benefit=self.weblogo_benefit, + upload="file1" + ) + # print logo benefit + self.make_temp_file("file2", 20) + SponsorBenefit.objects.create( + sponsor=self.sponsor, + benefit=self.printlogo_benefit, + upload="file2" + ) + # Sponsor 2 + self.make_temp_file("file3", 30) + SponsorBenefit.objects.create( + sponsor=self.sponsor2, + benefit=self.weblogo_benefit, + upload="file3" + ) + # print logo benefit + self.make_temp_file("file4", 42) + SponsorBenefit.objects.create( + sponsor=self.sponsor2, + benefit=self.printlogo_benefit, + upload="file4" + ) + # ad benefit + self.make_temp_file("file5", 55) + SponsorBenefit.objects.create( + sponsor=self.sponsor2, + benefit=self.advertisement_benefit, + upload="file5" + ) + + rsp = self.client.get(self.url) + expected = [ + ('web_logos/lead/big_daddy/file1', 10), + ('web_logos/silly_putty/big_mama/file3', 30), + ('print_logos/lead/big_daddy/file2', 20), + ('print_logos/silly_putty/big_mama/file4', 42), + ('advertisement/silly_putty/big_mama/file5', 55), + ] + self.validate_response(rsp, expected) + finally: + if hasattr(self, 'temp_dir'): + # Clean up any temp media files + shutil.rmtree(self.temp_dir) + + +class TestBenefitValidation(TestCase): + """ + It should not be possible to save a SponsorBenefit if it has the + wrong kind of data in it - e.g. a text-type benefit cannot have + an uploaded file, and vice-versa. + """ + def setUp(self): + # we need a sponsor + conference = current_conference() + self.sponsor_level = SponsorLevel.objects.create( + conference=conference, name="Lead", cost=1) + self.sponsor = Sponsor.objects.create( + name="Big Daddy", + level=self.sponsor_level, + ) + + # Create our benefit types + self.text_type = Benefit.objects.create(name="text", type="text") + self.file_type = Benefit.objects.create(name="file", type="file") + self.weblogo_type = Benefit.objects.create(name="log", type="weblogo") + self.simple_type = Benefit.objects.create(name="simple", type="simple") + + def validate(self, should_work, benefit_type, upload, text): + obj = SponsorBenefit( + benefit=benefit_type, + sponsor=self.sponsor, + upload=upload, + text=text + ) + if should_work: + obj.save() + else: + with self.assertRaises(ValidationError): + obj.save() + + def test_text_has_text(self): + self.validate(True, self.text_type, upload=None, text="Some text") + + def test_text_has_upload(self): + self.validate(False, self.text_type, upload="filename", text='') + + def test_text_has_both(self): + self.validate(False, self.text_type, upload="filename", text="Text") + + def test_file_has_text(self): + self.validate(False, self.file_type, upload=None, text="Some text") + + def test_file_has_upload(self): + self.validate(True, self.file_type, upload="filename", text='') + + def test_file_has_both(self): + self.validate(False, self.file_type, upload="filename", text="Text") + + def test_weblogo_has_text(self): + self.validate(False, self.weblogo_type, upload=None, text="Some text") + + def test_weblogo_has_upload(self): + self.validate(True, self.weblogo_type, upload="filename", text='') + + def test_weblogo_has_both(self): + self.validate(False, self.weblogo_type, upload="filename", text="Text") + + def test_simple_has_neither(self): + self.validate(True, self.simple_type, upload=None, text='') + + def test_simple_has_text(self): + self.validate(True, self.simple_type, upload=None, text="Some text") + + def test_simple_has_upload(self): + self.validate(False, self.simple_type, upload="filename", text='') + + def test_simple_has_both(self): + self.validate(False, self.simple_type, upload="filename", text="Text") diff --git a/symposion/sponsorship/urls.py b/symposion/sponsorship/urls.py index 17db5124..d7027fea 100644 --- a/symposion/sponsorship/urls.py +++ b/symposion/sponsorship/urls.py @@ -7,5 +7,6 @@ urlpatterns = patterns( url(r"^$", TemplateView.as_view(template_name="sponsorship/list.html"), name="sponsor_list"), url(r"^apply/$", "sponsor_apply", name="sponsor_apply"), url(r"^add/$", "sponsor_add", name="sponsor_add"), + url(r"^ziplogos/$", "sponsor_zip_logo_files", name="sponsor_zip_logos"), url(r"^(?P\d+)/$", "sponsor_detail", name="sponsor_detail"), ) diff --git a/symposion/sponsorship/views.py b/symposion/sponsorship/views.py index e3b22697..bf74ec3a 100644 --- a/symposion/sponsorship/views.py +++ b/symposion/sponsorship/views.py @@ -1,13 +1,28 @@ -from django.http import Http404 +from cStringIO import StringIO +import itertools +import logging +import os +import time +from zipfile import ZipFile, ZipInfo + +from django.conf import settings + +from django.http import Http404, HttpResponse from django.shortcuts import render_to_response, redirect, get_object_or_404 from django.template import RequestContext +from django.utils.translation import ugettext_lazy as _ from django.contrib import messages +from django.contrib.admin.views.decorators import staff_member_required from django.contrib.auth.decorators import login_required -from symposion.sponsorship.forms import SponsorApplicationForm, SponsorDetailsForm, \ - SponsorBenefitsFormSet -from symposion.sponsorship.models import Sponsor, SponsorBenefit +from symposion.sponsorship.forms import SponsorApplicationForm, \ + SponsorDetailsForm, SponsorBenefitsFormSet +from symposion.sponsorship.models import Benefit, Sponsor, SponsorBenefit, \ + SponsorLevel + + +log = logging.getLogger(__name__) @login_required @@ -18,13 +33,13 @@ def sponsor_apply(request): sponsor = form.save() if sponsor.sponsor_benefits.all(): # Redirect user to sponsor_detail to give extra information. - messages.success(request, "Thank you for your sponsorship " - "application. Please update your " - "benefit details below.") + messages.success(request, _("Thank you for your sponsorship " + "application. Please update your " + "benefit details below.")) return redirect("sponsor_detail", pk=sponsor.pk) else: - messages.success(request, "Thank you for your sponsorship " - "application.") + messages.success(request, _("Thank you for your sponsorship " + "application.")) return redirect("dashboard") else: form = SponsorApplicationForm(user=request.user) @@ -87,3 +102,96 @@ def sponsor_detail(request, pk): "form": form, "formset": formset, }, context_instance=RequestContext(request)) + + +@staff_member_required +def sponsor_export_data(request): + sponsors = [] + data = "" + + for sponsor in Sponsor.objects.order_by("added"): + d = { + "name": sponsor.name, + "url": sponsor.external_url, + "level": (sponsor.level.order, sponsor.level.name), + "description": "", + } + for sponsor_benefit in sponsor.sponsor_benefits.all(): + if sponsor_benefit.benefit_id == 2: + d["description"] = sponsor_benefit.text + sponsors.append(d) + + def izip_longest(*args): + fv = None + + def sentinel(counter=([fv] * (len(args) - 1)).pop): + yield counter() + iters = [itertools.chain(it, sentinel(), itertools.repeat(fv)) for it in args] + try: + for tup in itertools.izip(*iters): + yield tup + except IndexError: + pass + + def pairwise(iterable): + a, b = itertools.tee(iterable) + b.next() + return izip_longest(a, b) + + def level_key(s): + return s["level"] + + for level, level_sponsors in itertools.groupby(sorted(sponsors, key=level_key), level_key): + data += "%s\n" % ("-" * (len(level[1]) + 4)) + data += "| %s |\n" % level[1] + data += "%s\n\n" % ("-" * (len(level[1]) + 4)) + for sponsor, next in pairwise(level_sponsors): + description = sponsor["description"].strip() + description = description if description else "-- NO DESCRIPTION FOR THIS SPONSOR --" + data += "%s\n\n%s" % (sponsor["name"], description) + if next is not None: + data += "\n\n%s\n\n" % ("-" * 80) + else: + data += "\n\n" + + return HttpResponse(data, content_type="text/plain;charset=utf-8") + + +@staff_member_required +def sponsor_zip_logo_files(request): + """Return a zip file of sponsor web and print logos""" + + zip_stringio = StringIO() + zipfile = ZipFile(zip_stringio, "w") + try: + benefits = Benefit.objects.all() + for benefit in benefits: + dir_name = benefit.name.lower().replace(" ", "_").replace('/', '_') + for level in SponsorLevel.objects.all(): + level_name = level.name.lower().replace(" ", "_").replace('/', '_') + for sponsor in Sponsor.objects.filter(level=level, active=True): + sponsor_name = sponsor.name.lower().replace(" ", "_").replace('/', '_') + full_dir = "/".join([dir_name, level_name, sponsor_name]) + for sponsor_benefit in SponsorBenefit.objects.filter( + benefit=benefit, + sponsor=sponsor, + active=True, + ).exclude(upload=''): + if os.path.exists(sponsor_benefit.upload.path): + modtime = time.gmtime(os.stat(sponsor_benefit.upload.path).st_mtime) + with open(sponsor_benefit.upload.path, "rb") as f: + fname = os.path.split(sponsor_benefit.upload.name)[-1] + zipinfo = ZipInfo(filename=full_dir + "/" + fname, + date_time=modtime) + zipfile.writestr(zipinfo, f.read()) + else: + log.debug("No such sponsor file: %s" % sponsor_benefit.upload.path) + finally: + zipfile.close() + + response = HttpResponse(zip_stringio.getvalue(), + content_type="application/zip") + prefix = settings.CONFERENCE_URL_PREFIXES[settings.CONFERENCE_ID] + response['Content-Disposition'] = \ + 'attachment; filename="%s_sponsorlogos.zip"' % prefix + return response diff --git a/symposion/static/chosen/chosen.css b/symposion/static/chosen/chosen.css deleted file mode 100755 index 1ca56178..00000000 --- a/symposion/static/chosen/chosen.css +++ /dev/null @@ -1,384 +0,0 @@ -/* @group Base */ -.chzn-container { - font-size: 13px; - position: relative; - display: inline-block; - zoom: 1; - *display: inline; -} -.chzn-container .chzn-drop { - background: #fff; - border: 1px solid #aaa; - border-top: 0; - position: absolute; - top: 29px; - left: 0; - -webkit-box-shadow: 0 4px 5px rgba(0,0,0,.15); - -moz-box-shadow : 0 4px 5px rgba(0,0,0,.15); - box-shadow : 0 4px 5px rgba(0,0,0,.15); - z-index: 1010; -} -/* @end */ - -/* @group Single Chosen */ -.chzn-container-single .chzn-single { - background-color: #ffffff; - filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#eeeeee', GradientType=0 ); - background-image: -webkit-gradient(linear, 0 0, 0 100%, color-stop(20%, #ffffff), color-stop(50%, #f6f6f6), color-stop(52%, #eeeeee), color-stop(100%, #f4f4f4)); - background-image: -webkit-linear-gradient(top, #ffffff 20%, #f6f6f6 50%, #eeeeee 52%, #f4f4f4 100%); - background-image: -moz-linear-gradient(top, #ffffff 20%, #f6f6f6 50%, #eeeeee 52%, #f4f4f4 100%); - background-image: -o-linear-gradient(top, #ffffff 20%, #f6f6f6 50%, #eeeeee 52%, #f4f4f4 100%); - background-image: linear-gradient(#ffffff 20%, #f6f6f6 50%, #eeeeee 52%, #f4f4f4 100%); - -webkit-border-radius: 5px; - -moz-border-radius : 5px; - border-radius : 5px; - -moz-background-clip : padding; - -webkit-background-clip: padding-box; - background-clip : padding-box; - border: 1px solid #aaaaaa; - -webkit-box-shadow: 0 0 3px #ffffff inset, 0 1px 1px rgba(0,0,0,0.1); - -moz-box-shadow : 0 0 3px #ffffff inset, 0 1px 1px rgba(0,0,0,0.1); - box-shadow : 0 0 3px #ffffff inset, 0 1px 1px rgba(0,0,0,0.1); - display: block; - overflow: hidden; - white-space: nowrap; - position: relative; - height: 23px; - line-height: 24px; - padding: 0 0 0 8px; - color: #444444; - text-decoration: none; -} -.chzn-container-single .chzn-default { - color: #999; -} -.chzn-container-single .chzn-single span { - margin-right: 26px; - display: block; - overflow: hidden; - white-space: nowrap; - -o-text-overflow: ellipsis; - -ms-text-overflow: ellipsis; - text-overflow: ellipsis; -} -.chzn-container-single .chzn-single abbr { - display: block; - position: absolute; - right: 26px; - top: 6px; - width: 12px; - height: 13px; - font-size: 1px; - background: url('chosen-sprite.png') right top no-repeat; -} -.chzn-container-single .chzn-single abbr:hover { - background-position: right -11px; -} -.chzn-container-single.chzn-disabled .chzn-single abbr:hover { - background-position: right top; -} -.chzn-container-single .chzn-single div { - position: absolute; - right: 0; - top: 0; - display: block; - height: 100%; - width: 18px; -} -.chzn-container-single .chzn-single div b { - background: url('chosen-sprite.png') no-repeat 0 0; - display: block; - width: 100%; - height: 100%; -} -.chzn-container-single .chzn-search { - padding: 3px 4px; - position: relative; - margin: 0; - white-space: nowrap; - z-index: 1010; -} -.chzn-container-single .chzn-search input { - background: #fff url('chosen-sprite.png') no-repeat 100% -22px; - background: url('chosen-sprite.png') no-repeat 100% -22px, -webkit-gradient(linear, 0 0, 0 100%, color-stop(1%, #eeeeee), color-stop(15%, #ffffff)); - background: url('chosen-sprite.png') no-repeat 100% -22px, -webkit-linear-gradient(top, #eeeeee 1%, #ffffff 15%); - background: url('chosen-sprite.png') no-repeat 100% -22px, -moz-linear-gradient(top, #eeeeee 1%, #ffffff 15%); - background: url('chosen-sprite.png') no-repeat 100% -22px, -o-linear-gradient(top, #eeeeee 1%, #ffffff 15%); - background: url('chosen-sprite.png') no-repeat 100% -22px, linear-gradient(#eeeeee 1%, #ffffff 15%); - margin: 1px 0; - padding: 4px 20px 4px 5px; - outline: 0; - border: 1px solid #aaa; - font-family: sans-serif; - font-size: 1em; -} -.chzn-container-single .chzn-drop { - -webkit-border-radius: 0 0 4px 4px; - -moz-border-radius : 0 0 4px 4px; - border-radius : 0 0 4px 4px; - -moz-background-clip : padding; - -webkit-background-clip: padding-box; - background-clip : padding-box; -} -/* @end */ - -.chzn-container-single-nosearch .chzn-search input { - position: absolute; - left: -9000px; -} - -/* @group Multi Chosen */ -.chzn-container-multi .chzn-choices { - background-color: #fff; - background-image: -webkit-gradient(linear, 0 0, 0 100%, color-stop(1%, #eeeeee), color-stop(15%, #ffffff)); - background-image: -webkit-linear-gradient(top, #eeeeee 1%, #ffffff 15%); - background-image: -moz-linear-gradient(top, #eeeeee 1%, #ffffff 15%); - background-image: -o-linear-gradient(top, #eeeeee 1%, #ffffff 15%); - background-image: linear-gradient(#eeeeee 1%, #ffffff 15%); - border: 1px solid #aaa; - margin: 0; - padding: 0; - cursor: text; - overflow: hidden; - height: auto !important; - height: 1%; - position: relative; -} -.chzn-container-multi .chzn-choices li { - float: left; - list-style: none; -} -.chzn-container-multi .chzn-choices .search-field { - white-space: nowrap; - margin: 0; - padding: 0; -} -.chzn-container-multi .chzn-choices .search-field input { - color: #666; - background: transparent !important; - border: 0 !important; - font-family: sans-serif; - font-size: 100%; - height: 15px; - padding: 5px; - margin: 1px 0; - outline: 0; - -webkit-box-shadow: none; - -moz-box-shadow : none; - box-shadow : none; -} -.chzn-container-multi .chzn-choices .search-field .default { - color: #999; -} -.chzn-container-multi .chzn-choices .search-choice { - -webkit-border-radius: 3px; - -moz-border-radius : 3px; - border-radius : 3px; - -moz-background-clip : padding; - -webkit-background-clip: padding-box; - background-clip : padding-box; - background-color: #e4e4e4; - filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#f4f4f4', endColorstr='#eeeeee', GradientType=0 ); - background-image: -webkit-gradient(linear, 0 0, 0 100%, color-stop(20%, #f4f4f4), color-stop(50%, #f0f0f0), color-stop(52%, #e8e8e8), color-stop(100%, #eeeeee)); - background-image: -webkit-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); - background-image: -moz-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); - background-image: -o-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); - background-image: linear-gradient(#f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); - -webkit-box-shadow: 0 0 2px #ffffff inset, 0 1px 0 rgba(0,0,0,0.05); - -moz-box-shadow : 0 0 2px #ffffff inset, 0 1px 0 rgba(0,0,0,0.05); - box-shadow : 0 0 2px #ffffff inset, 0 1px 0 rgba(0,0,0,0.05); - color: #333; - border: 1px solid #aaaaaa; - line-height: 13px; - padding: 3px 20px 3px 5px; - margin: 3px 0 3px 5px; - position: relative; - cursor: default; -} -.chzn-container-multi .chzn-choices .search-choice-focus { - background: #d4d4d4; -} -.chzn-container-multi .chzn-choices .search-choice .search-choice-close { - display: block; - position: absolute; - right: 3px; - top: 4px; - width: 12px; - height: 13px; - font-size: 1px; - background: url('chosen-sprite.png') right top no-repeat; -} -.chzn-container-multi .chzn-choices .search-choice .search-choice-close:hover { - background-position: right -11px; -} -.chzn-container-multi .chzn-choices .search-choice-focus .search-choice-close { - background-position: right -11px; -} -/* @end */ - -/* @group Results */ -.chzn-container .chzn-results { - margin: 0 4px 4px 0; - max-height: 240px; - padding: 0 0 0 4px; - position: relative; - overflow-x: hidden; - overflow-y: auto; - -webkit-overflow-scrolling: touch; -} -.chzn-container-multi .chzn-results { - margin: -1px 0 0; - padding: 0; -} -.chzn-container .chzn-results li { - display: none; - line-height: 15px; - padding: 5px 6px; - margin: 0; - list-style: none; -} -.chzn-container .chzn-results .active-result { - cursor: pointer; - display: list-item; -} -.chzn-container .chzn-results .highlighted { - background-color: #3875d7; - filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#3875d7', endColorstr='#2a62bc', GradientType=0 ); - background-image: -webkit-gradient(linear, 0 0, 0 100%, color-stop(20%, #3875d7), color-stop(90%, #2a62bc)); - background-image: -webkit-linear-gradient(top, #3875d7 20%, #2a62bc 90%); - background-image: -moz-linear-gradient(top, #3875d7 20%, #2a62bc 90%); - background-image: -o-linear-gradient(top, #3875d7 20%, #2a62bc 90%); - background-image: linear-gradient(#3875d7 20%, #2a62bc 90%); - color: #fff; -} -.chzn-container .chzn-results li em { - background: #feffde; - font-style: normal; -} -.chzn-container .chzn-results .highlighted em { - background: transparent; -} -.chzn-container .chzn-results .no-results { - background: #f4f4f4; - display: list-item; -} -.chzn-container .chzn-results .group-result { - cursor: default; - color: #999; - font-weight: bold; -} -.chzn-container .chzn-results .group-option { - padding-left: 15px; -} -.chzn-container-multi .chzn-drop .result-selected { - display: none; -} -.chzn-container .chzn-results-scroll { - background: white; - margin: 0 4px; - position: absolute; - text-align: center; - width: 321px; /* This should by dynamic with js */ - z-index: 1; -} -.chzn-container .chzn-results-scroll span { - display: inline-block; - height: 17px; - text-indent: -5000px; - width: 9px; -} -.chzn-container .chzn-results-scroll-down { - bottom: 0; -} -.chzn-container .chzn-results-scroll-down span { - background: url('chosen-sprite.png') no-repeat -4px -3px; -} -.chzn-container .chzn-results-scroll-up span { - background: url('chosen-sprite.png') no-repeat -22px -3px; -} -/* @end */ - -/* @group Active */ -.chzn-container-active .chzn-single { - -webkit-box-shadow: 0 0 5px rgba(0,0,0,.3); - -moz-box-shadow : 0 0 5px rgba(0,0,0,.3); - box-shadow : 0 0 5px rgba(0,0,0,.3); - border: 1px solid #5897fb; -} -.chzn-container-active .chzn-single-with-drop { - border: 1px solid #aaa; - -webkit-box-shadow: 0 1px 0 #fff inset; - -moz-box-shadow : 0 1px 0 #fff inset; - box-shadow : 0 1px 0 #fff inset; - background-color: #eee; - filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#eeeeee', endColorstr='#ffffff', GradientType=0 ); - background-image: -webkit-gradient(linear, 0 0, 0 100%, color-stop(20%, #eeeeee), color-stop(80%, #ffffff)); - background-image: -webkit-linear-gradient(top, #eeeeee 20%, #ffffff 80%); - background-image: -moz-linear-gradient(top, #eeeeee 20%, #ffffff 80%); - background-image: -o-linear-gradient(top, #eeeeee 20%, #ffffff 80%); - background-image: linear-gradient(#eeeeee 20%, #ffffff 80%); - -webkit-border-bottom-left-radius : 0; - -webkit-border-bottom-right-radius: 0; - -moz-border-radius-bottomleft : 0; - -moz-border-radius-bottomright: 0; - border-bottom-left-radius : 0; - border-bottom-right-radius: 0; -} -.chzn-container-active .chzn-single-with-drop div { - background: transparent; - border-left: none; -} -.chzn-container-active .chzn-single-with-drop div b { - background-position: -18px 1px; -} -.chzn-container-active .chzn-choices { - -webkit-box-shadow: 0 0 5px rgba(0,0,0,.3); - -moz-box-shadow : 0 0 5px rgba(0,0,0,.3); - box-shadow : 0 0 5px rgba(0,0,0,.3); - border: 1px solid #5897fb; -} -.chzn-container-active .chzn-choices .search-field input { - color: #111 !important; -} -/* @end */ - -/* @group Disabled Support */ -.chzn-disabled { - cursor: default; - opacity:0.5 !important; -} -.chzn-disabled .chzn-single { - cursor: default; -} -.chzn-disabled .chzn-choices .search-choice .search-choice-close { - cursor: default; -} - -/* @group Right to Left */ -.chzn-rtl { text-align: right; } -.chzn-rtl .chzn-single { padding: 0 8px 0 0; overflow: visible; } -.chzn-rtl .chzn-single span { margin-left: 26px; margin-right: 0; direction: rtl; } - -.chzn-rtl .chzn-single div { left: 3px; right: auto; } -.chzn-rtl .chzn-single abbr { - left: 26px; - right: auto; -} -.chzn-rtl .chzn-choices .search-field input { direction: rtl; } -.chzn-rtl .chzn-choices li { float: right; } -.chzn-rtl .chzn-choices .search-choice { padding: 3px 5px 3px 19px; margin: 3px 5px 3px 0; } -.chzn-rtl .chzn-choices .search-choice .search-choice-close { left: 4px; right: auto; background-position: right top;} -.chzn-rtl.chzn-container-single .chzn-results { margin: 0 0 4px 4px; padding: 0 4px 0 0; } -.chzn-rtl .chzn-results .group-option { padding-left: 0; padding-right: 15px; } -.chzn-rtl.chzn-container-active .chzn-single-with-drop div { border-right: none; } -.chzn-rtl .chzn-search input { - background: #fff url('chosen-sprite.png') no-repeat -38px -22px; - background: url('chosen-sprite.png') no-repeat -38px -22px, -webkit-gradient(linear, 0 0, 0 100%, color-stop(1%, #eeeeee), color-stop(15%, #ffffff)); - background: url('chosen-sprite.png') no-repeat -38px -22px, -webkit-linear-gradient(top, #eeeeee 1%, #ffffff 15%); - background: url('chosen-sprite.png') no-repeat -38px -22px, -moz-linear-gradient(top, #eeeeee 1%, #ffffff 15%); - background: url('chosen-sprite.png') no-repeat -38px -22px, -o-linear-gradient(top, #eeeeee 1%, #ffffff 15%); - background: url('chosen-sprite.png') no-repeat -38px -22px, linear-gradient(#eeeeee 1%, #ffffff 15%); - padding: 4px 5px 4px 20px; - direction: rtl; -} -/* @end */ diff --git a/symposion/static/chosen/chosen.jquery.min.js b/symposion/static/chosen/chosen.jquery.min.js deleted file mode 100755 index b7806202..00000000 --- a/symposion/static/chosen/chosen.jquery.min.js +++ /dev/null @@ -1,10 +0,0 @@ -// Chosen, a Select Box Enhancer for jQuery and Protoype -// by Patrick Filler for Harvest, http://getharvest.com -// -// Version 0.9.8 -// Full source at https://github.com/harvesthq/chosen -// Copyright (c) 2011 Harvest http://getharvest.com - -// MIT License, https://github.com/harvesthq/chosen/blob/master/LICENSE.md -// This file is generated by `cake build`, do not edit it by hand. -(function(){var SelectParser;SelectParser=function(){function SelectParser(){this.options_index=0,this.parsed=[]}return SelectParser.prototype.add_node=function(child){return child.nodeName==="OPTGROUP"?this.add_group(child):this.add_option(child)},SelectParser.prototype.add_group=function(group){var group_position,option,_i,_len,_ref,_results;group_position=this.parsed.length,this.parsed.push({array_index:group_position,group:!0,label:group.label,children:0,disabled:group.disabled}),_ref=group.childNodes,_results=[];for(_i=0,_len=_ref.length;_i<_len;_i++)option=_ref[_i],_results.push(this.add_option(option,group_position,group.disabled));return _results},SelectParser.prototype.add_option=function(option,group_position,group_disabled){if(option.nodeName==="OPTION")return option.text!==""?(group_position!=null&&(this.parsed[group_position].children+=1),this.parsed.push({array_index:this.parsed.length,options_index:this.options_index,value:option.value,text:option.text,html:option.innerHTML,selected:option.selected,disabled:group_disabled===!0?group_disabled:option.disabled,group_array_index:group_position,classes:option.className,style:option.style.cssText})):this.parsed.push({array_index:this.parsed.length,options_index:this.options_index,empty:!0}),this.options_index+=1},SelectParser}(),SelectParser.select_to_array=function(select){var child,parser,_i,_len,_ref;parser=new SelectParser,_ref=select.childNodes;for(_i=0,_len=_ref.length;_i<_len;_i++)child=_ref[_i],parser.add_node(child);return parser.parsed},this.SelectParser=SelectParser}).call(this),function(){var AbstractChosen,root;root=this,AbstractChosen=function(){function AbstractChosen(form_field,options){this.form_field=form_field,this.options=options!=null?options:{},this.set_default_values(),this.is_multiple=this.form_field.multiple,this.set_default_text(),this.setup(),this.set_up_html(),this.register_observers(),this.finish_setup()}return AbstractChosen.prototype.set_default_values=function(){var _this=this;return this.click_test_action=function(evt){return _this.test_active_click(evt)},this.activate_action=function(evt){return _this.activate_field(evt)},this.active_field=!1,this.mouse_on_container=!1,this.results_showing=!1,this.result_highlighted=null,this.result_single_selected=null,this.allow_single_deselect=this.options.allow_single_deselect!=null&&this.form_field.options[0]!=null&&this.form_field.options[0].text===""?this.options.allow_single_deselect:!1,this.disable_search_threshold=this.options.disable_search_threshold||0,this.disable_search=this.options.disable_search||!1,this.search_contains=this.options.search_contains||!1,this.choices=0,this.single_backstroke_delete=this.options.single_backstroke_delete||!1,this.max_selected_options=this.options.max_selected_options||Infinity},AbstractChosen.prototype.set_default_text=function(){return this.form_field.getAttribute("data-placeholder")?this.default_text=this.form_field.getAttribute("data-placeholder"):this.is_multiple?this.default_text=this.options.placeholder_text_multiple||this.options.placeholder_text||"Select Some Options":this.default_text=this.options.placeholder_text_single||this.options.placeholder_text||"Select an Option",this.results_none_found=this.form_field.getAttribute("data-no_results_text")||this.options.no_results_text||"No results match"},AbstractChosen.prototype.mouse_enter=function(){return this.mouse_on_container=!0},AbstractChosen.prototype.mouse_leave=function(){return this.mouse_on_container=!1},AbstractChosen.prototype.input_focus=function(evt){var _this=this;if(!this.active_field)return setTimeout(function(){return _this.container_mousedown()},50)},AbstractChosen.prototype.input_blur=function(evt){var _this=this;if(!this.mouse_on_container)return this.active_field=!1,setTimeout(function(){return _this.blur_test()},100)},AbstractChosen.prototype.result_add_option=function(option){var classes,style;return option.disabled?"":(option.dom_id=this.container_id+"_o_"+option.array_index,classes=option.selected&&this.is_multiple?[]:["active-result"],option.selected&&classes.push("result-selected"),option.group_array_index!=null&&classes.push("group-option"),option.classes!==""&&classes.push(option.classes),style=option.style.cssText!==""?' style="'+option.style+'"':"",'
  • "+option.html+"
  • ")},AbstractChosen.prototype.results_update_field=function(){return this.is_multiple||this.results_reset_cleanup(),this.result_clear_highlight(),this.result_single_selected=null,this.results_build()},AbstractChosen.prototype.results_toggle=function(){return this.results_showing?this.results_hide():this.results_show()},AbstractChosen.prototype.results_search=function(evt){return this.results_showing?this.winnow_results():this.results_show()},AbstractChosen.prototype.keyup_checker=function(evt){var stroke,_ref;stroke=(_ref=evt.which)!=null?_ref:evt.keyCode,this.search_field_scale();switch(stroke){case 8:if(this.is_multiple&&this.backstroke_length<1&&this.choices>0)return this.keydown_backstroke();if(!this.pending_backstroke)return this.result_clear_highlight(),this.results_search();break;case 13:evt.preventDefault();if(this.results_showing)return this.result_select(evt);break;case 27:return this.results_showing&&this.results_hide(),!0;case 9:case 38:case 40:case 16:case 91:case 17:break;default:return this.results_search()}},AbstractChosen.prototype.generate_field_id=function(){var new_id;return new_id=this.generate_random_id(),this.form_field.id=new_id,new_id},AbstractChosen.prototype.generate_random_char=function(){var chars,newchar,rand;return chars="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ",rand=Math.floor(Math.random()*chars.length),newchar=chars.substring(rand,rand+1)},AbstractChosen}(),root.AbstractChosen=AbstractChosen}.call(this),function(){var $,Chosen,get_side_border_padding,root,__hasProp=Object.prototype.hasOwnProperty,__extends=function(child,parent){function ctor(){this.constructor=child}for(var key in parent)__hasProp.call(parent,key)&&(child[key]=parent[key]);return ctor.prototype=parent.prototype,child.prototype=new ctor,child.__super__=parent.prototype,child};root=this,$=jQuery,$.fn.extend({chosen:function(options){return!$.browser.msie||$.browser.version!=="6.0"&&$.browser.version!=="7.0"?this.each(function(input_field){var $this;$this=$(this);if(!$this.hasClass("chzn-done"))return $this.data("chosen",new Chosen(this,options))}):this}}),Chosen=function(_super){function Chosen(){Chosen.__super__.constructor.apply(this,arguments)}return __extends(Chosen,_super),Chosen.prototype.setup=function(){return this.form_field_jq=$(this.form_field),this.current_value=this.form_field_jq.val(),this.is_rtl=this.form_field_jq.hasClass("chzn-rtl")},Chosen.prototype.finish_setup=function(){return this.form_field_jq.addClass("chzn-done")},Chosen.prototype.set_up_html=function(){var container_div,dd_top,dd_width,sf_width;return this.container_id=this.form_field.id.length?this.form_field.id.replace(/[^\w]/g,"_"):this.generate_field_id(),this.container_id+="_chzn",this.f_width=this.form_field_jq.outerWidth(),container_div=$("
    ",{id:this.container_id,"class":"chzn-container"+(this.is_rtl?" chzn-rtl":""),style:"width: "+this.f_width+"px;"}),this.is_multiple?container_div.html('
      '):container_div.html(''+this.default_text+'
        '),this.form_field_jq.hide().after(container_div),this.container=$("#"+this.container_id),this.container.addClass("chzn-container-"+(this.is_multiple?"multi":"single")),this.dropdown=this.container.find("div.chzn-drop").first(),dd_top=this.container.height(),dd_width=this.f_width-get_side_border_padding(this.dropdown),this.dropdown.css({width:dd_width+"px",top:dd_top+"px"}),this.search_field=this.container.find("input").first(),this.search_results=this.container.find("ul.chzn-results").first(),this.search_field_scale(),this.search_no_results=this.container.find("li.no-results").first(),this.is_multiple?(this.search_choices=this.container.find("ul.chzn-choices").first(),this.search_container=this.container.find("li.search-field").first()):(this.search_container=this.container.find("div.chzn-search").first(),this.selected_item=this.container.find(".chzn-single").first(),sf_width=dd_width-get_side_border_padding(this.search_container)-get_side_border_padding(this.search_field),this.search_field.css({width:sf_width+"px"})),this.results_build(),this.set_tab_index(),this.form_field_jq.trigger("liszt:ready",{chosen:this})},Chosen.prototype.register_observers=function(){var _this=this;return this.container.mousedown(function(evt){return _this.container_mousedown(evt)}),this.container.mouseup(function(evt){return _this.container_mouseup(evt)}),this.container.mouseenter(function(evt){return _this.mouse_enter(evt)}),this.container.mouseleave(function(evt){return _this.mouse_leave(evt)}),this.search_results.mouseup(function(evt){return _this.search_results_mouseup(evt)}),this.search_results.mouseover(function(evt){return _this.search_results_mouseover(evt)}),this.search_results.mouseout(function(evt){return _this.search_results_mouseout(evt)}),this.form_field_jq.bind("liszt:updated",function(evt){return _this.results_update_field(evt)}),this.form_field_jq.bind("liszt:activate",function(evt){return _this.activate_field(evt)}),this.form_field_jq.bind("liszt:open",function(evt){return _this.container_mousedown(evt)}),this.search_field.blur(function(evt){return _this.input_blur(evt)}),this.search_field.keyup(function(evt){return _this.keyup_checker(evt)}),this.search_field.keydown(function(evt){return _this.keydown_checker(evt)}),this.is_multiple?(this.search_choices.click(function(evt){return _this.choices_click(evt)}),this.search_field.focus(function(evt){return _this.input_focus(evt)})):this.container.click(function(evt){return evt.preventDefault()})},Chosen.prototype.search_field_disabled=function(){this.is_disabled=this.form_field_jq[0].disabled;if(this.is_disabled)return this.container.addClass("chzn-disabled"),this.search_field[0].disabled=!0,this.is_multiple||this.selected_item.unbind("focus",this.activate_action),this.close_field();this.container.removeClass("chzn-disabled"),this.search_field[0].disabled=!1;if(!this.is_multiple)return this.selected_item.bind("focus",this.activate_action)},Chosen.prototype.container_mousedown=function(evt){var target_closelink;if(!this.is_disabled)return target_closelink=evt!=null?$(evt.target).hasClass("search-choice-close"):!1,evt&&evt.type==="mousedown"&&!this.results_showing&&evt.stopPropagation(),!this.pending_destroy_click&&!target_closelink?(this.active_field?!this.is_multiple&&evt&&($(evt.target)[0]===this.selected_item[0]||$(evt.target).parents("a.chzn-single").length)&&(evt.preventDefault(),this.results_toggle()):(this.is_multiple&&this.search_field.val(""),$(document).click(this.click_test_action),this.results_show()),this.activate_field()):this.pending_destroy_click=!1},Chosen.prototype.container_mouseup=function(evt){if(evt.target.nodeName==="ABBR"&&!this.is_disabled)return this.results_reset(evt)},Chosen.prototype.blur_test=function(evt){if(!this.active_field&&this.container.hasClass("chzn-container-active"))return this.close_field()},Chosen.prototype.close_field=function(){return $(document).unbind("click",this.click_test_action),this.is_multiple||(this.selected_item.attr("tabindex",this.search_field.attr("tabindex")),this.search_field.attr("tabindex",-1)),this.active_field=!1,this.results_hide(),this.container.removeClass("chzn-container-active"),this.winnow_results_clear(),this.clear_backstroke(),this.show_search_field_default(),this.search_field_scale()},Chosen.prototype.activate_field=function(){return!this.is_multiple&&!this.active_field&&(this.search_field.attr("tabindex",this.selected_item.attr("tabindex")),this.selected_item.attr("tabindex",-1)),this.container.addClass("chzn-container-active"),this.active_field=!0,this.search_field.val(this.search_field.val()),this.search_field.focus()},Chosen.prototype.test_active_click=function(evt){return $(evt.target).parents("#"+this.container_id).length?this.active_field=!0:this.close_field()},Chosen.prototype.results_build=function(){var content,data,_i,_len,_ref;this.parsing=!0,this.results_data=root.SelectParser.select_to_array(this.form_field),this.is_multiple&&this.choices>0?(this.search_choices.find("li.search-choice").remove(),this.choices=0):this.is_multiple||(this.selected_item.addClass("chzn-default").find("span").text(this.default_text),this.disable_search||this.form_field.options.length<=this.disable_search_threshold?this.container.addClass("chzn-container-single-nosearch"):this.container.removeClass("chzn-container-single-nosearch")),content="",_ref=this.results_data;for(_i=0,_len=_ref.length;_i<_len;_i++)data=_ref[_i],data.group?content+=this.result_add_group(data):data.empty||(content+=this.result_add_option(data),data.selected&&this.is_multiple?this.choice_build(data):data.selected&&!this.is_multiple&&(this.selected_item.removeClass("chzn-default").find("span").text(data.text),this.allow_single_deselect&&this.single_deselect_control_build()));return this.search_field_disabled(),this.show_search_field_default(),this.search_field_scale(),this.search_results.html(content),this.parsing=!1},Chosen.prototype.result_add_group=function(group){return group.disabled?"":(group.dom_id=this.container_id+"_g_"+group.array_index,'
      • '+$("
        ").text(group.label).html()+"
      • ")},Chosen.prototype.result_do_highlight=function(el){var high_bottom,high_top,maxHeight,visible_bottom,visible_top;if(el.length){this.result_clear_highlight(),this.result_highlight=el,this.result_highlight.addClass("highlighted"),maxHeight=parseInt(this.search_results.css("maxHeight"),10),visible_top=this.search_results.scrollTop(),visible_bottom=maxHeight+visible_top,high_top=this.result_highlight.position().top+this.search_results.scrollTop(),high_bottom=high_top+this.result_highlight.outerHeight();if(high_bottom>=visible_bottom)return this.search_results.scrollTop(high_bottom-maxHeight>0?high_bottom-maxHeight:0);if(high_top'+item.html+''),link=$("#"+choice_id).find("a").first(),link.click(function(evt){return _this.choice_destroy_link_click(evt)}))},Chosen.prototype.choice_destroy_link_click=function(evt){return evt.preventDefault(),this.is_disabled?evt.stopPropagation:(this.pending_destroy_click=!0,this.choice_destroy($(evt.target)))},Chosen.prototype.choice_destroy=function(link){return this.choices-=1,this.show_search_field_default(),this.is_multiple&&this.choices>0&&this.search_field.val().length<1&&this.results_hide(),this.result_deselect(link.attr("rel")),link.parents("li").first().remove()},Chosen.prototype.results_reset=function(){this.form_field.options[0].selected=!0,this.selected_item.find("span").text(this.default_text),this.is_multiple||this.selected_item.addClass("chzn-default"),this.show_search_field_default(),this.results_reset_cleanup(),this.form_field_jq.trigger("change");if(this.active_field)return this.results_hide()},Chosen.prototype.results_reset_cleanup=function(){return this.selected_item.find("abbr").remove()},Chosen.prototype.result_select=function(evt){var high,high_id,item,position;if(this.result_highlight)return high=this.result_highlight,high_id=high.attr("id"),this.result_clear_highlight(),this.is_multiple?this.result_deactivate(high):(this.search_results.find(".result-selected").removeClass("result-selected"),this.result_single_selected=high,this.selected_item.removeClass("chzn-default")),high.addClass("result-selected"),position=high_id.substr(high_id.lastIndexOf("_")+1),item=this.results_data[position],item.selected=!0,this.form_field.options[item.options_index].selected=!0,this.is_multiple?this.choice_build(item):(this.selected_item.find("span").first().text(item.text),this.allow_single_deselect&&this.single_deselect_control_build()),(!evt.metaKey||!this.is_multiple)&&this.results_hide(),this.search_field.val(""),(this.is_multiple||this.form_field_jq.val()!==this.current_value)&&this.form_field_jq.trigger("change",{selected:this.form_field.options[item.options_index].value}),this.current_value=this.form_field_jq.val(),this.search_field_scale()},Chosen.prototype.result_activate=function(el){return el.addClass("active-result")},Chosen.prototype.result_deactivate=function(el){return el.removeClass("active-result")},Chosen.prototype.result_deselect=function(pos){var result,result_data;return result_data=this.results_data[pos],result_data.selected=!1,this.form_field.options[result_data.options_index].selected=!1,result=$("#"+this.container_id+"_o_"+pos),result.removeClass("result-selected").addClass("active-result").show(),this.result_clear_highlight(),this.winnow_results(),this.form_field_jq.trigger("change",{deselected:this.form_field.options[result_data.options_index].value}),this.search_field_scale()},Chosen.prototype.single_deselect_control_build=function(){if(this.allow_single_deselect&&this.selected_item.find("abbr").length<1)return this.selected_item.find("span").first().after('')},Chosen.prototype.winnow_results=function(){var found,option,part,parts,regex,regexAnchor,result,result_id,results,searchText,startpos,text,zregex,_i,_j,_len,_len2,_ref;this.no_results_clear(),results=0,searchText=this.search_field.val()===this.default_text?"":$("
        ").text($.trim(this.search_field.val())).html(),regexAnchor=this.search_contains?"":"^",regex=new RegExp(regexAnchor+searchText.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&"),"i"),zregex=new RegExp(searchText.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&"),"i"),_ref=this.results_data;for(_i=0,_len=_ref.length;_i<_len;_i++){option=_ref[_i];if(!option.disabled&&!option.empty)if(option.group)$("#"+option.dom_id).css("display","none");else if(!this.is_multiple||!option.selected){found=!1,result_id=option.dom_id,result=$("#"+result_id);if(regex.test(option.html))found=!0,results+=1;else if(option.html.indexOf(" ")>=0||option.html.indexOf("[")===0){parts=option.html.replace(/\[|\]/g,"").split(" ");if(parts.length)for(_j=0,_len2=parts.length;_j<_len2;_j++)part=parts[_j],regex.test(part)&&(found=!0,results+=1)}found?(searchText.length?(startpos=option.html.search(zregex),text=option.html.substr(0,startpos+searchText.length)+""+option.html.substr(startpos+searchText.length),text=text.substr(0,startpos)+""+text.substr(startpos)):text=option.html,result.html(text),this.result_activate(result),option.group_array_index!=null&&$("#"+this.results_data[option.group_array_index].dom_id).css("display","list-item")):(this.result_highlight&&result_id===this.result_highlight.attr("id")&&this.result_clear_highlight(),this.result_deactivate(result))}}return results<1&&searchText.length?this.no_results(searchText):this.winnow_results_set_highlight()},Chosen.prototype.winnow_results_clear=function(){var li,lis,_i,_len,_results;this.search_field.val(""),lis=this.search_results.find("li"),_results=[];for(_i=0,_len=lis.length;_i<_len;_i++)li=lis[_i],li=$(li),li.hasClass("group-result")?_results.push(li.css("display","auto")):!this.is_multiple||!li.hasClass("result-selected")?_results.push(this.result_activate(li)):_results.push(void 0);return _results},Chosen.prototype.winnow_results_set_highlight=function(){var do_high,selected_results;if(!this.result_highlight){selected_results=this.is_multiple?[]:this.search_results.find(".result-selected.active-result"),do_high=selected_results.length?selected_results.first():this.search_results.find(".active-result").first();if(do_high!=null)return this.result_do_highlight(do_high)}},Chosen.prototype.no_results=function(terms){var no_results_html;return no_results_html=$('
      • '+this.results_none_found+' ""
      • '),no_results_html.find("span").first().html(terms),this.search_results.append(no_results_html)},Chosen.prototype.no_results_clear=function(){return this.search_results.find(".no-results").remove()},Chosen.prototype.keydown_arrow=function(){var first_active,next_sib;this.result_highlight?this.results_showing&&(next_sib=this.result_highlight.nextAll("li.active-result").first(),next_sib&&this.result_do_highlight(next_sib)):(first_active=this.search_results.find("li.active-result").first(),first_active&&this.result_do_highlight($(first_active)));if(!this.results_showing)return this.results_show()},Chosen.prototype.keyup_arrow=function(){var prev_sibs;if(!this.results_showing&&!this.is_multiple)return this.results_show();if(this.result_highlight)return prev_sibs=this.result_highlight.prevAll("li.active-result"),prev_sibs.length?this.result_do_highlight(prev_sibs.first()):(this.choices>0&&this.results_hide(),this.result_clear_highlight())},Chosen.prototype.keydown_backstroke=function(){return this.pending_backstroke?(this.choice_destroy(this.pending_backstroke.find("a").first()),this.clear_backstroke()):(this.pending_backstroke=this.search_container.siblings("li.search-choice").last(),this.single_backstroke_delete?this.keydown_backstroke():this.pending_backstroke.addClass("search-choice-focus"))},Chosen.prototype.clear_backstroke=function(){return this.pending_backstroke&&this.pending_backstroke.removeClass("search-choice-focus"),this.pending_backstroke=null},Chosen.prototype.keydown_checker=function(evt){var stroke,_ref;stroke=(_ref=evt.which)!=null?_ref:evt.keyCode,this.search_field_scale(),stroke!==8&&this.pending_backstroke&&this.clear_backstroke();switch(stroke){case 8:this.backstroke_length=this.search_field.val().length;break;case 9:this.results_showing&&!this.is_multiple&&this.result_select(evt),this.mouse_on_container=!1;break;case 13:evt.preventDefault();break;case 38:evt.preventDefault(),this.keyup_arrow();break;case 40:this.keydown_arrow()}},Chosen.prototype.search_field_scale=function(){var dd_top,div,h,style,style_block,styles,w,_i,_len;if(this.is_multiple){h=0,w=0,style_block="position:absolute; left: -1000px; top: -1000px; display:none;",styles=["font-size","font-style","font-weight","font-family","line-height","text-transform","letter-spacing"];for(_i=0,_len=styles.length;_i<_len;_i++)style=styles[_i],style_block+=style+":"+this.search_field.css(style)+";";return div=$("
        ",{style:style_block}),div.text(this.search_field.val()),$("body").append(div),w=div.width()+25,div.remove(),w>this.f_width-10&&(w=this.f_width-10),this.search_field.css({width:w+"px"}),dd_top=this.container.height(),this.dropdown.css({top:dd_top+"px"})}},Chosen.prototype.generate_random_id=function(){var string;string="sel"+this.generate_random_char()+this.generate_random_char()+this.generate_random_char();while($("#"+string).length>0)string+=this.generate_random_char();return string},Chosen}(AbstractChosen),get_side_border_padding=function(elmt){var side_border_padding;return side_border_padding=elmt.outerWidth()-elmt.width()},root.get_side_border_padding=get_side_border_padding}.call(this); \ No newline at end of file